sync
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368     
369     /**
370      * Set the element that will be used to show or hide
371      */
372     setVisibilityEl : function(el)
373     {
374         this.visibilityEl = el;
375     },
376     
377      /**
378      * Get the element that will be used to show or hide
379      */
380     getVisibilityEl : function()
381     {
382         if (typeof(this.visibilityEl) == 'object') {
383             return this.visibilityEl;
384         }
385         
386         if (typeof(this.visibilityEl) == 'string') {
387             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
388         }
389         
390         return this.getEl();
391     },
392     
393     /**
394      * Show a component - removes 'hidden' class
395      */
396     show : function()
397     {
398         if(!this.getVisibilityEl()){
399             return;
400         }
401          
402         this.getVisibilityEl().removeClass('hidden');
403         
404         this.fireEvent('show', this);
405         
406         
407     },
408     /**
409      * Hide a component - adds 'hidden' class
410      */
411     hide: function()
412     {
413         if(!this.getVisibilityEl()){
414             return;
415         }
416         
417         this.getVisibilityEl().addClass('hidden');
418         
419         this.fireEvent('hide', this);
420         
421     }
422 });
423
424  /*
425  * - LGPL
426  *
427  * Body
428  *
429  */
430
431 /**
432  * @class Roo.bootstrap.Body
433  * @extends Roo.bootstrap.Component
434  * Bootstrap Body class
435  *
436  * @constructor
437  * Create a new body
438  * @param {Object} config The config object
439  */
440
441 Roo.bootstrap.Body = function(config){
442
443     config = config || {};
444
445     Roo.bootstrap.Body.superclass.constructor.call(this, config);
446     this.el = Roo.get(config.el ? config.el : document.body );
447     if (this.cls && this.cls.length) {
448         Roo.get(document.body).addClass(this.cls);
449     }
450 };
451
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
453
454     is_body : true,// just to make sure it's constructed?
455
456         autoCreate : {
457         cls: 'container'
458     },
459     onRender : function(ct, position)
460     {
461        /* Roo.log("Roo.bootstrap.Body - onRender");
462         if (this.cls && this.cls.length) {
463             Roo.get(document.body).addClass(this.cls);
464         }
465         // style??? xttr???
466         */
467     }
468
469
470
471
472 });
473 /*
474  * - LGPL
475  *
476  * button group
477  * 
478  */
479
480
481 /**
482  * @class Roo.bootstrap.ButtonGroup
483  * @extends Roo.bootstrap.Component
484  * Bootstrap ButtonGroup class
485  * @cfg {String} size lg | sm | xs (default empty normal)
486  * @cfg {String} align vertical | justified  (default none)
487  * @cfg {String} direction up | down (default down)
488  * @cfg {Boolean} toolbar false | true
489  * @cfg {Boolean} btn true | false
490  * 
491  * 
492  * @constructor
493  * Create a new Input
494  * @param {Object} config The config object
495  */
496
497 Roo.bootstrap.ButtonGroup = function(config){
498     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
499 };
500
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
502     
503     size: '',
504     align: '',
505     direction: '',
506     toolbar: false,
507     btn: true,
508
509     getAutoCreate : function(){
510         var cfg = {
511             cls: 'btn-group',
512             html : null
513         };
514         
515         cfg.html = this.html || cfg.html;
516         
517         if (this.toolbar) {
518             cfg = {
519                 cls: 'btn-toolbar',
520                 html: null
521             };
522             
523             return cfg;
524         }
525         
526         if (['vertical','justified'].indexOf(this.align)!==-1) {
527             cfg.cls = 'btn-group-' + this.align;
528             
529             if (this.align == 'justified') {
530                 console.log(this.items);
531             }
532         }
533         
534         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535             cfg.cls += ' btn-group-' + this.size;
536         }
537         
538         if (this.direction == 'up') {
539             cfg.cls += ' dropup' ;
540         }
541         
542         return cfg;
543     }
544    
545 });
546
547  /*
548  * - LGPL
549  *
550  * button
551  * 
552  */
553
554 /**
555  * @class Roo.bootstrap.Button
556  * @extends Roo.bootstrap.Component
557  * Bootstrap Button class
558  * @cfg {String} html The button content
559  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
560  * @cfg {String} size ( lg | sm | xs)
561  * @cfg {String} tag ( a | input | submit)
562  * @cfg {String} href empty or href
563  * @cfg {Boolean} disabled default false;
564  * @cfg {Boolean} isClose default false;
565  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566  * @cfg {String} badge text for badge
567  * @cfg {String} theme (default|glow)  
568  * @cfg {Boolean} inverse dark themed version
569  * @cfg {Boolean} toggle is it a slidy toggle button
570  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571  * @cfg {String} ontext text for on slidy toggle state
572  * @cfg {String} offtext text for off slidy toggle state
573  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
574  * @cfg {Boolean} removeClass remove the standard class..
575  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
576  * 
577  * @constructor
578  * Create a new button
579  * @param {Object} config The config object
580  */
581
582
583 Roo.bootstrap.Button = function(config){
584     Roo.bootstrap.Button.superclass.constructor.call(this, config);
585     this.weightClass = ["btn-default", 
586                        "btn-primary", 
587                        "btn-success", 
588                        "btn-info", 
589                        "btn-warning",
590                        "btn-danger",
591                        "btn-link"
592                       ],  
593     this.addEvents({
594         // raw events
595         /**
596          * @event click
597          * When a butotn is pressed
598          * @param {Roo.bootstrap.Button} btn
599          * @param {Roo.EventObject} e
600          */
601         "click" : true,
602          /**
603          * @event toggle
604          * After the button has been toggles
605          * @param {Roo.bootstrap.Button} btn
606          * @param {Roo.EventObject} e
607          * @param {boolean} pressed (also available as button.pressed)
608          */
609         "toggle" : true
610     });
611 };
612
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
614     html: false,
615     active: false,
616     weight: '',
617     size: '',
618     tag: 'button',
619     href: '',
620     disabled: false,
621     isClose: false,
622     glyphicon: '',
623     badge: '',
624     theme: 'default',
625     inverse: false,
626     
627     toggle: false,
628     ontext: 'ON',
629     offtext: 'OFF',
630     defaulton: true,
631     preventDefault: true,
632     removeClass: false,
633     name: false,
634     target: false,
635      
636     pressed : null,
637      
638     
639     getAutoCreate : function(){
640         
641         var cfg = {
642             tag : 'button',
643             cls : 'roo-button',
644             html: ''
645         };
646         
647         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
649             this.tag = 'button';
650         } else {
651             cfg.tag = this.tag;
652         }
653         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
654         
655         if (this.toggle == true) {
656             cfg={
657                 tag: 'div',
658                 cls: 'slider-frame roo-button',
659                 cn: [
660                     {
661                         tag: 'span',
662                         'data-on-text':'ON',
663                         'data-off-text':'OFF',
664                         cls: 'slider-button',
665                         html: this.offtext
666                     }
667                 ]
668             };
669             
670             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671                 cfg.cls += ' '+this.weight;
672             }
673             
674             return cfg;
675         }
676         
677         if (this.isClose) {
678             cfg.cls += ' close';
679             
680             cfg["aria-hidden"] = true;
681             
682             cfg.html = "&times;";
683             
684             return cfg;
685         }
686         
687          
688         if (this.theme==='default') {
689             cfg.cls = 'btn roo-button';
690             
691             //if (this.parentType != 'Navbar') {
692             this.weight = this.weight.length ?  this.weight : 'default';
693             //}
694             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
695                 
696                 cfg.cls += ' btn-' + this.weight;
697             }
698         } else if (this.theme==='glow') {
699             
700             cfg.tag = 'a';
701             cfg.cls = 'btn-glow roo-button';
702             
703             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
704                 
705                 cfg.cls += ' ' + this.weight;
706             }
707         }
708    
709         
710         if (this.inverse) {
711             this.cls += ' inverse';
712         }
713         
714         
715         if (this.active || this.pressed === true) {
716             cfg.cls += ' active';
717         }
718         
719         if (this.disabled) {
720             cfg.disabled = 'disabled';
721         }
722         
723         if (this.items) {
724             Roo.log('changing to ul' );
725             cfg.tag = 'ul';
726             this.glyphicon = 'caret';
727         }
728         
729         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
730          
731         //gsRoo.log(this.parentType);
732         if (this.parentType === 'Navbar' && !this.parent().bar) {
733             Roo.log('changing to li?');
734             
735             cfg.tag = 'li';
736             
737             cfg.cls = '';
738             cfg.cn =  [{
739                 tag : 'a',
740                 cls : 'roo-button',
741                 html : this.html,
742                 href : this.href || '#'
743             }];
744             if (this.menu) {
745                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
746                 cfg.cls += ' dropdown';
747             }   
748             
749             delete cfg.html;
750             
751         }
752         
753        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
754         
755         if (this.glyphicon) {
756             cfg.html = ' ' + cfg.html;
757             
758             cfg.cn = [
759                 {
760                     tag: 'span',
761                     cls: 'glyphicon glyphicon-' + this.glyphicon
762                 }
763             ];
764         }
765         
766         if (this.badge) {
767             cfg.html += ' ';
768             
769             cfg.tag = 'a';
770             
771 //            cfg.cls='btn roo-button';
772             
773             cfg.href=this.href;
774             
775             var value = cfg.html;
776             
777             if(this.glyphicon){
778                 value = {
779                             tag: 'span',
780                             cls: 'glyphicon glyphicon-' + this.glyphicon,
781                             html: this.html
782                         };
783                 
784             }
785             
786             cfg.cn = [
787                 value,
788                 {
789                     tag: 'span',
790                     cls: 'badge',
791                     html: this.badge
792                 }
793             ];
794             
795             cfg.html='';
796         }
797         
798         if (this.menu) {
799             cfg.cls += ' dropdown';
800             cfg.html = typeof(cfg.html) != 'undefined' ?
801                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
802         }
803         
804         if (cfg.tag !== 'a' && this.href !== '') {
805             throw "Tag must be a to set href.";
806         } else if (this.href.length > 0) {
807             cfg.href = this.href;
808         }
809         
810         if(this.removeClass){
811             cfg.cls = '';
812         }
813         
814         if(this.target){
815             cfg.target = this.target;
816         }
817         
818         return cfg;
819     },
820     initEvents: function() {
821        // Roo.log('init events?');
822 //        Roo.log(this.el.dom);
823         // add the menu...
824         
825         if (typeof (this.menu) != 'undefined') {
826             this.menu.parentType = this.xtype;
827             this.menu.triggerEl = this.el;
828             this.addxtype(Roo.apply({}, this.menu));
829         }
830
831
832        if (this.el.hasClass('roo-button')) {
833             this.el.on('click', this.onClick, this);
834        } else {
835             this.el.select('.roo-button').on('click', this.onClick, this);
836        }
837        
838        if(this.removeClass){
839            this.el.on('click', this.onClick, this);
840        }
841        
842        this.el.enableDisplayMode();
843         
844     },
845     onClick : function(e)
846     {
847         if (this.disabled) {
848             return;
849         }
850         
851         Roo.log('button on click ');
852         if(this.preventDefault){
853             e.preventDefault();
854         }
855         
856         if (this.pressed === true || this.pressed === false) {
857             this.toggleActive(e);
858         }
859         
860         
861         this.fireEvent('click', this, e);
862     },
863     
864     /**
865      * Enables this button
866      */
867     enable : function()
868     {
869         this.disabled = false;
870         this.el.removeClass('disabled');
871     },
872     
873     /**
874      * Disable this button
875      */
876     disable : function()
877     {
878         this.disabled = true;
879         this.el.addClass('disabled');
880     },
881      /**
882      * sets the active state on/off, 
883      * @param {Boolean} state (optional) Force a particular state
884      */
885     setActive : function(v) {
886         
887         this.el[v ? 'addClass' : 'removeClass']('active');
888         this.pressed = v;
889     },
890      /**
891      * toggles the current active state 
892      */
893     toggleActive : function(e)
894     {
895         this.setActive(!this.pressed);
896         this.fireEvent('toggle', this, e, !this.pressed);
897     },
898      /**
899      * get the current active state
900      * @return {boolean} true if it's active
901      */
902     isActive : function()
903     {
904         return this.el.hasClass('active');
905     },
906     /**
907      * set the text of the first selected button
908      */
909     setText : function(str)
910     {
911         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
912     },
913     /**
914      * get the text of the first selected button
915      */
916     getText : function()
917     {
918         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
919     },
920     hide: function() {
921        
922      
923         this.el.hide();   
924     },
925     show: function() {
926        
927         this.el.show();   
928     },
929     setWeight : function(str)
930     {
931         this.el.removeClass(this.weightClass);
932         this.el.addClass('btn-' + str);        
933     }
934     
935     
936 });
937
938  /*
939  * - LGPL
940  *
941  * column
942  * 
943  */
944
945 /**
946  * @class Roo.bootstrap.Column
947  * @extends Roo.bootstrap.Component
948  * Bootstrap Column class
949  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
950  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
951  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
952  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
953  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
954  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
955  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
956  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
957  *
958  * 
959  * @cfg {Boolean} hidden (true|false) hide the element
960  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
961  * @cfg {String} fa (ban|check|...) font awesome icon
962  * @cfg {Number} fasize (1|2|....) font awsome size
963
964  * @cfg {String} icon (info-sign|check|...) glyphicon name
965
966  * @cfg {String} html content of column.
967  * 
968  * @constructor
969  * Create a new Column
970  * @param {Object} config The config object
971  */
972
973 Roo.bootstrap.Column = function(config){
974     Roo.bootstrap.Column.superclass.constructor.call(this, config);
975 };
976
977 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
978     
979     xs: false,
980     sm: false,
981     md: false,
982     lg: false,
983     xsoff: false,
984     smoff: false,
985     mdoff: false,
986     lgoff: false,
987     html: '',
988     offset: 0,
989     alert: false,
990     fa: false,
991     icon : false,
992     hidden : false,
993     fasize : 1,
994     
995     getAutoCreate : function(){
996         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
997         
998         cfg = {
999             tag: 'div',
1000             cls: 'column'
1001         };
1002         
1003         var settings=this;
1004         ['xs','sm','md','lg'].map(function(size){
1005             //Roo.log( size + ':' + settings[size]);
1006             
1007             if (settings[size+'off'] !== false) {
1008                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1009             }
1010             
1011             if (settings[size] === false) {
1012                 return;
1013             }
1014             
1015             if (!settings[size]) { // 0 = hidden
1016                 cfg.cls += ' hidden-' + size;
1017                 return;
1018             }
1019             cfg.cls += ' col-' + size + '-' + settings[size];
1020             
1021         });
1022         
1023         if (this.hidden) {
1024             cfg.cls += ' hidden';
1025         }
1026         
1027         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1028             cfg.cls +=' alert alert-' + this.alert;
1029         }
1030         
1031         
1032         if (this.html.length) {
1033             cfg.html = this.html;
1034         }
1035         if (this.fa) {
1036             var fasize = '';
1037             if (this.fasize > 1) {
1038                 fasize = ' fa-' + this.fasize + 'x';
1039             }
1040             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1041             
1042             
1043         }
1044         if (this.icon) {
1045             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1046         }
1047         
1048         return cfg;
1049     }
1050    
1051 });
1052
1053  
1054
1055  /*
1056  * - LGPL
1057  *
1058  * page container.
1059  * 
1060  */
1061
1062
1063 /**
1064  * @class Roo.bootstrap.Container
1065  * @extends Roo.bootstrap.Component
1066  * Bootstrap Container class
1067  * @cfg {Boolean} jumbotron is it a jumbotron element
1068  * @cfg {String} html content of element
1069  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1070  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1071  * @cfg {String} header content of header (for panel)
1072  * @cfg {String} footer content of footer (for panel)
1073  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1074  * @cfg {String} tag (header|aside|section) type of HTML tag.
1075  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1076  * @cfg {String} fa font awesome icon
1077  * @cfg {String} icon (info-sign|check|...) glyphicon name
1078  * @cfg {Boolean} hidden (true|false) hide the element
1079  * @cfg {Boolean} expandable (true|false) default false
1080  * @cfg {Boolean} expanded (true|false) default true
1081  * @cfg {String} rheader contet on the right of header
1082  * @cfg {Boolean} clickable (true|false) default false
1083
1084  *     
1085  * @constructor
1086  * Create a new Container
1087  * @param {Object} config The config object
1088  */
1089
1090 Roo.bootstrap.Container = function(config){
1091     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1092     
1093     this.addEvents({
1094         // raw events
1095          /**
1096          * @event expand
1097          * After the panel has been expand
1098          * 
1099          * @param {Roo.bootstrap.Container} this
1100          */
1101         "expand" : true,
1102         /**
1103          * @event collapse
1104          * After the panel has been collapsed
1105          * 
1106          * @param {Roo.bootstrap.Container} this
1107          */
1108         "collapse" : true,
1109         /**
1110          * @event click
1111          * When a element is chick
1112          * @param {Roo.bootstrap.Container} this
1113          * @param {Roo.EventObject} e
1114          */
1115         "click" : true
1116     });
1117 };
1118
1119 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1120     
1121     jumbotron : false,
1122     well: '',
1123     panel : '',
1124     header: '',
1125     footer : '',
1126     sticky: '',
1127     tag : false,
1128     alert : false,
1129     fa: false,
1130     icon : false,
1131     expandable : false,
1132     rheader : '',
1133     expanded : true,
1134     clickable: false,
1135   
1136      
1137     getChildContainer : function() {
1138         
1139         if(!this.el){
1140             return false;
1141         }
1142         
1143         if (this.panel.length) {
1144             return this.el.select('.panel-body',true).first();
1145         }
1146         
1147         return this.el;
1148     },
1149     
1150     
1151     getAutoCreate : function(){
1152         
1153         var cfg = {
1154             tag : this.tag || 'div',
1155             html : '',
1156             cls : ''
1157         };
1158         if (this.jumbotron) {
1159             cfg.cls = 'jumbotron';
1160         }
1161         
1162         
1163         
1164         // - this is applied by the parent..
1165         //if (this.cls) {
1166         //    cfg.cls = this.cls + '';
1167         //}
1168         
1169         if (this.sticky.length) {
1170             
1171             var bd = Roo.get(document.body);
1172             if (!bd.hasClass('bootstrap-sticky')) {
1173                 bd.addClass('bootstrap-sticky');
1174                 Roo.select('html',true).setStyle('height', '100%');
1175             }
1176              
1177             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1178         }
1179         
1180         
1181         if (this.well.length) {
1182             switch (this.well) {
1183                 case 'lg':
1184                 case 'sm':
1185                     cfg.cls +=' well well-' +this.well;
1186                     break;
1187                 default:
1188                     cfg.cls +=' well';
1189                     break;
1190             }
1191         }
1192         
1193         if (this.hidden) {
1194             cfg.cls += ' hidden';
1195         }
1196         
1197         
1198         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1199             cfg.cls +=' alert alert-' + this.alert;
1200         }
1201         
1202         var body = cfg;
1203         
1204         if (this.panel.length) {
1205             cfg.cls += ' panel panel-' + this.panel;
1206             cfg.cn = [];
1207             if (this.header.length) {
1208                 
1209                 var h = [];
1210                 
1211                 if(this.expandable){
1212                     
1213                     cfg.cls = cfg.cls + ' expandable';
1214                     
1215                     h.push({
1216                         tag: 'i',
1217                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1218                     });
1219                     
1220                 }
1221                 
1222                 h.push(
1223                     {
1224                         tag: 'span',
1225                         cls : 'panel-title',
1226                         html : (this.expandable ? '&nbsp;' : '') + this.header
1227                     },
1228                     {
1229                         tag: 'span',
1230                         cls: 'panel-header-right',
1231                         html: this.rheader
1232                     }
1233                 );
1234                 
1235                 cfg.cn.push({
1236                     cls : 'panel-heading',
1237                     style : this.expandable ? 'cursor: pointer' : '',
1238                     cn : h
1239                 });
1240                 
1241             }
1242             
1243             body = false;
1244             cfg.cn.push({
1245                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1246                 html : this.html
1247             });
1248             
1249             
1250             if (this.footer.length) {
1251                 cfg.cn.push({
1252                     cls : 'panel-footer',
1253                     html : this.footer
1254                     
1255                 });
1256             }
1257             
1258         }
1259         
1260         if (body) {
1261             body.html = this.html || cfg.html;
1262             // prefix with the icons..
1263             if (this.fa) {
1264                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1265             }
1266             if (this.icon) {
1267                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1268             }
1269             
1270             
1271         }
1272         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1273             cfg.cls =  'container';
1274         }
1275         
1276         return cfg;
1277     },
1278     
1279     initEvents: function() 
1280     {
1281         if(this.expandable){
1282             var headerEl = this.headerEl();
1283         
1284             if(headerEl){
1285                 headerEl.on('click', this.onToggleClick, this);
1286             }
1287         }
1288         
1289         if(this.clickable){
1290             this.el.on('click', this.onClick, this);
1291         }
1292         
1293     },
1294     
1295     onToggleClick : function()
1296     {
1297         var headerEl = this.headerEl();
1298         
1299         if(!headerEl){
1300             return;
1301         }
1302         
1303         if(this.expanded){
1304             this.collapse();
1305             return;
1306         }
1307         
1308         this.expand();
1309     },
1310     
1311     expand : function()
1312     {
1313         if(this.fireEvent('expand', this)) {
1314             
1315             this.expanded = true;
1316             
1317             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1318             
1319             this.el.select('.panel-body',true).first().removeClass('hide');
1320             
1321             var toggleEl = this.toggleEl();
1322
1323             if(!toggleEl){
1324                 return;
1325             }
1326
1327             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1328         }
1329         
1330     },
1331     
1332     collapse : function()
1333     {
1334         if(this.fireEvent('collapse', this)) {
1335             
1336             this.expanded = false;
1337             
1338             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1339             this.el.select('.panel-body',true).first().addClass('hide');
1340         
1341             var toggleEl = this.toggleEl();
1342
1343             if(!toggleEl){
1344                 return;
1345             }
1346
1347             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1348         }
1349     },
1350     
1351     toggleEl : function()
1352     {
1353         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1354             return;
1355         }
1356         
1357         return this.el.select('.panel-heading .fa',true).first();
1358     },
1359     
1360     headerEl : function()
1361     {
1362         if(!this.el || !this.panel.length || !this.header.length){
1363             return;
1364         }
1365         
1366         return this.el.select('.panel-heading',true).first()
1367     },
1368     
1369     bodyEl : function()
1370     {
1371         if(!this.el || !this.panel.length){
1372             return;
1373         }
1374         
1375         return this.el.select('.panel-body',true).first()
1376     },
1377     
1378     titleEl : function()
1379     {
1380         if(!this.el || !this.panel.length || !this.header.length){
1381             return;
1382         }
1383         
1384         return this.el.select('.panel-title',true).first();
1385     },
1386     
1387     setTitle : function(v)
1388     {
1389         var titleEl = this.titleEl();
1390         
1391         if(!titleEl){
1392             return;
1393         }
1394         
1395         titleEl.dom.innerHTML = v;
1396     },
1397     
1398     getTitle : function()
1399     {
1400         
1401         var titleEl = this.titleEl();
1402         
1403         if(!titleEl){
1404             return '';
1405         }
1406         
1407         return titleEl.dom.innerHTML;
1408     },
1409     
1410     setRightTitle : function(v)
1411     {
1412         var t = this.el.select('.panel-header-right',true).first();
1413         
1414         if(!t){
1415             return;
1416         }
1417         
1418         t.dom.innerHTML = v;
1419     },
1420     
1421     onClick : function(e)
1422     {
1423         e.preventDefault();
1424         
1425         this.fireEvent('click', this, e);
1426     }
1427 });
1428
1429  /*
1430  * - LGPL
1431  *
1432  * image
1433  * 
1434  */
1435
1436
1437 /**
1438  * @class Roo.bootstrap.Img
1439  * @extends Roo.bootstrap.Component
1440  * Bootstrap Img class
1441  * @cfg {Boolean} imgResponsive false | true
1442  * @cfg {String} border rounded | circle | thumbnail
1443  * @cfg {String} src image source
1444  * @cfg {String} alt image alternative text
1445  * @cfg {String} href a tag href
1446  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1447  * @cfg {String} xsUrl xs image source
1448  * @cfg {String} smUrl sm image source
1449  * @cfg {String} mdUrl md image source
1450  * @cfg {String} lgUrl lg image source
1451  * 
1452  * @constructor
1453  * Create a new Input
1454  * @param {Object} config The config object
1455  */
1456
1457 Roo.bootstrap.Img = function(config){
1458     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1459     
1460     this.addEvents({
1461         // img events
1462         /**
1463          * @event click
1464          * The img click event for the img.
1465          * @param {Roo.EventObject} e
1466          */
1467         "click" : true
1468     });
1469 };
1470
1471 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1472     
1473     imgResponsive: true,
1474     border: '',
1475     src: 'about:blank',
1476     href: false,
1477     target: false,
1478     xsUrl: '',
1479     smUrl: '',
1480     mdUrl: '',
1481     lgUrl: '',
1482
1483     getAutoCreate : function()
1484     {   
1485         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1486             return this.createSingleImg();
1487         }
1488         
1489         var cfg = {
1490             tag: 'div',
1491             cls: 'roo-image-responsive-group',
1492             cn: []
1493         };
1494         var _this = this;
1495         
1496         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1497             
1498             if(!_this[size + 'Url']){
1499                 return;
1500             }
1501             
1502             var img = {
1503                 tag: 'img',
1504                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1505                 html: _this.html || cfg.html,
1506                 src: _this[size + 'Url']
1507             };
1508             
1509             img.cls += ' roo-image-responsive-' + size;
1510             
1511             var s = ['xs', 'sm', 'md', 'lg'];
1512             
1513             s.splice(s.indexOf(size), 1);
1514             
1515             Roo.each(s, function(ss){
1516                 img.cls += ' hidden-' + ss;
1517             });
1518             
1519             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1520                 cfg.cls += ' img-' + _this.border;
1521             }
1522             
1523             if(_this.alt){
1524                 cfg.alt = _this.alt;
1525             }
1526             
1527             if(_this.href){
1528                 var a = {
1529                     tag: 'a',
1530                     href: _this.href,
1531                     cn: [
1532                         img
1533                     ]
1534                 };
1535
1536                 if(this.target){
1537                     a.target = _this.target;
1538                 }
1539             }
1540             
1541             cfg.cn.push((_this.href) ? a : img);
1542             
1543         });
1544         
1545         return cfg;
1546     },
1547     
1548     createSingleImg : function()
1549     {
1550         var cfg = {
1551             tag: 'img',
1552             cls: (this.imgResponsive) ? 'img-responsive' : '',
1553             html : null,
1554             src : 'about:blank'  // just incase src get's set to undefined?!?
1555         };
1556         
1557         cfg.html = this.html || cfg.html;
1558         
1559         cfg.src = this.src || cfg.src;
1560         
1561         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1562             cfg.cls += ' img-' + this.border;
1563         }
1564         
1565         if(this.alt){
1566             cfg.alt = this.alt;
1567         }
1568         
1569         if(this.href){
1570             var a = {
1571                 tag: 'a',
1572                 href: this.href,
1573                 cn: [
1574                     cfg
1575                 ]
1576             };
1577             
1578             if(this.target){
1579                 a.target = this.target;
1580             }
1581             
1582         }
1583         
1584         return (this.href) ? a : cfg;
1585     },
1586     
1587     initEvents: function() 
1588     {
1589         if(!this.href){
1590             this.el.on('click', this.onClick, this);
1591         }
1592         
1593     },
1594     
1595     onClick : function(e)
1596     {
1597         Roo.log('img onclick');
1598         this.fireEvent('click', this, e);
1599     },
1600     /**
1601      * Sets the url of the image - used to update it
1602      * @param {String} url the url of the image
1603      */
1604     
1605     setSrc : function(url)
1606     {
1607         this.src =  url;
1608         
1609         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1610             this.el.dom.src =  url;
1611             return;
1612         }
1613         
1614         this.el.select('img', true).first().dom.src =  url;
1615     }
1616     
1617     
1618    
1619 });
1620
1621  /*
1622  * - LGPL
1623  *
1624  * image
1625  * 
1626  */
1627
1628
1629 /**
1630  * @class Roo.bootstrap.Link
1631  * @extends Roo.bootstrap.Component
1632  * Bootstrap Link Class
1633  * @cfg {String} alt image alternative text
1634  * @cfg {String} href a tag href
1635  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1636  * @cfg {String} html the content of the link.
1637  * @cfg {String} anchor name for the anchor link
1638  * @cfg {String} fa - favicon
1639
1640  * @cfg {Boolean} preventDefault (true | false) default false
1641
1642  * 
1643  * @constructor
1644  * Create a new Input
1645  * @param {Object} config The config object
1646  */
1647
1648 Roo.bootstrap.Link = function(config){
1649     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1650     
1651     this.addEvents({
1652         // img events
1653         /**
1654          * @event click
1655          * The img click event for the img.
1656          * @param {Roo.EventObject} e
1657          */
1658         "click" : true
1659     });
1660 };
1661
1662 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1663     
1664     href: false,
1665     target: false,
1666     preventDefault: false,
1667     anchor : false,
1668     alt : false,
1669     fa: false,
1670
1671
1672     getAutoCreate : function()
1673     {
1674         var html = this.html || '';
1675         
1676         if (this.fa !== false) {
1677             html = '<i class="fa fa-' + this.fa + '"></i>';
1678         }
1679         var cfg = {
1680             tag: 'a'
1681         };
1682         // anchor's do not require html/href...
1683         if (this.anchor === false) {
1684             cfg.html = html;
1685             cfg.href = this.href || '#';
1686         } else {
1687             cfg.name = this.anchor;
1688             if (this.html !== false || this.fa !== false) {
1689                 cfg.html = html;
1690             }
1691             if (this.href !== false) {
1692                 cfg.href = this.href;
1693             }
1694         }
1695         
1696         if(this.alt !== false){
1697             cfg.alt = this.alt;
1698         }
1699         
1700         
1701         if(this.target !== false) {
1702             cfg.target = this.target;
1703         }
1704         
1705         return cfg;
1706     },
1707     
1708     initEvents: function() {
1709         
1710         if(!this.href || this.preventDefault){
1711             this.el.on('click', this.onClick, this);
1712         }
1713     },
1714     
1715     onClick : function(e)
1716     {
1717         if(this.preventDefault){
1718             e.preventDefault();
1719         }
1720         //Roo.log('img onclick');
1721         this.fireEvent('click', this, e);
1722     }
1723    
1724 });
1725
1726  /*
1727  * - LGPL
1728  *
1729  * header
1730  * 
1731  */
1732
1733 /**
1734  * @class Roo.bootstrap.Header
1735  * @extends Roo.bootstrap.Component
1736  * Bootstrap Header class
1737  * @cfg {String} html content of header
1738  * @cfg {Number} level (1|2|3|4|5|6) default 1
1739  * 
1740  * @constructor
1741  * Create a new Header
1742  * @param {Object} config The config object
1743  */
1744
1745
1746 Roo.bootstrap.Header  = function(config){
1747     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1748 };
1749
1750 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1751     
1752     //href : false,
1753     html : false,
1754     level : 1,
1755     
1756     
1757     
1758     getAutoCreate : function(){
1759         
1760         
1761         
1762         var cfg = {
1763             tag: 'h' + (1 *this.level),
1764             html: this.html || ''
1765         } ;
1766         
1767         return cfg;
1768     }
1769    
1770 });
1771
1772  
1773
1774  /*
1775  * Based on:
1776  * Ext JS Library 1.1.1
1777  * Copyright(c) 2006-2007, Ext JS, LLC.
1778  *
1779  * Originally Released Under LGPL - original licence link has changed is not relivant.
1780  *
1781  * Fork - LGPL
1782  * <script type="text/javascript">
1783  */
1784  
1785 /**
1786  * @class Roo.bootstrap.MenuMgr
1787  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1788  * @singleton
1789  */
1790 Roo.bootstrap.MenuMgr = function(){
1791    var menus, active, groups = {}, attached = false, lastShow = new Date();
1792
1793    // private - called when first menu is created
1794    function init(){
1795        menus = {};
1796        active = new Roo.util.MixedCollection();
1797        Roo.get(document).addKeyListener(27, function(){
1798            if(active.length > 0){
1799                hideAll();
1800            }
1801        });
1802    }
1803
1804    // private
1805    function hideAll(){
1806        if(active && active.length > 0){
1807            var c = active.clone();
1808            c.each(function(m){
1809                m.hide();
1810            });
1811        }
1812    }
1813
1814    // private
1815    function onHide(m){
1816        active.remove(m);
1817        if(active.length < 1){
1818            Roo.get(document).un("mouseup", onMouseDown);
1819             
1820            attached = false;
1821        }
1822    }
1823
1824    // private
1825    function onShow(m){
1826        var last = active.last();
1827        lastShow = new Date();
1828        active.add(m);
1829        if(!attached){
1830           Roo.get(document).on("mouseup", onMouseDown);
1831            
1832            attached = true;
1833        }
1834        if(m.parentMenu){
1835           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1836           m.parentMenu.activeChild = m;
1837        }else if(last && last.isVisible()){
1838           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1839        }
1840    }
1841
1842    // private
1843    function onBeforeHide(m){
1844        if(m.activeChild){
1845            m.activeChild.hide();
1846        }
1847        if(m.autoHideTimer){
1848            clearTimeout(m.autoHideTimer);
1849            delete m.autoHideTimer;
1850        }
1851    }
1852
1853    // private
1854    function onBeforeShow(m){
1855        var pm = m.parentMenu;
1856        if(!pm && !m.allowOtherMenus){
1857            hideAll();
1858        }else if(pm && pm.activeChild && active != m){
1859            pm.activeChild.hide();
1860        }
1861    }
1862
1863    // private this should really trigger on mouseup..
1864    function onMouseDown(e){
1865         Roo.log("on Mouse Up");
1866         
1867         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1868             Roo.log("MenuManager hideAll");
1869             hideAll();
1870             e.stopEvent();
1871         }
1872         
1873         
1874    }
1875
1876    // private
1877    function onBeforeCheck(mi, state){
1878        if(state){
1879            var g = groups[mi.group];
1880            for(var i = 0, l = g.length; i < l; i++){
1881                if(g[i] != mi){
1882                    g[i].setChecked(false);
1883                }
1884            }
1885        }
1886    }
1887
1888    return {
1889
1890        /**
1891         * Hides all menus that are currently visible
1892         */
1893        hideAll : function(){
1894             hideAll();  
1895        },
1896
1897        // private
1898        register : function(menu){
1899            if(!menus){
1900                init();
1901            }
1902            menus[menu.id] = menu;
1903            menu.on("beforehide", onBeforeHide);
1904            menu.on("hide", onHide);
1905            menu.on("beforeshow", onBeforeShow);
1906            menu.on("show", onShow);
1907            var g = menu.group;
1908            if(g && menu.events["checkchange"]){
1909                if(!groups[g]){
1910                    groups[g] = [];
1911                }
1912                groups[g].push(menu);
1913                menu.on("checkchange", onCheck);
1914            }
1915        },
1916
1917         /**
1918          * Returns a {@link Roo.menu.Menu} object
1919          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1920          * be used to generate and return a new Menu instance.
1921          */
1922        get : function(menu){
1923            if(typeof menu == "string"){ // menu id
1924                return menus[menu];
1925            }else if(menu.events){  // menu instance
1926                return menu;
1927            }
1928            /*else if(typeof menu.length == 'number'){ // array of menu items?
1929                return new Roo.bootstrap.Menu({items:menu});
1930            }else{ // otherwise, must be a config
1931                return new Roo.bootstrap.Menu(menu);
1932            }
1933            */
1934            return false;
1935        },
1936
1937        // private
1938        unregister : function(menu){
1939            delete menus[menu.id];
1940            menu.un("beforehide", onBeforeHide);
1941            menu.un("hide", onHide);
1942            menu.un("beforeshow", onBeforeShow);
1943            menu.un("show", onShow);
1944            var g = menu.group;
1945            if(g && menu.events["checkchange"]){
1946                groups[g].remove(menu);
1947                menu.un("checkchange", onCheck);
1948            }
1949        },
1950
1951        // private
1952        registerCheckable : function(menuItem){
1953            var g = menuItem.group;
1954            if(g){
1955                if(!groups[g]){
1956                    groups[g] = [];
1957                }
1958                groups[g].push(menuItem);
1959                menuItem.on("beforecheckchange", onBeforeCheck);
1960            }
1961        },
1962
1963        // private
1964        unregisterCheckable : function(menuItem){
1965            var g = menuItem.group;
1966            if(g){
1967                groups[g].remove(menuItem);
1968                menuItem.un("beforecheckchange", onBeforeCheck);
1969            }
1970        }
1971    };
1972 }();/*
1973  * - LGPL
1974  *
1975  * menu
1976  * 
1977  */
1978
1979 /**
1980  * @class Roo.bootstrap.Menu
1981  * @extends Roo.bootstrap.Component
1982  * Bootstrap Menu class - container for MenuItems
1983  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1984  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1985  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1986  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1987  * 
1988  * @constructor
1989  * Create a new Menu
1990  * @param {Object} config The config object
1991  */
1992
1993
1994 Roo.bootstrap.Menu = function(config){
1995     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1996     if (this.registerMenu && this.type != 'treeview')  {
1997         Roo.bootstrap.MenuMgr.register(this);
1998     }
1999     this.addEvents({
2000         /**
2001          * @event beforeshow
2002          * Fires before this menu is displayed
2003          * @param {Roo.menu.Menu} this
2004          */
2005         beforeshow : true,
2006         /**
2007          * @event beforehide
2008          * Fires before this menu is hidden
2009          * @param {Roo.menu.Menu} this
2010          */
2011         beforehide : true,
2012         /**
2013          * @event show
2014          * Fires after this menu is displayed
2015          * @param {Roo.menu.Menu} this
2016          */
2017         show : true,
2018         /**
2019          * @event hide
2020          * Fires after this menu is hidden
2021          * @param {Roo.menu.Menu} this
2022          */
2023         hide : true,
2024         /**
2025          * @event click
2026          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2027          * @param {Roo.menu.Menu} this
2028          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2029          * @param {Roo.EventObject} e
2030          */
2031         click : true,
2032         /**
2033          * @event mouseover
2034          * Fires when the mouse is hovering over this menu
2035          * @param {Roo.menu.Menu} this
2036          * @param {Roo.EventObject} e
2037          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2038          */
2039         mouseover : true,
2040         /**
2041          * @event mouseout
2042          * Fires when the mouse exits this menu
2043          * @param {Roo.menu.Menu} this
2044          * @param {Roo.EventObject} e
2045          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2046          */
2047         mouseout : true,
2048         /**
2049          * @event itemclick
2050          * Fires when a menu item contained in this menu is clicked
2051          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2052          * @param {Roo.EventObject} e
2053          */
2054         itemclick: true
2055     });
2056     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2057 };
2058
2059 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2060     
2061    /// html : false,
2062     //align : '',
2063     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2064     type: false,
2065     /**
2066      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2067      */
2068     registerMenu : true,
2069     
2070     menuItems :false, // stores the menu items..
2071     
2072     hidden:true,
2073         
2074     parentMenu : false,
2075     
2076     stopEvent : true,
2077     
2078     isLink : false,
2079     
2080     getChildContainer : function() {
2081         return this.el;  
2082     },
2083     
2084     getAutoCreate : function(){
2085          
2086         //if (['right'].indexOf(this.align)!==-1) {
2087         //    cfg.cn[1].cls += ' pull-right'
2088         //}
2089         
2090         
2091         var cfg = {
2092             tag : 'ul',
2093             cls : 'dropdown-menu' ,
2094             style : 'z-index:1000'
2095             
2096         };
2097         
2098         if (this.type === 'submenu') {
2099             cfg.cls = 'submenu active';
2100         }
2101         if (this.type === 'treeview') {
2102             cfg.cls = 'treeview-menu';
2103         }
2104         
2105         return cfg;
2106     },
2107     initEvents : function() {
2108         
2109        // Roo.log("ADD event");
2110        // Roo.log(this.triggerEl.dom);
2111         
2112         this.triggerEl.on('click', this.onTriggerClick, this);
2113         
2114         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2115         
2116         this.triggerEl.addClass('dropdown-toggle');
2117         
2118         if (Roo.isTouch) {
2119             this.el.on('touchstart'  , this.onTouch, this);
2120         }
2121         this.el.on('click' , this.onClick, this);
2122
2123         this.el.on("mouseover", this.onMouseOver, this);
2124         this.el.on("mouseout", this.onMouseOut, this);
2125         
2126     },
2127     
2128     findTargetItem : function(e)
2129     {
2130         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2131         if(!t){
2132             return false;
2133         }
2134         //Roo.log(t);         Roo.log(t.id);
2135         if(t && t.id){
2136             //Roo.log(this.menuitems);
2137             return this.menuitems.get(t.id);
2138             
2139             //return this.items.get(t.menuItemId);
2140         }
2141         
2142         return false;
2143     },
2144     
2145     onTouch : function(e) 
2146     {
2147         Roo.log("menu.onTouch");
2148         //e.stopEvent(); this make the user popdown broken
2149         this.onClick(e);
2150     },
2151     
2152     onClick : function(e)
2153     {
2154         Roo.log("menu.onClick");
2155         
2156         var t = this.findTargetItem(e);
2157         if(!t || t.isContainer){
2158             return;
2159         }
2160         Roo.log(e);
2161         /*
2162         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2163             if(t == this.activeItem && t.shouldDeactivate(e)){
2164                 this.activeItem.deactivate();
2165                 delete this.activeItem;
2166                 return;
2167             }
2168             if(t.canActivate){
2169                 this.setActiveItem(t, true);
2170             }
2171             return;
2172             
2173             
2174         }
2175         */
2176        
2177         Roo.log('pass click event');
2178         
2179         t.onClick(e);
2180         
2181         this.fireEvent("click", this, t, e);
2182         
2183         var _this = this;
2184         
2185         if(!t.href.length || t.href == '#'){
2186             (function() { _this.hide(); }).defer(100);
2187         }
2188         
2189     },
2190     
2191     onMouseOver : function(e){
2192         var t  = this.findTargetItem(e);
2193         //Roo.log(t);
2194         //if(t){
2195         //    if(t.canActivate && !t.disabled){
2196         //        this.setActiveItem(t, true);
2197         //    }
2198         //}
2199         
2200         this.fireEvent("mouseover", this, e, t);
2201     },
2202     isVisible : function(){
2203         return !this.hidden;
2204     },
2205      onMouseOut : function(e){
2206         var t  = this.findTargetItem(e);
2207         
2208         //if(t ){
2209         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2210         //        this.activeItem.deactivate();
2211         //        delete this.activeItem;
2212         //    }
2213         //}
2214         this.fireEvent("mouseout", this, e, t);
2215     },
2216     
2217     
2218     /**
2219      * Displays this menu relative to another element
2220      * @param {String/HTMLElement/Roo.Element} element The element to align to
2221      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2222      * the element (defaults to this.defaultAlign)
2223      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2224      */
2225     show : function(el, pos, parentMenu){
2226         this.parentMenu = parentMenu;
2227         if(!this.el){
2228             this.render();
2229         }
2230         this.fireEvent("beforeshow", this);
2231         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2232     },
2233      /**
2234      * Displays this menu at a specific xy position
2235      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2236      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2237      */
2238     showAt : function(xy, parentMenu, /* private: */_e){
2239         this.parentMenu = parentMenu;
2240         if(!this.el){
2241             this.render();
2242         }
2243         if(_e !== false){
2244             this.fireEvent("beforeshow", this);
2245             //xy = this.el.adjustForConstraints(xy);
2246         }
2247         
2248         //this.el.show();
2249         this.hideMenuItems();
2250         this.hidden = false;
2251         this.triggerEl.addClass('open');
2252         
2253         // reassign x when hitting right
2254         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2255             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2256         }
2257         
2258         // reassign y when hitting bottom
2259         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2260             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2261         }
2262         
2263         // but the list may align on trigger left or trigger top... should it be a properity?
2264         
2265         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2266             this.el.setXY(xy);
2267         }
2268         
2269         this.focus();
2270         this.fireEvent("show", this);
2271     },
2272     
2273     focus : function(){
2274         return;
2275         if(!this.hidden){
2276             this.doFocus.defer(50, this);
2277         }
2278     },
2279
2280     doFocus : function(){
2281         if(!this.hidden){
2282             this.focusEl.focus();
2283         }
2284     },
2285
2286     /**
2287      * Hides this menu and optionally all parent menus
2288      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2289      */
2290     hide : function(deep)
2291     {
2292         
2293         this.hideMenuItems();
2294         if(this.el && this.isVisible()){
2295             this.fireEvent("beforehide", this);
2296             if(this.activeItem){
2297                 this.activeItem.deactivate();
2298                 this.activeItem = null;
2299             }
2300             this.triggerEl.removeClass('open');;
2301             this.hidden = true;
2302             this.fireEvent("hide", this);
2303         }
2304         if(deep === true && this.parentMenu){
2305             this.parentMenu.hide(true);
2306         }
2307     },
2308     
2309     onTriggerClick : function(e)
2310     {
2311         Roo.log('trigger click');
2312         
2313         var target = e.getTarget();
2314         
2315         Roo.log(target.nodeName.toLowerCase());
2316         
2317         if(target.nodeName.toLowerCase() === 'i'){
2318             e.preventDefault();
2319         }
2320         
2321     },
2322     
2323     onTriggerPress  : function(e)
2324     {
2325         Roo.log('trigger press');
2326         //Roo.log(e.getTarget());
2327        // Roo.log(this.triggerEl.dom);
2328        
2329         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2330         var pel = Roo.get(e.getTarget());
2331         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2332             Roo.log('is treeview or dropdown?');
2333             return;
2334         }
2335         
2336         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2337             return;
2338         }
2339         
2340         if (this.isVisible()) {
2341             Roo.log('hide');
2342             this.hide();
2343         } else {
2344             Roo.log('show');
2345             this.show(this.triggerEl, false, false);
2346         }
2347         
2348         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2349             e.stopEvent();
2350         }
2351         
2352     },
2353        
2354     
2355     hideMenuItems : function()
2356     {
2357         Roo.log("hide Menu Items");
2358         if (!this.el) { 
2359             return;
2360         }
2361         //$(backdrop).remove()
2362         this.el.select('.open',true).each(function(aa) {
2363             
2364             aa.removeClass('open');
2365           //var parent = getParent($(this))
2366           //var relatedTarget = { relatedTarget: this }
2367           
2368            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2369           //if (e.isDefaultPrevented()) return
2370            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2371         });
2372     },
2373     addxtypeChild : function (tree, cntr) {
2374         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2375           
2376         this.menuitems.add(comp);
2377         return comp;
2378
2379     },
2380     getEl : function()
2381     {
2382         Roo.log(this.el);
2383         return this.el;
2384     },
2385     
2386     clear : function()
2387     {
2388         this.getEl().dom.innerHTML = '';
2389         this.menuitems.clear();
2390     }
2391 });
2392
2393  
2394  /*
2395  * - LGPL
2396  *
2397  * menu item
2398  * 
2399  */
2400
2401
2402 /**
2403  * @class Roo.bootstrap.MenuItem
2404  * @extends Roo.bootstrap.Component
2405  * Bootstrap MenuItem class
2406  * @cfg {String} html the menu label
2407  * @cfg {String} href the link
2408  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2409  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2410  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2411  * @cfg {String} fa favicon to show on left of menu item.
2412  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2413  * 
2414  * 
2415  * @constructor
2416  * Create a new MenuItem
2417  * @param {Object} config The config object
2418  */
2419
2420
2421 Roo.bootstrap.MenuItem = function(config){
2422     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2423     this.addEvents({
2424         // raw events
2425         /**
2426          * @event click
2427          * The raw click event for the entire grid.
2428          * @param {Roo.bootstrap.MenuItem} this
2429          * @param {Roo.EventObject} e
2430          */
2431         "click" : true
2432     });
2433 };
2434
2435 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2436     
2437     href : false,
2438     html : false,
2439     preventDefault: false,
2440     isContainer : false,
2441     active : false,
2442     fa: false,
2443     
2444     getAutoCreate : function(){
2445         
2446         if(this.isContainer){
2447             return {
2448                 tag: 'li',
2449                 cls: 'dropdown-menu-item'
2450             };
2451         }
2452         var ctag = {
2453             tag: 'span',
2454             html: 'Link'
2455         };
2456         
2457         var anc = {
2458             tag : 'a',
2459             href : '#',
2460             cn : [  ]
2461         };
2462         
2463         if (this.fa !== false) {
2464             anc.cn.push({
2465                 tag : 'i',
2466                 cls : 'fa fa-' + this.fa
2467             });
2468         }
2469         
2470         anc.cn.push(ctag);
2471         
2472         
2473         var cfg= {
2474             tag: 'li',
2475             cls: 'dropdown-menu-item',
2476             cn: [ anc ]
2477         };
2478         if (this.parent().type == 'treeview') {
2479             cfg.cls = 'treeview-menu';
2480         }
2481         if (this.active) {
2482             cfg.cls += ' active';
2483         }
2484         
2485         
2486         
2487         anc.href = this.href || cfg.cn[0].href ;
2488         ctag.html = this.html || cfg.cn[0].html ;
2489         return cfg;
2490     },
2491     
2492     initEvents: function()
2493     {
2494         if (this.parent().type == 'treeview') {
2495             this.el.select('a').on('click', this.onClick, this);
2496         }
2497         
2498         if (this.menu) {
2499             this.menu.parentType = this.xtype;
2500             this.menu.triggerEl = this.el;
2501             this.menu = this.addxtype(Roo.apply({}, this.menu));
2502         }
2503         
2504     },
2505     onClick : function(e)
2506     {
2507         Roo.log('item on click ');
2508         
2509         if(this.preventDefault){
2510             e.preventDefault();
2511         }
2512         //this.parent().hideMenuItems();
2513         
2514         this.fireEvent('click', this, e);
2515     },
2516     getEl : function()
2517     {
2518         return this.el;
2519     } 
2520 });
2521
2522  
2523
2524  /*
2525  * - LGPL
2526  *
2527  * menu separator
2528  * 
2529  */
2530
2531
2532 /**
2533  * @class Roo.bootstrap.MenuSeparator
2534  * @extends Roo.bootstrap.Component
2535  * Bootstrap MenuSeparator class
2536  * 
2537  * @constructor
2538  * Create a new MenuItem
2539  * @param {Object} config The config object
2540  */
2541
2542
2543 Roo.bootstrap.MenuSeparator = function(config){
2544     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2545 };
2546
2547 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2548     
2549     getAutoCreate : function(){
2550         var cfg = {
2551             cls: 'divider',
2552             tag : 'li'
2553         };
2554         
2555         return cfg;
2556     }
2557    
2558 });
2559
2560  
2561
2562  
2563 /*
2564 * Licence: LGPL
2565 */
2566
2567 /**
2568  * @class Roo.bootstrap.Modal
2569  * @extends Roo.bootstrap.Component
2570  * Bootstrap Modal class
2571  * @cfg {String} title Title of dialog
2572  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2573  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2574  * @cfg {Boolean} specificTitle default false
2575  * @cfg {Array} buttons Array of buttons or standard button set..
2576  * @cfg {String} buttonPosition (left|right|center) default right
2577  * @cfg {Boolean} animate default true
2578  * @cfg {Boolean} allow_close default true
2579  * @cfg {Boolean} fitwindow default false
2580  * @cfg {String} size (sm|lg) default empty
2581  * @cfg {Number} max_width set the max width of modal
2582  *
2583  *
2584  * @constructor
2585  * Create a new Modal Dialog
2586  * @param {Object} config The config object
2587  */
2588
2589 Roo.bootstrap.Modal = function(config){
2590     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2591     this.addEvents({
2592         // raw events
2593         /**
2594          * @event btnclick
2595          * The raw btnclick event for the button
2596          * @param {Roo.EventObject} e
2597          */
2598         "btnclick" : true,
2599         /**
2600          * @event resize
2601          * Fire when dialog resize
2602          * @param {Roo.bootstrap.Modal} this
2603          * @param {Roo.EventObject} e
2604          */
2605         "resize" : true
2606     });
2607     this.buttons = this.buttons || [];
2608
2609     if (this.tmpl) {
2610         this.tmpl = Roo.factory(this.tmpl);
2611     }
2612
2613 };
2614
2615 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2616
2617     title : 'test dialog',
2618
2619     buttons : false,
2620
2621     // set on load...
2622
2623     html: false,
2624
2625     tmp: false,
2626
2627     specificTitle: false,
2628
2629     buttonPosition: 'right',
2630
2631     allow_close : true,
2632
2633     animate : true,
2634
2635     fitwindow: false,
2636
2637
2638      // private
2639     dialogEl: false,
2640     bodyEl:  false,
2641     footerEl:  false,
2642     titleEl:  false,
2643     closeEl:  false,
2644
2645     size: '',
2646     
2647     max_width: 0,
2648     
2649     fit_content: false,
2650
2651     onRender : function(ct, position)
2652     {
2653         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2654
2655         if(!this.el){
2656             var cfg = Roo.apply({},  this.getAutoCreate());
2657             cfg.id = Roo.id();
2658             //if(!cfg.name){
2659             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2660             //}
2661             //if (!cfg.name.length) {
2662             //    delete cfg.name;
2663            // }
2664             if (this.cls) {
2665                 cfg.cls += ' ' + this.cls;
2666             }
2667             if (this.style) {
2668                 cfg.style = this.style;
2669             }
2670             this.el = Roo.get(document.body).createChild(cfg, position);
2671         }
2672         //var type = this.el.dom.type;
2673
2674
2675         if(this.tabIndex !== undefined){
2676             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2677         }
2678
2679         this.dialogEl = this.el.select('.modal-dialog',true).first();
2680         this.bodyEl = this.el.select('.modal-body',true).first();
2681         this.closeEl = this.el.select('.modal-header .close', true).first();
2682         this.headerEl = this.el.select('.modal-header',true).first();
2683         this.titleEl = this.el.select('.modal-title',true).first();
2684         this.footerEl = this.el.select('.modal-footer',true).first();
2685
2686         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2687         
2688         //this.el.addClass("x-dlg-modal");
2689
2690         if (this.buttons.length) {
2691             Roo.each(this.buttons, function(bb) {
2692                 var b = Roo.apply({}, bb);
2693                 b.xns = b.xns || Roo.bootstrap;
2694                 b.xtype = b.xtype || 'Button';
2695                 if (typeof(b.listeners) == 'undefined') {
2696                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2697                 }
2698
2699                 var btn = Roo.factory(b);
2700
2701                 btn.render(this.el.select('.modal-footer div').first());
2702
2703             },this);
2704         }
2705         // render the children.
2706         var nitems = [];
2707
2708         if(typeof(this.items) != 'undefined'){
2709             var items = this.items;
2710             delete this.items;
2711
2712             for(var i =0;i < items.length;i++) {
2713                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2714             }
2715         }
2716
2717         this.items = nitems;
2718
2719         // where are these used - they used to be body/close/footer
2720
2721
2722         this.initEvents();
2723         //this.el.addClass([this.fieldClass, this.cls]);
2724
2725     },
2726
2727     getAutoCreate : function()
2728     {
2729         var bdy = {
2730                 cls : 'modal-body',
2731                 html : this.html || ''
2732         };
2733
2734         var title = {
2735             tag: 'h4',
2736             cls : 'modal-title',
2737             html : this.title
2738         };
2739
2740         if(this.specificTitle){
2741             title = this.title;
2742
2743         };
2744
2745         var header = [];
2746         if (this.allow_close) {
2747             header.push({
2748                 tag: 'button',
2749                 cls : 'close',
2750                 html : '&times'
2751             });
2752         }
2753
2754         header.push(title);
2755
2756         var size = '';
2757
2758         if(this.size.length){
2759             size = 'modal-' + this.size;
2760         }
2761
2762         var modal = {
2763             cls: "modal",
2764              cn : [
2765                 {
2766                     cls: "modal-dialog " + size,
2767                     cn : [
2768                         {
2769                             cls : "modal-content",
2770                             cn : [
2771                                 {
2772                                     cls : 'modal-header',
2773                                     cn : header
2774                                 },
2775                                 bdy,
2776                                 {
2777                                     cls : 'modal-footer',
2778                                     cn : [
2779                                         {
2780                                             tag: 'div',
2781                                             cls: 'btn-' + this.buttonPosition
2782                                         }
2783                                     ]
2784
2785                                 }
2786
2787
2788                             ]
2789
2790                         }
2791                     ]
2792
2793                 }
2794             ]
2795         };
2796
2797         if(this.animate){
2798             modal.cls += ' fade';
2799         }
2800
2801         return modal;
2802
2803     },
2804     getChildContainer : function() {
2805
2806          return this.bodyEl;
2807
2808     },
2809     getButtonContainer : function() {
2810          return this.el.select('.modal-footer div',true).first();
2811
2812     },
2813     initEvents : function()
2814     {
2815         if (this.allow_close) {
2816             this.closeEl.on('click', this.hide, this);
2817         }
2818         Roo.EventManager.onWindowResize(this.resize, this, true);
2819
2820
2821     },
2822
2823     resize : function()
2824     {
2825         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2826         
2827         if (this.fitwindow) {
2828             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2829             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2830             this.setSize(w,h);
2831         }
2832         
2833         if(!this.fitwindow && this.max_width !== 0){
2834             
2835             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2836             
2837             // for fix height
2838             if(this.height) {
2839                 this.setSize(w, this.height);
2840                 Roo.log('this height??');
2841                 return;
2842             }
2843             
2844             if(!this.fit_content) {
2845                 Roo.log('not fitting???');
2846                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2847                 return;
2848             }
2849             
2850             var body_childs = this.bodyEl.dom.childNodes;
2851             var full_height = this.headerEl.getHeight() + this.footerEl.getHeight();
2852             for(var i = 0; i < body_childs.length; i++) {
2853                 
2854                 // if(body_childs[i].classList.indexOf('roo-layout-region') * 1 != -1) {
2855                 //     var layout_childs = body_childs[i].childNodes;
2856                 //     for(var j = 0; j < layout_childs.length; j++) {
2857                 // 
2858                 //     }
2859                 // }
2860                 
2861                 full_height += body_childs[i].offsetHeight;
2862             }
2863             
2864             this.setSize(w, Math.min(full_height, Roo.lib.Dom.getViewportHeight(true) - 60));
2865         }
2866         
2867     },
2868
2869     setSize : function(w,h)
2870     {
2871         if (!w && !h) {
2872             return;
2873         }
2874         this.resizeTo(w,h);
2875     },
2876
2877     show : function() {
2878
2879         if (!this.rendered) {
2880             this.render();
2881         }
2882
2883         //this.el.setStyle('display', 'block');
2884         this.el.removeClass('hideing');        
2885         this.el.addClass('show');
2886  
2887         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2888             var _this = this;
2889             (function(){
2890                 this.el.addClass('in');
2891             }).defer(50, this);
2892         }else{
2893             this.el.addClass('in');
2894         }
2895
2896         // not sure how we can show data in here..
2897         //if (this.tmpl) {
2898         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2899         //}
2900
2901         Roo.get(document.body).addClass("x-body-masked");
2902         
2903         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2904         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2905         this.maskEl.addClass('show');
2906         
2907         this.resize();
2908         
2909         this.fireEvent('show', this);
2910
2911         // set zindex here - otherwise it appears to be ignored...
2912         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2913
2914         (function () {
2915             this.items.forEach( function(e) {
2916                 e.layout ? e.layout() : false;
2917
2918             });
2919         }).defer(100,this);
2920
2921     },
2922     hide : function()
2923     {
2924         if(this.fireEvent("beforehide", this) !== false){
2925             this.maskEl.removeClass('show');
2926             Roo.get(document.body).removeClass("x-body-masked");
2927             this.el.removeClass('in');
2928             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2929
2930             if(this.animate){ // why
2931                 this.el.addClass('hideing');
2932                 (function(){
2933                     if (!this.el.hasClass('hideing')) {
2934                         return; // it's been shown again...
2935                     }
2936                     this.el.removeClass('show');
2937                     this.el.removeClass('hideing');
2938                 }).defer(150,this);
2939                 
2940             }else{
2941                  this.el.removeClass('show');
2942             }
2943             this.fireEvent('hide', this);
2944         }
2945     },
2946     isVisible : function()
2947     {
2948         
2949         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2950         
2951     },
2952
2953     addButton : function(str, cb)
2954     {
2955
2956
2957         var b = Roo.apply({}, { html : str } );
2958         b.xns = b.xns || Roo.bootstrap;
2959         b.xtype = b.xtype || 'Button';
2960         if (typeof(b.listeners) == 'undefined') {
2961             b.listeners = { click : cb.createDelegate(this)  };
2962         }
2963
2964         var btn = Roo.factory(b);
2965
2966         btn.render(this.el.select('.modal-footer div').first());
2967
2968         return btn;
2969
2970     },
2971
2972     setDefaultButton : function(btn)
2973     {
2974         //this.el.select('.modal-footer').()
2975     },
2976     diff : false,
2977
2978     resizeTo: function(w,h)
2979     {
2980         // skip.. ?? why??
2981
2982         this.dialogEl.setWidth(w);
2983         if (this.diff === false) {
2984             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2985         }
2986
2987         this.bodyEl.setHeight(h-this.diff);
2988
2989         this.fireEvent('resize', this);
2990
2991     },
2992     setContentSize  : function(w, h)
2993     {
2994
2995     },
2996     onButtonClick: function(btn,e)
2997     {
2998         //Roo.log([a,b,c]);
2999         this.fireEvent('btnclick', btn.name, e);
3000     },
3001      /**
3002      * Set the title of the Dialog
3003      * @param {String} str new Title
3004      */
3005     setTitle: function(str) {
3006         this.titleEl.dom.innerHTML = str;
3007     },
3008     /**
3009      * Set the body of the Dialog
3010      * @param {String} str new Title
3011      */
3012     setBody: function(str) {
3013         this.bodyEl.dom.innerHTML = str;
3014     },
3015     /**
3016      * Set the body of the Dialog using the template
3017      * @param {Obj} data - apply this data to the template and replace the body contents.
3018      */
3019     applyBody: function(obj)
3020     {
3021         if (!this.tmpl) {
3022             Roo.log("Error - using apply Body without a template");
3023             //code
3024         }
3025         this.tmpl.overwrite(this.bodyEl, obj);
3026     }
3027
3028 });
3029
3030
3031 Roo.apply(Roo.bootstrap.Modal,  {
3032     /**
3033          * Button config that displays a single OK button
3034          * @type Object
3035          */
3036         OK :  [{
3037             name : 'ok',
3038             weight : 'primary',
3039             html : 'OK'
3040         }],
3041         /**
3042          * Button config that displays Yes and No buttons
3043          * @type Object
3044          */
3045         YESNO : [
3046             {
3047                 name  : 'no',
3048                 html : 'No'
3049             },
3050             {
3051                 name  :'yes',
3052                 weight : 'primary',
3053                 html : 'Yes'
3054             }
3055         ],
3056
3057         /**
3058          * Button config that displays OK and Cancel buttons
3059          * @type Object
3060          */
3061         OKCANCEL : [
3062             {
3063                name : 'cancel',
3064                 html : 'Cancel'
3065             },
3066             {
3067                 name : 'ok',
3068                 weight : 'primary',
3069                 html : 'OK'
3070             }
3071         ],
3072         /**
3073          * Button config that displays Yes, No and Cancel buttons
3074          * @type Object
3075          */
3076         YESNOCANCEL : [
3077             {
3078                 name : 'yes',
3079                 weight : 'primary',
3080                 html : 'Yes'
3081             },
3082             {
3083                 name : 'no',
3084                 html : 'No'
3085             },
3086             {
3087                 name : 'cancel',
3088                 html : 'Cancel'
3089             }
3090         ],
3091         
3092         zIndex : 10001
3093 });
3094 /*
3095  * - LGPL
3096  *
3097  * messagebox - can be used as a replace
3098  * 
3099  */
3100 /**
3101  * @class Roo.MessageBox
3102  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3103  * Example usage:
3104  *<pre><code>
3105 // Basic alert:
3106 Roo.Msg.alert('Status', 'Changes saved successfully.');
3107
3108 // Prompt for user data:
3109 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3110     if (btn == 'ok'){
3111         // process text value...
3112     }
3113 });
3114
3115 // Show a dialog using config options:
3116 Roo.Msg.show({
3117    title:'Save Changes?',
3118    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3119    buttons: Roo.Msg.YESNOCANCEL,
3120    fn: processResult,
3121    animEl: 'elId'
3122 });
3123 </code></pre>
3124  * @singleton
3125  */
3126 Roo.bootstrap.MessageBox = function(){
3127     var dlg, opt, mask, waitTimer;
3128     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3129     var buttons, activeTextEl, bwidth;
3130
3131     
3132     // private
3133     var handleButton = function(button){
3134         dlg.hide();
3135         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3136     };
3137
3138     // private
3139     var handleHide = function(){
3140         if(opt && opt.cls){
3141             dlg.el.removeClass(opt.cls);
3142         }
3143         //if(waitTimer){
3144         //    Roo.TaskMgr.stop(waitTimer);
3145         //    waitTimer = null;
3146         //}
3147     };
3148
3149     // private
3150     var updateButtons = function(b){
3151         var width = 0;
3152         if(!b){
3153             buttons["ok"].hide();
3154             buttons["cancel"].hide();
3155             buttons["yes"].hide();
3156             buttons["no"].hide();
3157             //dlg.footer.dom.style.display = 'none';
3158             return width;
3159         }
3160         dlg.footerEl.dom.style.display = '';
3161         for(var k in buttons){
3162             if(typeof buttons[k] != "function"){
3163                 if(b[k]){
3164                     buttons[k].show();
3165                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3166                     width += buttons[k].el.getWidth()+15;
3167                 }else{
3168                     buttons[k].hide();
3169                 }
3170             }
3171         }
3172         return width;
3173     };
3174
3175     // private
3176     var handleEsc = function(d, k, e){
3177         if(opt && opt.closable !== false){
3178             dlg.hide();
3179         }
3180         if(e){
3181             e.stopEvent();
3182         }
3183     };
3184
3185     return {
3186         /**
3187          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3188          * @return {Roo.BasicDialog} The BasicDialog element
3189          */
3190         getDialog : function(){
3191            if(!dlg){
3192                 dlg = new Roo.bootstrap.Modal( {
3193                     //draggable: true,
3194                     //resizable:false,
3195                     //constraintoviewport:false,
3196                     //fixedcenter:true,
3197                     //collapsible : false,
3198                     //shim:true,
3199                     //modal: true,
3200                 //    width: 'auto',
3201                   //  height:100,
3202                     //buttonAlign:"center",
3203                     closeClick : function(){
3204                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3205                             handleButton("no");
3206                         }else{
3207                             handleButton("cancel");
3208                         }
3209                     }
3210                 });
3211                 dlg.render();
3212                 dlg.on("hide", handleHide);
3213                 mask = dlg.mask;
3214                 //dlg.addKeyListener(27, handleEsc);
3215                 buttons = {};
3216                 this.buttons = buttons;
3217                 var bt = this.buttonText;
3218                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3219                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3220                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3221                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3222                 //Roo.log(buttons);
3223                 bodyEl = dlg.bodyEl.createChild({
3224
3225                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3226                         '<textarea class="roo-mb-textarea"></textarea>' +
3227                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3228                 });
3229                 msgEl = bodyEl.dom.firstChild;
3230                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3231                 textboxEl.enableDisplayMode();
3232                 textboxEl.addKeyListener([10,13], function(){
3233                     if(dlg.isVisible() && opt && opt.buttons){
3234                         if(opt.buttons.ok){
3235                             handleButton("ok");
3236                         }else if(opt.buttons.yes){
3237                             handleButton("yes");
3238                         }
3239                     }
3240                 });
3241                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3242                 textareaEl.enableDisplayMode();
3243                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3244                 progressEl.enableDisplayMode();
3245                 
3246                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3247                 var pf = progressEl.dom.firstChild;
3248                 if (pf) {
3249                     pp = Roo.get(pf.firstChild);
3250                     pp.setHeight(pf.offsetHeight);
3251                 }
3252                 
3253             }
3254             return dlg;
3255         },
3256
3257         /**
3258          * Updates the message box body text
3259          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3260          * the XHTML-compliant non-breaking space character '&amp;#160;')
3261          * @return {Roo.MessageBox} This message box
3262          */
3263         updateText : function(text)
3264         {
3265             if(!dlg.isVisible() && !opt.width){
3266                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3267                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3268             }
3269             msgEl.innerHTML = text || '&#160;';
3270       
3271             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3272             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3273             var w = Math.max(
3274                     Math.min(opt.width || cw , this.maxWidth), 
3275                     Math.max(opt.minWidth || this.minWidth, bwidth)
3276             );
3277             if(opt.prompt){
3278                 activeTextEl.setWidth(w);
3279             }
3280             if(dlg.isVisible()){
3281                 dlg.fixedcenter = false;
3282             }
3283             // to big, make it scroll. = But as usual stupid IE does not support
3284             // !important..
3285             
3286             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3287                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3288                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3289             } else {
3290                 bodyEl.dom.style.height = '';
3291                 bodyEl.dom.style.overflowY = '';
3292             }
3293             if (cw > w) {
3294                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3295             } else {
3296                 bodyEl.dom.style.overflowX = '';
3297             }
3298             
3299             dlg.setContentSize(w, bodyEl.getHeight());
3300             if(dlg.isVisible()){
3301                 dlg.fixedcenter = true;
3302             }
3303             return this;
3304         },
3305
3306         /**
3307          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3308          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3309          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3310          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3311          * @return {Roo.MessageBox} This message box
3312          */
3313         updateProgress : function(value, text){
3314             if(text){
3315                 this.updateText(text);
3316             }
3317             
3318             if (pp) { // weird bug on my firefox - for some reason this is not defined
3319                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3320                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3321             }
3322             return this;
3323         },        
3324
3325         /**
3326          * Returns true if the message box is currently displayed
3327          * @return {Boolean} True if the message box is visible, else false
3328          */
3329         isVisible : function(){
3330             return dlg && dlg.isVisible();  
3331         },
3332
3333         /**
3334          * Hides the message box if it is displayed
3335          */
3336         hide : function(){
3337             if(this.isVisible()){
3338                 dlg.hide();
3339             }  
3340         },
3341
3342         /**
3343          * Displays a new message box, or reinitializes an existing message box, based on the config options
3344          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3345          * The following config object properties are supported:
3346          * <pre>
3347 Property    Type             Description
3348 ----------  ---------------  ------------------------------------------------------------------------------------
3349 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3350                                    closes (defaults to undefined)
3351 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3352                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3353 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3354                                    progress and wait dialogs will ignore this property and always hide the
3355                                    close button as they can only be closed programmatically.
3356 cls               String           A custom CSS class to apply to the message box element
3357 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3358                                    displayed (defaults to 75)
3359 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3360                                    function will be btn (the name of the button that was clicked, if applicable,
3361                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3362                                    Progress and wait dialogs will ignore this option since they do not respond to
3363                                    user actions and can only be closed programmatically, so any required function
3364                                    should be called by the same code after it closes the dialog.
3365 icon              String           A CSS class that provides a background image to be used as an icon for
3366                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3367 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3368 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3369 modal             Boolean          False to allow user interaction with the page while the message box is
3370                                    displayed (defaults to true)
3371 msg               String           A string that will replace the existing message box body text (defaults
3372                                    to the XHTML-compliant non-breaking space character '&#160;')
3373 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3374 progress          Boolean          True to display a progress bar (defaults to false)
3375 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3376 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3377 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3378 title             String           The title text
3379 value             String           The string value to set into the active textbox element if displayed
3380 wait              Boolean          True to display a progress bar (defaults to false)
3381 width             Number           The width of the dialog in pixels
3382 </pre>
3383          *
3384          * Example usage:
3385          * <pre><code>
3386 Roo.Msg.show({
3387    title: 'Address',
3388    msg: 'Please enter your address:',
3389    width: 300,
3390    buttons: Roo.MessageBox.OKCANCEL,
3391    multiline: true,
3392    fn: saveAddress,
3393    animEl: 'addAddressBtn'
3394 });
3395 </code></pre>
3396          * @param {Object} config Configuration options
3397          * @return {Roo.MessageBox} This message box
3398          */
3399         show : function(options)
3400         {
3401             
3402             // this causes nightmares if you show one dialog after another
3403             // especially on callbacks..
3404              
3405             if(this.isVisible()){
3406                 
3407                 this.hide();
3408                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3409                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3410                 Roo.log("New Dialog Message:" +  options.msg )
3411                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3412                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3413                 
3414             }
3415             var d = this.getDialog();
3416             opt = options;
3417             d.setTitle(opt.title || "&#160;");
3418             d.closeEl.setDisplayed(opt.closable !== false);
3419             activeTextEl = textboxEl;
3420             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3421             if(opt.prompt){
3422                 if(opt.multiline){
3423                     textboxEl.hide();
3424                     textareaEl.show();
3425                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3426                         opt.multiline : this.defaultTextHeight);
3427                     activeTextEl = textareaEl;
3428                 }else{
3429                     textboxEl.show();
3430                     textareaEl.hide();
3431                 }
3432             }else{
3433                 textboxEl.hide();
3434                 textareaEl.hide();
3435             }
3436             progressEl.setDisplayed(opt.progress === true);
3437             this.updateProgress(0);
3438             activeTextEl.dom.value = opt.value || "";
3439             if(opt.prompt){
3440                 dlg.setDefaultButton(activeTextEl);
3441             }else{
3442                 var bs = opt.buttons;
3443                 var db = null;
3444                 if(bs && bs.ok){
3445                     db = buttons["ok"];
3446                 }else if(bs && bs.yes){
3447                     db = buttons["yes"];
3448                 }
3449                 dlg.setDefaultButton(db);
3450             }
3451             bwidth = updateButtons(opt.buttons);
3452             this.updateText(opt.msg);
3453             if(opt.cls){
3454                 d.el.addClass(opt.cls);
3455             }
3456             d.proxyDrag = opt.proxyDrag === true;
3457             d.modal = opt.modal !== false;
3458             d.mask = opt.modal !== false ? mask : false;
3459             if(!d.isVisible()){
3460                 // force it to the end of the z-index stack so it gets a cursor in FF
3461                 document.body.appendChild(dlg.el.dom);
3462                 d.animateTarget = null;
3463                 d.show(options.animEl);
3464             }
3465             return this;
3466         },
3467
3468         /**
3469          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3470          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3471          * and closing the message box when the process is complete.
3472          * @param {String} title The title bar text
3473          * @param {String} msg The message box body text
3474          * @return {Roo.MessageBox} This message box
3475          */
3476         progress : function(title, msg){
3477             this.show({
3478                 title : title,
3479                 msg : msg,
3480                 buttons: false,
3481                 progress:true,
3482                 closable:false,
3483                 minWidth: this.minProgressWidth,
3484                 modal : true
3485             });
3486             return this;
3487         },
3488
3489         /**
3490          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3491          * If a callback function is passed it will be called after the user clicks the button, and the
3492          * id of the button that was clicked will be passed as the only parameter to the callback
3493          * (could also be the top-right close button).
3494          * @param {String} title The title bar text
3495          * @param {String} msg The message box body text
3496          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3497          * @param {Object} scope (optional) The scope of the callback function
3498          * @return {Roo.MessageBox} This message box
3499          */
3500         alert : function(title, msg, fn, scope)
3501         {
3502             this.show({
3503                 title : title,
3504                 msg : msg,
3505                 buttons: this.OK,
3506                 fn: fn,
3507                 closable : false,
3508                 scope : scope,
3509                 modal : true
3510             });
3511             return this;
3512         },
3513
3514         /**
3515          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3516          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3517          * You are responsible for closing the message box when the process is complete.
3518          * @param {String} msg The message box body text
3519          * @param {String} title (optional) The title bar text
3520          * @return {Roo.MessageBox} This message box
3521          */
3522         wait : function(msg, title){
3523             this.show({
3524                 title : title,
3525                 msg : msg,
3526                 buttons: false,
3527                 closable:false,
3528                 progress:true,
3529                 modal:true,
3530                 width:300,
3531                 wait:true
3532             });
3533             waitTimer = Roo.TaskMgr.start({
3534                 run: function(i){
3535                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3536                 },
3537                 interval: 1000
3538             });
3539             return this;
3540         },
3541
3542         /**
3543          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3544          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3545          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3546          * @param {String} title The title bar text
3547          * @param {String} msg The message box body text
3548          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3549          * @param {Object} scope (optional) The scope of the callback function
3550          * @return {Roo.MessageBox} This message box
3551          */
3552         confirm : function(title, msg, fn, scope){
3553             this.show({
3554                 title : title,
3555                 msg : msg,
3556                 buttons: this.YESNO,
3557                 fn: fn,
3558                 scope : scope,
3559                 modal : true
3560             });
3561             return this;
3562         },
3563
3564         /**
3565          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3566          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3567          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3568          * (could also be the top-right close button) and the text that was entered will be passed as the two
3569          * parameters to the callback.
3570          * @param {String} title The title bar text
3571          * @param {String} msg The message box body text
3572          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3573          * @param {Object} scope (optional) The scope of the callback function
3574          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3575          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3576          * @return {Roo.MessageBox} This message box
3577          */
3578         prompt : function(title, msg, fn, scope, multiline){
3579             this.show({
3580                 title : title,
3581                 msg : msg,
3582                 buttons: this.OKCANCEL,
3583                 fn: fn,
3584                 minWidth:250,
3585                 scope : scope,
3586                 prompt:true,
3587                 multiline: multiline,
3588                 modal : true
3589             });
3590             return this;
3591         },
3592
3593         /**
3594          * Button config that displays a single OK button
3595          * @type Object
3596          */
3597         OK : {ok:true},
3598         /**
3599          * Button config that displays Yes and No buttons
3600          * @type Object
3601          */
3602         YESNO : {yes:true, no:true},
3603         /**
3604          * Button config that displays OK and Cancel buttons
3605          * @type Object
3606          */
3607         OKCANCEL : {ok:true, cancel:true},
3608         /**
3609          * Button config that displays Yes, No and Cancel buttons
3610          * @type Object
3611          */
3612         YESNOCANCEL : {yes:true, no:true, cancel:true},
3613
3614         /**
3615          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3616          * @type Number
3617          */
3618         defaultTextHeight : 75,
3619         /**
3620          * The maximum width in pixels of the message box (defaults to 600)
3621          * @type Number
3622          */
3623         maxWidth : 600,
3624         /**
3625          * The minimum width in pixels of the message box (defaults to 100)
3626          * @type Number
3627          */
3628         minWidth : 100,
3629         /**
3630          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3631          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3632          * @type Number
3633          */
3634         minProgressWidth : 250,
3635         /**
3636          * An object containing the default button text strings that can be overriden for localized language support.
3637          * Supported properties are: ok, cancel, yes and no.
3638          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3639          * @type Object
3640          */
3641         buttonText : {
3642             ok : "OK",
3643             cancel : "Cancel",
3644             yes : "Yes",
3645             no : "No"
3646         }
3647     };
3648 }();
3649
3650 /**
3651  * Shorthand for {@link Roo.MessageBox}
3652  */
3653 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3654 Roo.Msg = Roo.Msg || Roo.MessageBox;
3655 /*
3656  * - LGPL
3657  *
3658  * navbar
3659  * 
3660  */
3661
3662 /**
3663  * @class Roo.bootstrap.Navbar
3664  * @extends Roo.bootstrap.Component
3665  * Bootstrap Navbar class
3666
3667  * @constructor
3668  * Create a new Navbar
3669  * @param {Object} config The config object
3670  */
3671
3672
3673 Roo.bootstrap.Navbar = function(config){
3674     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3675     this.addEvents({
3676         // raw events
3677         /**
3678          * @event beforetoggle
3679          * Fire before toggle the menu
3680          * @param {Roo.EventObject} e
3681          */
3682         "beforetoggle" : true
3683     });
3684 };
3685
3686 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3687     
3688     
3689    
3690     // private
3691     navItems : false,
3692     loadMask : false,
3693     
3694     
3695     getAutoCreate : function(){
3696         
3697         
3698         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3699         
3700     },
3701     
3702     initEvents :function ()
3703     {
3704         //Roo.log(this.el.select('.navbar-toggle',true));
3705         this.el.select('.navbar-toggle',true).on('click', function() {
3706             if(this.fireEvent('beforetoggle', this) !== false){
3707                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3708             }
3709             
3710         }, this);
3711         
3712         var mark = {
3713             tag: "div",
3714             cls:"x-dlg-mask"
3715         };
3716         
3717         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3718         
3719         var size = this.el.getSize();
3720         this.maskEl.setSize(size.width, size.height);
3721         this.maskEl.enableDisplayMode("block");
3722         this.maskEl.hide();
3723         
3724         if(this.loadMask){
3725             this.maskEl.show();
3726         }
3727     },
3728     
3729     
3730     getChildContainer : function()
3731     {
3732         if (this.el.select('.collapse').getCount()) {
3733             return this.el.select('.collapse',true).first();
3734         }
3735         
3736         return this.el;
3737     },
3738     
3739     mask : function()
3740     {
3741         this.maskEl.show();
3742     },
3743     
3744     unmask : function()
3745     {
3746         this.maskEl.hide();
3747     } 
3748     
3749     
3750     
3751     
3752 });
3753
3754
3755
3756  
3757
3758  /*
3759  * - LGPL
3760  *
3761  * navbar
3762  * 
3763  */
3764
3765 /**
3766  * @class Roo.bootstrap.NavSimplebar
3767  * @extends Roo.bootstrap.Navbar
3768  * Bootstrap Sidebar class
3769  *
3770  * @cfg {Boolean} inverse is inverted color
3771  * 
3772  * @cfg {String} type (nav | pills | tabs)
3773  * @cfg {Boolean} arrangement stacked | justified
3774  * @cfg {String} align (left | right) alignment
3775  * 
3776  * @cfg {Boolean} main (true|false) main nav bar? default false
3777  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3778  * 
3779  * @cfg {String} tag (header|footer|nav|div) default is nav 
3780
3781  * 
3782  * 
3783  * 
3784  * @constructor
3785  * Create a new Sidebar
3786  * @param {Object} config The config object
3787  */
3788
3789
3790 Roo.bootstrap.NavSimplebar = function(config){
3791     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3792 };
3793
3794 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3795     
3796     inverse: false,
3797     
3798     type: false,
3799     arrangement: '',
3800     align : false,
3801     
3802     
3803     
3804     main : false,
3805     
3806     
3807     tag : false,
3808     
3809     
3810     getAutoCreate : function(){
3811         
3812         
3813         var cfg = {
3814             tag : this.tag || 'div',
3815             cls : 'navbar'
3816         };
3817           
3818         
3819         cfg.cn = [
3820             {
3821                 cls: 'nav',
3822                 tag : 'ul'
3823             }
3824         ];
3825         
3826          
3827         this.type = this.type || 'nav';
3828         if (['tabs','pills'].indexOf(this.type)!==-1) {
3829             cfg.cn[0].cls += ' nav-' + this.type
3830         
3831         
3832         } else {
3833             if (this.type!=='nav') {
3834                 Roo.log('nav type must be nav/tabs/pills')
3835             }
3836             cfg.cn[0].cls += ' navbar-nav'
3837         }
3838         
3839         
3840         
3841         
3842         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3843             cfg.cn[0].cls += ' nav-' + this.arrangement;
3844         }
3845         
3846         
3847         if (this.align === 'right') {
3848             cfg.cn[0].cls += ' navbar-right';
3849         }
3850         
3851         if (this.inverse) {
3852             cfg.cls += ' navbar-inverse';
3853             
3854         }
3855         
3856         
3857         return cfg;
3858     
3859         
3860     }
3861     
3862     
3863     
3864 });
3865
3866
3867
3868  
3869
3870  
3871        /*
3872  * - LGPL
3873  *
3874  * navbar
3875  * 
3876  */
3877
3878 /**
3879  * @class Roo.bootstrap.NavHeaderbar
3880  * @extends Roo.bootstrap.NavSimplebar
3881  * Bootstrap Sidebar class
3882  *
3883  * @cfg {String} brand what is brand
3884  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3885  * @cfg {String} brand_href href of the brand
3886  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3887  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3888  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3889  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3890  * 
3891  * @constructor
3892  * Create a new Sidebar
3893  * @param {Object} config The config object
3894  */
3895
3896
3897 Roo.bootstrap.NavHeaderbar = function(config){
3898     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3899       
3900 };
3901
3902 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3903     
3904     position: '',
3905     brand: '',
3906     brand_href: false,
3907     srButton : true,
3908     autohide : false,
3909     desktopCenter : false,
3910    
3911     
3912     getAutoCreate : function(){
3913         
3914         var   cfg = {
3915             tag: this.nav || 'nav',
3916             cls: 'navbar',
3917             role: 'navigation',
3918             cn: []
3919         };
3920         
3921         var cn = cfg.cn;
3922         if (this.desktopCenter) {
3923             cn.push({cls : 'container', cn : []});
3924             cn = cn[0].cn;
3925         }
3926         
3927         if(this.srButton){
3928             cn.push({
3929                 tag: 'div',
3930                 cls: 'navbar-header',
3931                 cn: [
3932                     {
3933                         tag: 'button',
3934                         type: 'button',
3935                         cls: 'navbar-toggle',
3936                         'data-toggle': 'collapse',
3937                         cn: [
3938                             {
3939                                 tag: 'span',
3940                                 cls: 'sr-only',
3941                                 html: 'Toggle navigation'
3942                             },
3943                             {
3944                                 tag: 'span',
3945                                 cls: 'icon-bar'
3946                             },
3947                             {
3948                                 tag: 'span',
3949                                 cls: 'icon-bar'
3950                             },
3951                             {
3952                                 tag: 'span',
3953                                 cls: 'icon-bar'
3954                             }
3955                         ]
3956                     }
3957                 ]
3958             });
3959         }
3960         
3961         cn.push({
3962             tag: 'div',
3963             cls: 'collapse navbar-collapse',
3964             cn : []
3965         });
3966         
3967         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3968         
3969         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3970             cfg.cls += ' navbar-' + this.position;
3971             
3972             // tag can override this..
3973             
3974             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3975         }
3976         
3977         if (this.brand !== '') {
3978             cn[0].cn.push({
3979                 tag: 'a',
3980                 href: this.brand_href ? this.brand_href : '#',
3981                 cls: 'navbar-brand',
3982                 cn: [
3983                 this.brand
3984                 ]
3985             });
3986         }
3987         
3988         if(this.main){
3989             cfg.cls += ' main-nav';
3990         }
3991         
3992         
3993         return cfg;
3994
3995         
3996     },
3997     getHeaderChildContainer : function()
3998     {
3999         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4000             return this.el.select('.navbar-header',true).first();
4001         }
4002         
4003         return this.getChildContainer();
4004     },
4005     
4006     
4007     initEvents : function()
4008     {
4009         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4010         
4011         if (this.autohide) {
4012             
4013             var prevScroll = 0;
4014             var ft = this.el;
4015             
4016             Roo.get(document).on('scroll',function(e) {
4017                 var ns = Roo.get(document).getScroll().top;
4018                 var os = prevScroll;
4019                 prevScroll = ns;
4020                 
4021                 if(ns > os){
4022                     ft.removeClass('slideDown');
4023                     ft.addClass('slideUp');
4024                     return;
4025                 }
4026                 ft.removeClass('slideUp');
4027                 ft.addClass('slideDown');
4028                  
4029               
4030           },this);
4031         }
4032     }    
4033     
4034 });
4035
4036
4037
4038  
4039
4040  /*
4041  * - LGPL
4042  *
4043  * navbar
4044  * 
4045  */
4046
4047 /**
4048  * @class Roo.bootstrap.NavSidebar
4049  * @extends Roo.bootstrap.Navbar
4050  * Bootstrap Sidebar class
4051  * 
4052  * @constructor
4053  * Create a new Sidebar
4054  * @param {Object} config The config object
4055  */
4056
4057
4058 Roo.bootstrap.NavSidebar = function(config){
4059     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4060 };
4061
4062 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4063     
4064     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4065     
4066     getAutoCreate : function(){
4067         
4068         
4069         return  {
4070             tag: 'div',
4071             cls: 'sidebar sidebar-nav'
4072         };
4073     
4074         
4075     }
4076     
4077     
4078     
4079 });
4080
4081
4082
4083  
4084
4085  /*
4086  * - LGPL
4087  *
4088  * nav group
4089  * 
4090  */
4091
4092 /**
4093  * @class Roo.bootstrap.NavGroup
4094  * @extends Roo.bootstrap.Component
4095  * Bootstrap NavGroup class
4096  * @cfg {String} align (left|right)
4097  * @cfg {Boolean} inverse
4098  * @cfg {String} type (nav|pills|tab) default nav
4099  * @cfg {String} navId - reference Id for navbar.
4100
4101  * 
4102  * @constructor
4103  * Create a new nav group
4104  * @param {Object} config The config object
4105  */
4106
4107 Roo.bootstrap.NavGroup = function(config){
4108     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4109     this.navItems = [];
4110    
4111     Roo.bootstrap.NavGroup.register(this);
4112      this.addEvents({
4113         /**
4114              * @event changed
4115              * Fires when the active item changes
4116              * @param {Roo.bootstrap.NavGroup} this
4117              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4118              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4119          */
4120         'changed': true
4121      });
4122     
4123 };
4124
4125 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4126     
4127     align: '',
4128     inverse: false,
4129     form: false,
4130     type: 'nav',
4131     navId : '',
4132     // private
4133     
4134     navItems : false, 
4135     
4136     getAutoCreate : function()
4137     {
4138         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4139         
4140         cfg = {
4141             tag : 'ul',
4142             cls: 'nav' 
4143         };
4144         
4145         if (['tabs','pills'].indexOf(this.type)!==-1) {
4146             cfg.cls += ' nav-' + this.type
4147         } else {
4148             if (this.type!=='nav') {
4149                 Roo.log('nav type must be nav/tabs/pills')
4150             }
4151             cfg.cls += ' navbar-nav'
4152         }
4153         
4154         if (this.parent() && this.parent().sidebar) {
4155             cfg = {
4156                 tag: 'ul',
4157                 cls: 'dashboard-menu sidebar-menu'
4158             };
4159             
4160             return cfg;
4161         }
4162         
4163         if (this.form === true) {
4164             cfg = {
4165                 tag: 'form',
4166                 cls: 'navbar-form'
4167             };
4168             
4169             if (this.align === 'right') {
4170                 cfg.cls += ' navbar-right';
4171             } else {
4172                 cfg.cls += ' navbar-left';
4173             }
4174         }
4175         
4176         if (this.align === 'right') {
4177             cfg.cls += ' navbar-right';
4178         }
4179         
4180         if (this.inverse) {
4181             cfg.cls += ' navbar-inverse';
4182             
4183         }
4184         
4185         
4186         return cfg;
4187     },
4188     /**
4189     * sets the active Navigation item
4190     * @param {Roo.bootstrap.NavItem} the new current navitem
4191     */
4192     setActiveItem : function(item)
4193     {
4194         var prev = false;
4195         Roo.each(this.navItems, function(v){
4196             if (v == item) {
4197                 return ;
4198             }
4199             if (v.isActive()) {
4200                 v.setActive(false, true);
4201                 prev = v;
4202                 
4203             }
4204             
4205         });
4206
4207         item.setActive(true, true);
4208         this.fireEvent('changed', this, item, prev);
4209         
4210         
4211     },
4212     /**
4213     * gets the active Navigation item
4214     * @return {Roo.bootstrap.NavItem} the current navitem
4215     */
4216     getActive : function()
4217     {
4218         
4219         var prev = false;
4220         Roo.each(this.navItems, function(v){
4221             
4222             if (v.isActive()) {
4223                 prev = v;
4224                 
4225             }
4226             
4227         });
4228         return prev;
4229     },
4230     
4231     indexOfNav : function()
4232     {
4233         
4234         var prev = false;
4235         Roo.each(this.navItems, function(v,i){
4236             
4237             if (v.isActive()) {
4238                 prev = i;
4239                 
4240             }
4241             
4242         });
4243         return prev;
4244     },
4245     /**
4246     * adds a Navigation item
4247     * @param {Roo.bootstrap.NavItem} the navitem to add
4248     */
4249     addItem : function(cfg)
4250     {
4251         var cn = new Roo.bootstrap.NavItem(cfg);
4252         this.register(cn);
4253         cn.parentId = this.id;
4254         cn.onRender(this.el, null);
4255         return cn;
4256     },
4257     /**
4258     * register a Navigation item
4259     * @param {Roo.bootstrap.NavItem} the navitem to add
4260     */
4261     register : function(item)
4262     {
4263         this.navItems.push( item);
4264         item.navId = this.navId;
4265     
4266     },
4267     
4268     /**
4269     * clear all the Navigation item
4270     */
4271    
4272     clearAll : function()
4273     {
4274         this.navItems = [];
4275         this.el.dom.innerHTML = '';
4276     },
4277     
4278     getNavItem: function(tabId)
4279     {
4280         var ret = false;
4281         Roo.each(this.navItems, function(e) {
4282             if (e.tabId == tabId) {
4283                ret =  e;
4284                return false;
4285             }
4286             return true;
4287             
4288         });
4289         return ret;
4290     },
4291     
4292     setActiveNext : function()
4293     {
4294         var i = this.indexOfNav(this.getActive());
4295         if (i > this.navItems.length) {
4296             return;
4297         }
4298         this.setActiveItem(this.navItems[i+1]);
4299     },
4300     setActivePrev : function()
4301     {
4302         var i = this.indexOfNav(this.getActive());
4303         if (i  < 1) {
4304             return;
4305         }
4306         this.setActiveItem(this.navItems[i-1]);
4307     },
4308     clearWasActive : function(except) {
4309         Roo.each(this.navItems, function(e) {
4310             if (e.tabId != except.tabId && e.was_active) {
4311                e.was_active = false;
4312                return false;
4313             }
4314             return true;
4315             
4316         });
4317     },
4318     getWasActive : function ()
4319     {
4320         var r = false;
4321         Roo.each(this.navItems, function(e) {
4322             if (e.was_active) {
4323                r = e;
4324                return false;
4325             }
4326             return true;
4327             
4328         });
4329         return r;
4330     }
4331     
4332     
4333 });
4334
4335  
4336 Roo.apply(Roo.bootstrap.NavGroup, {
4337     
4338     groups: {},
4339      /**
4340     * register a Navigation Group
4341     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4342     */
4343     register : function(navgrp)
4344     {
4345         this.groups[navgrp.navId] = navgrp;
4346         
4347     },
4348     /**
4349     * fetch a Navigation Group based on the navigation ID
4350     * @param {string} the navgroup to add
4351     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4352     */
4353     get: function(navId) {
4354         if (typeof(this.groups[navId]) == 'undefined') {
4355             return false;
4356             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4357         }
4358         return this.groups[navId] ;
4359     }
4360     
4361     
4362     
4363 });
4364
4365  /*
4366  * - LGPL
4367  *
4368  * row
4369  * 
4370  */
4371
4372 /**
4373  * @class Roo.bootstrap.NavItem
4374  * @extends Roo.bootstrap.Component
4375  * Bootstrap Navbar.NavItem class
4376  * @cfg {String} href  link to
4377  * @cfg {String} html content of button
4378  * @cfg {String} badge text inside badge
4379  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4380  * @cfg {String} glyphicon name of glyphicon
4381  * @cfg {String} icon name of font awesome icon
4382  * @cfg {Boolean} active Is item active
4383  * @cfg {Boolean} disabled Is item disabled
4384  
4385  * @cfg {Boolean} preventDefault (true | false) default false
4386  * @cfg {String} tabId the tab that this item activates.
4387  * @cfg {String} tagtype (a|span) render as a href or span?
4388  * @cfg {Boolean} animateRef (true|false) link to element default false  
4389   
4390  * @constructor
4391  * Create a new Navbar Item
4392  * @param {Object} config The config object
4393  */
4394 Roo.bootstrap.NavItem = function(config){
4395     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4396     this.addEvents({
4397         // raw events
4398         /**
4399          * @event click
4400          * The raw click event for the entire grid.
4401          * @param {Roo.EventObject} e
4402          */
4403         "click" : true,
4404          /**
4405             * @event changed
4406             * Fires when the active item active state changes
4407             * @param {Roo.bootstrap.NavItem} this
4408             * @param {boolean} state the new state
4409              
4410          */
4411         'changed': true,
4412         /**
4413             * @event scrollto
4414             * Fires when scroll to element
4415             * @param {Roo.bootstrap.NavItem} this
4416             * @param {Object} options
4417             * @param {Roo.EventObject} e
4418              
4419          */
4420         'scrollto': true
4421     });
4422    
4423 };
4424
4425 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4426     
4427     href: false,
4428     html: '',
4429     badge: '',
4430     icon: false,
4431     glyphicon: false,
4432     active: false,
4433     preventDefault : false,
4434     tabId : false,
4435     tagtype : 'a',
4436     disabled : false,
4437     animateRef : false,
4438     was_active : false,
4439     
4440     getAutoCreate : function(){
4441          
4442         var cfg = {
4443             tag: 'li',
4444             cls: 'nav-item'
4445             
4446         };
4447         
4448         if (this.active) {
4449             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4450         }
4451         if (this.disabled) {
4452             cfg.cls += ' disabled';
4453         }
4454         
4455         if (this.href || this.html || this.glyphicon || this.icon) {
4456             cfg.cn = [
4457                 {
4458                     tag: this.tagtype,
4459                     href : this.href || "#",
4460                     html: this.html || ''
4461                 }
4462             ];
4463             
4464             if (this.icon) {
4465                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4466             }
4467
4468             if(this.glyphicon) {
4469                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4470             }
4471             
4472             if (this.menu) {
4473                 
4474                 cfg.cn[0].html += " <span class='caret'></span>";
4475              
4476             }
4477             
4478             if (this.badge !== '') {
4479                  
4480                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4481             }
4482         }
4483         
4484         
4485         
4486         return cfg;
4487     },
4488     initEvents: function() 
4489     {
4490         if (typeof (this.menu) != 'undefined') {
4491             this.menu.parentType = this.xtype;
4492             this.menu.triggerEl = this.el;
4493             this.menu = this.addxtype(Roo.apply({}, this.menu));
4494         }
4495         
4496         this.el.select('a',true).on('click', this.onClick, this);
4497         
4498         if(this.tagtype == 'span'){
4499             this.el.select('span',true).on('click', this.onClick, this);
4500         }
4501        
4502         // at this point parent should be available..
4503         this.parent().register(this);
4504     },
4505     
4506     onClick : function(e)
4507     {
4508         if (e.getTarget('.dropdown-menu-item')) {
4509             // did you click on a menu itemm.... - then don't trigger onclick..
4510             return;
4511         }
4512         
4513         if(
4514                 this.preventDefault || 
4515                 this.href == '#' 
4516         ){
4517             Roo.log("NavItem - prevent Default?");
4518             e.preventDefault();
4519         }
4520         
4521         if (this.disabled) {
4522             return;
4523         }
4524         
4525         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4526         if (tg && tg.transition) {
4527             Roo.log("waiting for the transitionend");
4528             return;
4529         }
4530         
4531         
4532         
4533         //Roo.log("fire event clicked");
4534         if(this.fireEvent('click', this, e) === false){
4535             return;
4536         };
4537         
4538         if(this.tagtype == 'span'){
4539             return;
4540         }
4541         
4542         //Roo.log(this.href);
4543         var ael = this.el.select('a',true).first();
4544         //Roo.log(ael);
4545         
4546         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4547             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4548             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4549                 return; // ignore... - it's a 'hash' to another page.
4550             }
4551             Roo.log("NavItem - prevent Default?");
4552             e.preventDefault();
4553             this.scrollToElement(e);
4554         }
4555         
4556         
4557         var p =  this.parent();
4558    
4559         if (['tabs','pills'].indexOf(p.type)!==-1) {
4560             if (typeof(p.setActiveItem) !== 'undefined') {
4561                 p.setActiveItem(this);
4562             }
4563         }
4564         
4565         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4566         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4567             // remove the collapsed menu expand...
4568             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4569         }
4570     },
4571     
4572     isActive: function () {
4573         return this.active
4574     },
4575     setActive : function(state, fire, is_was_active)
4576     {
4577         if (this.active && !state && this.navId) {
4578             this.was_active = true;
4579             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4580             if (nv) {
4581                 nv.clearWasActive(this);
4582             }
4583             
4584         }
4585         this.active = state;
4586         
4587         if (!state ) {
4588             this.el.removeClass('active');
4589         } else if (!this.el.hasClass('active')) {
4590             this.el.addClass('active');
4591         }
4592         if (fire) {
4593             this.fireEvent('changed', this, state);
4594         }
4595         
4596         // show a panel if it's registered and related..
4597         
4598         if (!this.navId || !this.tabId || !state || is_was_active) {
4599             return;
4600         }
4601         
4602         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4603         if (!tg) {
4604             return;
4605         }
4606         var pan = tg.getPanelByName(this.tabId);
4607         if (!pan) {
4608             return;
4609         }
4610         // if we can not flip to new panel - go back to old nav highlight..
4611         if (false == tg.showPanel(pan)) {
4612             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4613             if (nv) {
4614                 var onav = nv.getWasActive();
4615                 if (onav) {
4616                     onav.setActive(true, false, true);
4617                 }
4618             }
4619             
4620         }
4621         
4622         
4623         
4624     },
4625      // this should not be here...
4626     setDisabled : function(state)
4627     {
4628         this.disabled = state;
4629         if (!state ) {
4630             this.el.removeClass('disabled');
4631         } else if (!this.el.hasClass('disabled')) {
4632             this.el.addClass('disabled');
4633         }
4634         
4635     },
4636     
4637     /**
4638      * Fetch the element to display the tooltip on.
4639      * @return {Roo.Element} defaults to this.el
4640      */
4641     tooltipEl : function()
4642     {
4643         return this.el.select('' + this.tagtype + '', true).first();
4644     },
4645     
4646     scrollToElement : function(e)
4647     {
4648         var c = document.body;
4649         
4650         /*
4651          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4652          */
4653         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4654             c = document.documentElement;
4655         }
4656         
4657         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4658         
4659         if(!target){
4660             return;
4661         }
4662
4663         var o = target.calcOffsetsTo(c);
4664         
4665         var options = {
4666             target : target,
4667             value : o[1]
4668         };
4669         
4670         this.fireEvent('scrollto', this, options, e);
4671         
4672         Roo.get(c).scrollTo('top', options.value, true);
4673         
4674         return;
4675     }
4676 });
4677  
4678
4679  /*
4680  * - LGPL
4681  *
4682  * sidebar item
4683  *
4684  *  li
4685  *    <span> icon </span>
4686  *    <span> text </span>
4687  *    <span>badge </span>
4688  */
4689
4690 /**
4691  * @class Roo.bootstrap.NavSidebarItem
4692  * @extends Roo.bootstrap.NavItem
4693  * Bootstrap Navbar.NavSidebarItem class
4694  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4695  * {Boolean} open is the menu open
4696  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4697  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4698  * {String} buttonSize (sm|md|lg)the extra classes for the button
4699  * {Boolean} showArrow show arrow next to the text (default true)
4700  * @constructor
4701  * Create a new Navbar Button
4702  * @param {Object} config The config object
4703  */
4704 Roo.bootstrap.NavSidebarItem = function(config){
4705     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4706     this.addEvents({
4707         // raw events
4708         /**
4709          * @event click
4710          * The raw click event for the entire grid.
4711          * @param {Roo.EventObject} e
4712          */
4713         "click" : true,
4714          /**
4715             * @event changed
4716             * Fires when the active item active state changes
4717             * @param {Roo.bootstrap.NavSidebarItem} this
4718             * @param {boolean} state the new state
4719              
4720          */
4721         'changed': true
4722     });
4723    
4724 };
4725
4726 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4727     
4728     badgeWeight : 'default',
4729     
4730     open: false,
4731     
4732     buttonView : false,
4733     
4734     buttonWeight : 'default',
4735     
4736     buttonSize : 'md',
4737     
4738     showArrow : true,
4739     
4740     getAutoCreate : function(){
4741         
4742         
4743         var a = {
4744                 tag: 'a',
4745                 href : this.href || '#',
4746                 cls: '',
4747                 html : '',
4748                 cn : []
4749         };
4750         
4751         if(this.buttonView){
4752             a = {
4753                 tag: 'button',
4754                 href : this.href || '#',
4755                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4756                 html : this.html,
4757                 cn : []
4758             };
4759         }
4760         
4761         var cfg = {
4762             tag: 'li',
4763             cls: '',
4764             cn: [ a ]
4765         };
4766         
4767         if (this.active) {
4768             cfg.cls += ' active';
4769         }
4770         
4771         if (this.disabled) {
4772             cfg.cls += ' disabled';
4773         }
4774         if (this.open) {
4775             cfg.cls += ' open x-open';
4776         }
4777         // left icon..
4778         if (this.glyphicon || this.icon) {
4779             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4780             a.cn.push({ tag : 'i', cls : c }) ;
4781         }
4782         
4783         if(!this.buttonView){
4784             var span = {
4785                 tag: 'span',
4786                 html : this.html || ''
4787             };
4788
4789             a.cn.push(span);
4790             
4791         }
4792         
4793         if (this.badge !== '') {
4794             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4795         }
4796         
4797         if (this.menu) {
4798             
4799             if(this.showArrow){
4800                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4801             }
4802             
4803             a.cls += ' dropdown-toggle treeview' ;
4804         }
4805         
4806         return cfg;
4807     },
4808     
4809     initEvents : function()
4810     { 
4811         if (typeof (this.menu) != 'undefined') {
4812             this.menu.parentType = this.xtype;
4813             this.menu.triggerEl = this.el;
4814             this.menu = this.addxtype(Roo.apply({}, this.menu));
4815         }
4816         
4817         this.el.on('click', this.onClick, this);
4818         
4819         if(this.badge !== ''){
4820             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4821         }
4822         
4823     },
4824     
4825     onClick : function(e)
4826     {
4827         if(this.disabled){
4828             e.preventDefault();
4829             return;
4830         }
4831         
4832         if(this.preventDefault){
4833             e.preventDefault();
4834         }
4835         
4836         this.fireEvent('click', this);
4837     },
4838     
4839     disable : function()
4840     {
4841         this.setDisabled(true);
4842     },
4843     
4844     enable : function()
4845     {
4846         this.setDisabled(false);
4847     },
4848     
4849     setDisabled : function(state)
4850     {
4851         if(this.disabled == state){
4852             return;
4853         }
4854         
4855         this.disabled = state;
4856         
4857         if (state) {
4858             this.el.addClass('disabled');
4859             return;
4860         }
4861         
4862         this.el.removeClass('disabled');
4863         
4864         return;
4865     },
4866     
4867     setActive : function(state)
4868     {
4869         if(this.active == state){
4870             return;
4871         }
4872         
4873         this.active = state;
4874         
4875         if (state) {
4876             this.el.addClass('active');
4877             return;
4878         }
4879         
4880         this.el.removeClass('active');
4881         
4882         return;
4883     },
4884     
4885     isActive: function () 
4886     {
4887         return this.active;
4888     },
4889     
4890     setBadge : function(str)
4891     {
4892         if(!this.badgeEl){
4893             return;
4894         }
4895         
4896         this.badgeEl.dom.innerHTML = str;
4897     }
4898     
4899    
4900      
4901  
4902 });
4903  
4904
4905  /*
4906  * - LGPL
4907  *
4908  * row
4909  * 
4910  */
4911
4912 /**
4913  * @class Roo.bootstrap.Row
4914  * @extends Roo.bootstrap.Component
4915  * Bootstrap Row class (contains columns...)
4916  * 
4917  * @constructor
4918  * Create a new Row
4919  * @param {Object} config The config object
4920  */
4921
4922 Roo.bootstrap.Row = function(config){
4923     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4924 };
4925
4926 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4927     
4928     getAutoCreate : function(){
4929        return {
4930             cls: 'row clearfix'
4931        };
4932     }
4933     
4934     
4935 });
4936
4937  
4938
4939  /*
4940  * - LGPL
4941  *
4942  * element
4943  * 
4944  */
4945
4946 /**
4947  * @class Roo.bootstrap.Element
4948  * @extends Roo.bootstrap.Component
4949  * Bootstrap Element class
4950  * @cfg {String} html contents of the element
4951  * @cfg {String} tag tag of the element
4952  * @cfg {String} cls class of the element
4953  * @cfg {Boolean} preventDefault (true|false) default false
4954  * @cfg {Boolean} clickable (true|false) default false
4955  * 
4956  * @constructor
4957  * Create a new Element
4958  * @param {Object} config The config object
4959  */
4960
4961 Roo.bootstrap.Element = function(config){
4962     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4963     
4964     this.addEvents({
4965         // raw events
4966         /**
4967          * @event click
4968          * When a element is chick
4969          * @param {Roo.bootstrap.Element} this
4970          * @param {Roo.EventObject} e
4971          */
4972         "click" : true
4973     });
4974 };
4975
4976 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4977     
4978     tag: 'div',
4979     cls: '',
4980     html: '',
4981     preventDefault: false, 
4982     clickable: false,
4983     
4984     getAutoCreate : function(){
4985         
4986         var cfg = {
4987             tag: this.tag,
4988             // cls: this.cls, double assign in parent class Component.js :: onRender
4989             html: this.html
4990         };
4991         
4992         return cfg;
4993     },
4994     
4995     initEvents: function() 
4996     {
4997         Roo.bootstrap.Element.superclass.initEvents.call(this);
4998         
4999         if(this.clickable){
5000             this.el.on('click', this.onClick, this);
5001         }
5002         
5003     },
5004     
5005     onClick : function(e)
5006     {
5007         if(this.preventDefault){
5008             e.preventDefault();
5009         }
5010         
5011         this.fireEvent('click', this, e);
5012     },
5013     
5014     getValue : function()
5015     {
5016         return this.el.dom.innerHTML;
5017     },
5018     
5019     setValue : function(value)
5020     {
5021         this.el.dom.innerHTML = value;
5022     }
5023    
5024 });
5025
5026  
5027
5028  /*
5029  * - LGPL
5030  *
5031  * pagination
5032  * 
5033  */
5034
5035 /**
5036  * @class Roo.bootstrap.Pagination
5037  * @extends Roo.bootstrap.Component
5038  * Bootstrap Pagination class
5039  * @cfg {String} size xs | sm | md | lg
5040  * @cfg {Boolean} inverse false | true
5041  * 
5042  * @constructor
5043  * Create a new Pagination
5044  * @param {Object} config The config object
5045  */
5046
5047 Roo.bootstrap.Pagination = function(config){
5048     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5049 };
5050
5051 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5052     
5053     cls: false,
5054     size: false,
5055     inverse: false,
5056     
5057     getAutoCreate : function(){
5058         var cfg = {
5059             tag: 'ul',
5060                 cls: 'pagination'
5061         };
5062         if (this.inverse) {
5063             cfg.cls += ' inverse';
5064         }
5065         if (this.html) {
5066             cfg.html=this.html;
5067         }
5068         if (this.cls) {
5069             cfg.cls += " " + this.cls;
5070         }
5071         return cfg;
5072     }
5073    
5074 });
5075
5076  
5077
5078  /*
5079  * - LGPL
5080  *
5081  * Pagination item
5082  * 
5083  */
5084
5085
5086 /**
5087  * @class Roo.bootstrap.PaginationItem
5088  * @extends Roo.bootstrap.Component
5089  * Bootstrap PaginationItem class
5090  * @cfg {String} html text
5091  * @cfg {String} href the link
5092  * @cfg {Boolean} preventDefault (true | false) default true
5093  * @cfg {Boolean} active (true | false) default false
5094  * @cfg {Boolean} disabled default false
5095  * 
5096  * 
5097  * @constructor
5098  * Create a new PaginationItem
5099  * @param {Object} config The config object
5100  */
5101
5102
5103 Roo.bootstrap.PaginationItem = function(config){
5104     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5105     this.addEvents({
5106         // raw events
5107         /**
5108          * @event click
5109          * The raw click event for the entire grid.
5110          * @param {Roo.EventObject} e
5111          */
5112         "click" : true
5113     });
5114 };
5115
5116 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5117     
5118     href : false,
5119     html : false,
5120     preventDefault: true,
5121     active : false,
5122     cls : false,
5123     disabled: false,
5124     
5125     getAutoCreate : function(){
5126         var cfg= {
5127             tag: 'li',
5128             cn: [
5129                 {
5130                     tag : 'a',
5131                     href : this.href ? this.href : '#',
5132                     html : this.html ? this.html : ''
5133                 }
5134             ]
5135         };
5136         
5137         if(this.cls){
5138             cfg.cls = this.cls;
5139         }
5140         
5141         if(this.disabled){
5142             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5143         }
5144         
5145         if(this.active){
5146             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5147         }
5148         
5149         return cfg;
5150     },
5151     
5152     initEvents: function() {
5153         
5154         this.el.on('click', this.onClick, this);
5155         
5156     },
5157     onClick : function(e)
5158     {
5159         Roo.log('PaginationItem on click ');
5160         if(this.preventDefault){
5161             e.preventDefault();
5162         }
5163         
5164         if(this.disabled){
5165             return;
5166         }
5167         
5168         this.fireEvent('click', this, e);
5169     }
5170    
5171 });
5172
5173  
5174
5175  /*
5176  * - LGPL
5177  *
5178  * slider
5179  * 
5180  */
5181
5182
5183 /**
5184  * @class Roo.bootstrap.Slider
5185  * @extends Roo.bootstrap.Component
5186  * Bootstrap Slider class
5187  *    
5188  * @constructor
5189  * Create a new Slider
5190  * @param {Object} config The config object
5191  */
5192
5193 Roo.bootstrap.Slider = function(config){
5194     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5195 };
5196
5197 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5198     
5199     getAutoCreate : function(){
5200         
5201         var cfg = {
5202             tag: 'div',
5203             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5204             cn: [
5205                 {
5206                     tag: 'a',
5207                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5208                 }
5209             ]
5210         };
5211         
5212         return cfg;
5213     }
5214    
5215 });
5216
5217  /*
5218  * Based on:
5219  * Ext JS Library 1.1.1
5220  * Copyright(c) 2006-2007, Ext JS, LLC.
5221  *
5222  * Originally Released Under LGPL - original licence link has changed is not relivant.
5223  *
5224  * Fork - LGPL
5225  * <script type="text/javascript">
5226  */
5227  
5228
5229 /**
5230  * @class Roo.grid.ColumnModel
5231  * @extends Roo.util.Observable
5232  * This is the default implementation of a ColumnModel used by the Grid. It defines
5233  * the columns in the grid.
5234  * <br>Usage:<br>
5235  <pre><code>
5236  var colModel = new Roo.grid.ColumnModel([
5237         {header: "Ticker", width: 60, sortable: true, locked: true},
5238         {header: "Company Name", width: 150, sortable: true},
5239         {header: "Market Cap.", width: 100, sortable: true},
5240         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5241         {header: "Employees", width: 100, sortable: true, resizable: false}
5242  ]);
5243  </code></pre>
5244  * <p>
5245  
5246  * The config options listed for this class are options which may appear in each
5247  * individual column definition.
5248  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5249  * @constructor
5250  * @param {Object} config An Array of column config objects. See this class's
5251  * config objects for details.
5252 */
5253 Roo.grid.ColumnModel = function(config){
5254         /**
5255      * The config passed into the constructor
5256      */
5257     this.config = config;
5258     this.lookup = {};
5259
5260     // if no id, create one
5261     // if the column does not have a dataIndex mapping,
5262     // map it to the order it is in the config
5263     for(var i = 0, len = config.length; i < len; i++){
5264         var c = config[i];
5265         if(typeof c.dataIndex == "undefined"){
5266             c.dataIndex = i;
5267         }
5268         if(typeof c.renderer == "string"){
5269             c.renderer = Roo.util.Format[c.renderer];
5270         }
5271         if(typeof c.id == "undefined"){
5272             c.id = Roo.id();
5273         }
5274         if(c.editor && c.editor.xtype){
5275             c.editor  = Roo.factory(c.editor, Roo.grid);
5276         }
5277         if(c.editor && c.editor.isFormField){
5278             c.editor = new Roo.grid.GridEditor(c.editor);
5279         }
5280         this.lookup[c.id] = c;
5281     }
5282
5283     /**
5284      * The width of columns which have no width specified (defaults to 100)
5285      * @type Number
5286      */
5287     this.defaultWidth = 100;
5288
5289     /**
5290      * Default sortable of columns which have no sortable specified (defaults to false)
5291      * @type Boolean
5292      */
5293     this.defaultSortable = false;
5294
5295     this.addEvents({
5296         /**
5297              * @event widthchange
5298              * Fires when the width of a column changes.
5299              * @param {ColumnModel} this
5300              * @param {Number} columnIndex The column index
5301              * @param {Number} newWidth The new width
5302              */
5303             "widthchange": true,
5304         /**
5305              * @event headerchange
5306              * Fires when the text of a header changes.
5307              * @param {ColumnModel} this
5308              * @param {Number} columnIndex The column index
5309              * @param {Number} newText The new header text
5310              */
5311             "headerchange": true,
5312         /**
5313              * @event hiddenchange
5314              * Fires when a column is hidden or "unhidden".
5315              * @param {ColumnModel} this
5316              * @param {Number} columnIndex The column index
5317              * @param {Boolean} hidden true if hidden, false otherwise
5318              */
5319             "hiddenchange": true,
5320             /**
5321          * @event columnmoved
5322          * Fires when a column is moved.
5323          * @param {ColumnModel} this
5324          * @param {Number} oldIndex
5325          * @param {Number} newIndex
5326          */
5327         "columnmoved" : true,
5328         /**
5329          * @event columlockchange
5330          * Fires when a column's locked state is changed
5331          * @param {ColumnModel} this
5332          * @param {Number} colIndex
5333          * @param {Boolean} locked true if locked
5334          */
5335         "columnlockchange" : true
5336     });
5337     Roo.grid.ColumnModel.superclass.constructor.call(this);
5338 };
5339 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5340     /**
5341      * @cfg {String} header The header text to display in the Grid view.
5342      */
5343     /**
5344      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5345      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5346      * specified, the column's index is used as an index into the Record's data Array.
5347      */
5348     /**
5349      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5350      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5351      */
5352     /**
5353      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5354      * Defaults to the value of the {@link #defaultSortable} property.
5355      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5356      */
5357     /**
5358      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5359      */
5360     /**
5361      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5362      */
5363     /**
5364      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5365      */
5366     /**
5367      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5368      */
5369     /**
5370      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5371      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5372      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5373      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5374      */
5375        /**
5376      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5377      */
5378     /**
5379      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5380      */
5381     /**
5382      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5383      */
5384     /**
5385      * @cfg {String} cursor (Optional)
5386      */
5387     /**
5388      * @cfg {String} tooltip (Optional)
5389      */
5390     /**
5391      * @cfg {Number} xs (Optional)
5392      */
5393     /**
5394      * @cfg {Number} sm (Optional)
5395      */
5396     /**
5397      * @cfg {Number} md (Optional)
5398      */
5399     /**
5400      * @cfg {Number} lg (Optional)
5401      */
5402     /**
5403      * Returns the id of the column at the specified index.
5404      * @param {Number} index The column index
5405      * @return {String} the id
5406      */
5407     getColumnId : function(index){
5408         return this.config[index].id;
5409     },
5410
5411     /**
5412      * Returns the column for a specified id.
5413      * @param {String} id The column id
5414      * @return {Object} the column
5415      */
5416     getColumnById : function(id){
5417         return this.lookup[id];
5418     },
5419
5420     
5421     /**
5422      * Returns the column for a specified dataIndex.
5423      * @param {String} dataIndex The column dataIndex
5424      * @return {Object|Boolean} the column or false if not found
5425      */
5426     getColumnByDataIndex: function(dataIndex){
5427         var index = this.findColumnIndex(dataIndex);
5428         return index > -1 ? this.config[index] : false;
5429     },
5430     
5431     /**
5432      * Returns the index for a specified column id.
5433      * @param {String} id The column id
5434      * @return {Number} the index, or -1 if not found
5435      */
5436     getIndexById : function(id){
5437         for(var i = 0, len = this.config.length; i < len; i++){
5438             if(this.config[i].id == id){
5439                 return i;
5440             }
5441         }
5442         return -1;
5443     },
5444     
5445     /**
5446      * Returns the index for a specified column dataIndex.
5447      * @param {String} dataIndex The column dataIndex
5448      * @return {Number} the index, or -1 if not found
5449      */
5450     
5451     findColumnIndex : function(dataIndex){
5452         for(var i = 0, len = this.config.length; i < len; i++){
5453             if(this.config[i].dataIndex == dataIndex){
5454                 return i;
5455             }
5456         }
5457         return -1;
5458     },
5459     
5460     
5461     moveColumn : function(oldIndex, newIndex){
5462         var c = this.config[oldIndex];
5463         this.config.splice(oldIndex, 1);
5464         this.config.splice(newIndex, 0, c);
5465         this.dataMap = null;
5466         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5467     },
5468
5469     isLocked : function(colIndex){
5470         return this.config[colIndex].locked === true;
5471     },
5472
5473     setLocked : function(colIndex, value, suppressEvent){
5474         if(this.isLocked(colIndex) == value){
5475             return;
5476         }
5477         this.config[colIndex].locked = value;
5478         if(!suppressEvent){
5479             this.fireEvent("columnlockchange", this, colIndex, value);
5480         }
5481     },
5482
5483     getTotalLockedWidth : function(){
5484         var totalWidth = 0;
5485         for(var i = 0; i < this.config.length; i++){
5486             if(this.isLocked(i) && !this.isHidden(i)){
5487                 this.totalWidth += this.getColumnWidth(i);
5488             }
5489         }
5490         return totalWidth;
5491     },
5492
5493     getLockedCount : function(){
5494         for(var i = 0, len = this.config.length; i < len; i++){
5495             if(!this.isLocked(i)){
5496                 return i;
5497             }
5498         }
5499         
5500         return this.config.length;
5501     },
5502
5503     /**
5504      * Returns the number of columns.
5505      * @return {Number}
5506      */
5507     getColumnCount : function(visibleOnly){
5508         if(visibleOnly === true){
5509             var c = 0;
5510             for(var i = 0, len = this.config.length; i < len; i++){
5511                 if(!this.isHidden(i)){
5512                     c++;
5513                 }
5514             }
5515             return c;
5516         }
5517         return this.config.length;
5518     },
5519
5520     /**
5521      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5522      * @param {Function} fn
5523      * @param {Object} scope (optional)
5524      * @return {Array} result
5525      */
5526     getColumnsBy : function(fn, scope){
5527         var r = [];
5528         for(var i = 0, len = this.config.length; i < len; i++){
5529             var c = this.config[i];
5530             if(fn.call(scope||this, c, i) === true){
5531                 r[r.length] = c;
5532             }
5533         }
5534         return r;
5535     },
5536
5537     /**
5538      * Returns true if the specified column is sortable.
5539      * @param {Number} col The column index
5540      * @return {Boolean}
5541      */
5542     isSortable : function(col){
5543         if(typeof this.config[col].sortable == "undefined"){
5544             return this.defaultSortable;
5545         }
5546         return this.config[col].sortable;
5547     },
5548
5549     /**
5550      * Returns the rendering (formatting) function defined for the column.
5551      * @param {Number} col The column index.
5552      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5553      */
5554     getRenderer : function(col){
5555         if(!this.config[col].renderer){
5556             return Roo.grid.ColumnModel.defaultRenderer;
5557         }
5558         return this.config[col].renderer;
5559     },
5560
5561     /**
5562      * Sets the rendering (formatting) function for a column.
5563      * @param {Number} col The column index
5564      * @param {Function} fn The function to use to process the cell's raw data
5565      * to return HTML markup for the grid view. The render function is called with
5566      * the following parameters:<ul>
5567      * <li>Data value.</li>
5568      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5569      * <li>css A CSS style string to apply to the table cell.</li>
5570      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5571      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5572      * <li>Row index</li>
5573      * <li>Column index</li>
5574      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5575      */
5576     setRenderer : function(col, fn){
5577         this.config[col].renderer = fn;
5578     },
5579
5580     /**
5581      * Returns the width for the specified column.
5582      * @param {Number} col The column index
5583      * @return {Number}
5584      */
5585     getColumnWidth : function(col){
5586         return this.config[col].width * 1 || this.defaultWidth;
5587     },
5588
5589     /**
5590      * Sets the width for a column.
5591      * @param {Number} col The column index
5592      * @param {Number} width The new width
5593      */
5594     setColumnWidth : function(col, width, suppressEvent){
5595         this.config[col].width = width;
5596         this.totalWidth = null;
5597         if(!suppressEvent){
5598              this.fireEvent("widthchange", this, col, width);
5599         }
5600     },
5601
5602     /**
5603      * Returns the total width of all columns.
5604      * @param {Boolean} includeHidden True to include hidden column widths
5605      * @return {Number}
5606      */
5607     getTotalWidth : function(includeHidden){
5608         if(!this.totalWidth){
5609             this.totalWidth = 0;
5610             for(var i = 0, len = this.config.length; i < len; i++){
5611                 if(includeHidden || !this.isHidden(i)){
5612                     this.totalWidth += this.getColumnWidth(i);
5613                 }
5614             }
5615         }
5616         return this.totalWidth;
5617     },
5618
5619     /**
5620      * Returns the header for the specified column.
5621      * @param {Number} col The column index
5622      * @return {String}
5623      */
5624     getColumnHeader : function(col){
5625         return this.config[col].header;
5626     },
5627
5628     /**
5629      * Sets the header for a column.
5630      * @param {Number} col The column index
5631      * @param {String} header The new header
5632      */
5633     setColumnHeader : function(col, header){
5634         this.config[col].header = header;
5635         this.fireEvent("headerchange", this, col, header);
5636     },
5637
5638     /**
5639      * Returns the tooltip for the specified column.
5640      * @param {Number} col The column index
5641      * @return {String}
5642      */
5643     getColumnTooltip : function(col){
5644             return this.config[col].tooltip;
5645     },
5646     /**
5647      * Sets the tooltip for a column.
5648      * @param {Number} col The column index
5649      * @param {String} tooltip The new tooltip
5650      */
5651     setColumnTooltip : function(col, tooltip){
5652             this.config[col].tooltip = tooltip;
5653     },
5654
5655     /**
5656      * Returns the dataIndex for the specified column.
5657      * @param {Number} col The column index
5658      * @return {Number}
5659      */
5660     getDataIndex : function(col){
5661         return this.config[col].dataIndex;
5662     },
5663
5664     /**
5665      * Sets the dataIndex for a column.
5666      * @param {Number} col The column index
5667      * @param {Number} dataIndex The new dataIndex
5668      */
5669     setDataIndex : function(col, dataIndex){
5670         this.config[col].dataIndex = dataIndex;
5671     },
5672
5673     
5674     
5675     /**
5676      * Returns true if the cell is editable.
5677      * @param {Number} colIndex The column index
5678      * @param {Number} rowIndex The row index - this is nto actually used..?
5679      * @return {Boolean}
5680      */
5681     isCellEditable : function(colIndex, rowIndex){
5682         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5683     },
5684
5685     /**
5686      * Returns the editor defined for the cell/column.
5687      * return false or null to disable editing.
5688      * @param {Number} colIndex The column index
5689      * @param {Number} rowIndex The row index
5690      * @return {Object}
5691      */
5692     getCellEditor : function(colIndex, rowIndex){
5693         return this.config[colIndex].editor;
5694     },
5695
5696     /**
5697      * Sets if a column is editable.
5698      * @param {Number} col The column index
5699      * @param {Boolean} editable True if the column is editable
5700      */
5701     setEditable : function(col, editable){
5702         this.config[col].editable = editable;
5703     },
5704
5705
5706     /**
5707      * Returns true if the column is hidden.
5708      * @param {Number} colIndex The column index
5709      * @return {Boolean}
5710      */
5711     isHidden : function(colIndex){
5712         return this.config[colIndex].hidden;
5713     },
5714
5715
5716     /**
5717      * Returns true if the column width cannot be changed
5718      */
5719     isFixed : function(colIndex){
5720         return this.config[colIndex].fixed;
5721     },
5722
5723     /**
5724      * Returns true if the column can be resized
5725      * @return {Boolean}
5726      */
5727     isResizable : function(colIndex){
5728         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5729     },
5730     /**
5731      * Sets if a column is hidden.
5732      * @param {Number} colIndex The column index
5733      * @param {Boolean} hidden True if the column is hidden
5734      */
5735     setHidden : function(colIndex, hidden){
5736         this.config[colIndex].hidden = hidden;
5737         this.totalWidth = null;
5738         this.fireEvent("hiddenchange", this, colIndex, hidden);
5739     },
5740
5741     /**
5742      * Sets the editor for a column.
5743      * @param {Number} col The column index
5744      * @param {Object} editor The editor object
5745      */
5746     setEditor : function(col, editor){
5747         this.config[col].editor = editor;
5748     }
5749 });
5750
5751 Roo.grid.ColumnModel.defaultRenderer = function(value)
5752 {
5753     if(typeof value == "object") {
5754         return value;
5755     }
5756         if(typeof value == "string" && value.length < 1){
5757             return "&#160;";
5758         }
5759     
5760         return String.format("{0}", value);
5761 };
5762
5763 // Alias for backwards compatibility
5764 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5765 /*
5766  * Based on:
5767  * Ext JS Library 1.1.1
5768  * Copyright(c) 2006-2007, Ext JS, LLC.
5769  *
5770  * Originally Released Under LGPL - original licence link has changed is not relivant.
5771  *
5772  * Fork - LGPL
5773  * <script type="text/javascript">
5774  */
5775  
5776 /**
5777  * @class Roo.LoadMask
5778  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5779  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5780  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5781  * element's UpdateManager load indicator and will be destroyed after the initial load.
5782  * @constructor
5783  * Create a new LoadMask
5784  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5785  * @param {Object} config The config object
5786  */
5787 Roo.LoadMask = function(el, config){
5788     this.el = Roo.get(el);
5789     Roo.apply(this, config);
5790     if(this.store){
5791         this.store.on('beforeload', this.onBeforeLoad, this);
5792         this.store.on('load', this.onLoad, this);
5793         this.store.on('loadexception', this.onLoadException, this);
5794         this.removeMask = false;
5795     }else{
5796         var um = this.el.getUpdateManager();
5797         um.showLoadIndicator = false; // disable the default indicator
5798         um.on('beforeupdate', this.onBeforeLoad, this);
5799         um.on('update', this.onLoad, this);
5800         um.on('failure', this.onLoad, this);
5801         this.removeMask = true;
5802     }
5803 };
5804
5805 Roo.LoadMask.prototype = {
5806     /**
5807      * @cfg {Boolean} removeMask
5808      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5809      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5810      */
5811     /**
5812      * @cfg {String} msg
5813      * The text to display in a centered loading message box (defaults to 'Loading...')
5814      */
5815     msg : 'Loading...',
5816     /**
5817      * @cfg {String} msgCls
5818      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5819      */
5820     msgCls : 'x-mask-loading',
5821
5822     /**
5823      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5824      * @type Boolean
5825      */
5826     disabled: false,
5827
5828     /**
5829      * Disables the mask to prevent it from being displayed
5830      */
5831     disable : function(){
5832        this.disabled = true;
5833     },
5834
5835     /**
5836      * Enables the mask so that it can be displayed
5837      */
5838     enable : function(){
5839         this.disabled = false;
5840     },
5841     
5842     onLoadException : function()
5843     {
5844         Roo.log(arguments);
5845         
5846         if (typeof(arguments[3]) != 'undefined') {
5847             Roo.MessageBox.alert("Error loading",arguments[3]);
5848         } 
5849         /*
5850         try {
5851             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5852                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5853             }   
5854         } catch(e) {
5855             
5856         }
5857         */
5858     
5859         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5860     },
5861     // private
5862     onLoad : function()
5863     {
5864         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5865     },
5866
5867     // private
5868     onBeforeLoad : function(){
5869         if(!this.disabled){
5870             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5871         }
5872     },
5873
5874     // private
5875     destroy : function(){
5876         if(this.store){
5877             this.store.un('beforeload', this.onBeforeLoad, this);
5878             this.store.un('load', this.onLoad, this);
5879             this.store.un('loadexception', this.onLoadException, this);
5880         }else{
5881             var um = this.el.getUpdateManager();
5882             um.un('beforeupdate', this.onBeforeLoad, this);
5883             um.un('update', this.onLoad, this);
5884             um.un('failure', this.onLoad, this);
5885         }
5886     }
5887 };/*
5888  * - LGPL
5889  *
5890  * table
5891  * 
5892  */
5893
5894 /**
5895  * @class Roo.bootstrap.Table
5896  * @extends Roo.bootstrap.Component
5897  * Bootstrap Table class
5898  * @cfg {String} cls table class
5899  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5900  * @cfg {String} bgcolor Specifies the background color for a table
5901  * @cfg {Number} border Specifies whether the table cells should have borders or not
5902  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5903  * @cfg {Number} cellspacing Specifies the space between cells
5904  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5905  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5906  * @cfg {String} sortable Specifies that the table should be sortable
5907  * @cfg {String} summary Specifies a summary of the content of a table
5908  * @cfg {Number} width Specifies the width of a table
5909  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5910  * 
5911  * @cfg {boolean} striped Should the rows be alternative striped
5912  * @cfg {boolean} bordered Add borders to the table
5913  * @cfg {boolean} hover Add hover highlighting
5914  * @cfg {boolean} condensed Format condensed
5915  * @cfg {boolean} responsive Format condensed
5916  * @cfg {Boolean} loadMask (true|false) default false
5917  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5918  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5919  * @cfg {Boolean} rowSelection (true|false) default false
5920  * @cfg {Boolean} cellSelection (true|false) default false
5921  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5922  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5923  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5924  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
5925  
5926  * 
5927  * @constructor
5928  * Create a new Table
5929  * @param {Object} config The config object
5930  */
5931
5932 Roo.bootstrap.Table = function(config){
5933     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5934     
5935   
5936     
5937     // BC...
5938     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5939     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5940     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5941     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5942     
5943     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5944     if (this.sm) {
5945         this.sm.grid = this;
5946         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5947         this.sm = this.selModel;
5948         this.sm.xmodule = this.xmodule || false;
5949     }
5950     
5951     if (this.cm && typeof(this.cm.config) == 'undefined') {
5952         this.colModel = new Roo.grid.ColumnModel(this.cm);
5953         this.cm = this.colModel;
5954         this.cm.xmodule = this.xmodule || false;
5955     }
5956     if (this.store) {
5957         this.store= Roo.factory(this.store, Roo.data);
5958         this.ds = this.store;
5959         this.ds.xmodule = this.xmodule || false;
5960          
5961     }
5962     if (this.footer && this.store) {
5963         this.footer.dataSource = this.ds;
5964         this.footer = Roo.factory(this.footer);
5965     }
5966     
5967     /** @private */
5968     this.addEvents({
5969         /**
5970          * @event cellclick
5971          * Fires when a cell is clicked
5972          * @param {Roo.bootstrap.Table} this
5973          * @param {Roo.Element} el
5974          * @param {Number} rowIndex
5975          * @param {Number} columnIndex
5976          * @param {Roo.EventObject} e
5977          */
5978         "cellclick" : true,
5979         /**
5980          * @event celldblclick
5981          * Fires when a cell is double clicked
5982          * @param {Roo.bootstrap.Table} this
5983          * @param {Roo.Element} el
5984          * @param {Number} rowIndex
5985          * @param {Number} columnIndex
5986          * @param {Roo.EventObject} e
5987          */
5988         "celldblclick" : true,
5989         /**
5990          * @event rowclick
5991          * Fires when a row is clicked
5992          * @param {Roo.bootstrap.Table} this
5993          * @param {Roo.Element} el
5994          * @param {Number} rowIndex
5995          * @param {Roo.EventObject} e
5996          */
5997         "rowclick" : true,
5998         /**
5999          * @event rowdblclick
6000          * Fires when a row is double clicked
6001          * @param {Roo.bootstrap.Table} this
6002          * @param {Roo.Element} el
6003          * @param {Number} rowIndex
6004          * @param {Roo.EventObject} e
6005          */
6006         "rowdblclick" : true,
6007         /**
6008          * @event mouseover
6009          * Fires when a mouseover occur
6010          * @param {Roo.bootstrap.Table} this
6011          * @param {Roo.Element} el
6012          * @param {Number} rowIndex
6013          * @param {Number} columnIndex
6014          * @param {Roo.EventObject} e
6015          */
6016         "mouseover" : true,
6017         /**
6018          * @event mouseout
6019          * Fires when a mouseout occur
6020          * @param {Roo.bootstrap.Table} this
6021          * @param {Roo.Element} el
6022          * @param {Number} rowIndex
6023          * @param {Number} columnIndex
6024          * @param {Roo.EventObject} e
6025          */
6026         "mouseout" : true,
6027         /**
6028          * @event rowclass
6029          * Fires when a row is rendered, so you can change add a style to it.
6030          * @param {Roo.bootstrap.Table} this
6031          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6032          */
6033         'rowclass' : true,
6034           /**
6035          * @event rowsrendered
6036          * Fires when all the  rows have been rendered
6037          * @param {Roo.bootstrap.Table} this
6038          */
6039         'rowsrendered' : true,
6040         /**
6041          * @event contextmenu
6042          * The raw contextmenu event for the entire grid.
6043          * @param {Roo.EventObject} e
6044          */
6045         "contextmenu" : true,
6046         /**
6047          * @event rowcontextmenu
6048          * Fires when a row is right clicked
6049          * @param {Roo.bootstrap.Table} this
6050          * @param {Number} rowIndex
6051          * @param {Roo.EventObject} e
6052          */
6053         "rowcontextmenu" : true,
6054         /**
6055          * @event cellcontextmenu
6056          * Fires when a cell is right clicked
6057          * @param {Roo.bootstrap.Table} this
6058          * @param {Number} rowIndex
6059          * @param {Number} cellIndex
6060          * @param {Roo.EventObject} e
6061          */
6062          "cellcontextmenu" : true,
6063          /**
6064          * @event headercontextmenu
6065          * Fires when a header is right clicked
6066          * @param {Roo.bootstrap.Table} this
6067          * @param {Number} columnIndex
6068          * @param {Roo.EventObject} e
6069          */
6070         "headercontextmenu" : true
6071     });
6072 };
6073
6074 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6075     
6076     cls: false,
6077     align: false,
6078     bgcolor: false,
6079     border: false,
6080     cellpadding: false,
6081     cellspacing: false,
6082     frame: false,
6083     rules: false,
6084     sortable: false,
6085     summary: false,
6086     width: false,
6087     striped : false,
6088     scrollBody : false,
6089     bordered: false,
6090     hover:  false,
6091     condensed : false,
6092     responsive : false,
6093     sm : false,
6094     cm : false,
6095     store : false,
6096     loadMask : false,
6097     footerShow : true,
6098     headerShow : true,
6099   
6100     rowSelection : false,
6101     cellSelection : false,
6102     layout : false,
6103     
6104     // Roo.Element - the tbody
6105     mainBody: false,
6106     // Roo.Element - thead element
6107     mainHead: false,
6108     
6109     container: false, // used by gridpanel...
6110     
6111     lazyLoad : false,
6112     
6113     CSS : Roo.util.CSS,
6114     
6115     auto_hide_footer : false,
6116     
6117     getAutoCreate : function()
6118     {
6119         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6120         
6121         cfg = {
6122             tag: 'table',
6123             cls : 'table',
6124             cn : []
6125         };
6126         if (this.scrollBody) {
6127             cfg.cls += ' table-body-fixed';
6128         }    
6129         if (this.striped) {
6130             cfg.cls += ' table-striped';
6131         }
6132         
6133         if (this.hover) {
6134             cfg.cls += ' table-hover';
6135         }
6136         if (this.bordered) {
6137             cfg.cls += ' table-bordered';
6138         }
6139         if (this.condensed) {
6140             cfg.cls += ' table-condensed';
6141         }
6142         if (this.responsive) {
6143             cfg.cls += ' table-responsive';
6144         }
6145         
6146         if (this.cls) {
6147             cfg.cls+=  ' ' +this.cls;
6148         }
6149         
6150         // this lot should be simplifed...
6151         var _t = this;
6152         var cp = [
6153             'align',
6154             'bgcolor',
6155             'border',
6156             'cellpadding',
6157             'cellspacing',
6158             'frame',
6159             'rules',
6160             'sortable',
6161             'summary',
6162             'width'
6163         ].forEach(function(k) {
6164             if (_t[k]) {
6165                 cfg[k] = _t[k];
6166             }
6167         });
6168         
6169         
6170         if (this.layout) {
6171             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6172         }
6173         
6174         if(this.store || this.cm){
6175             if(this.headerShow){
6176                 cfg.cn.push(this.renderHeader());
6177             }
6178             
6179             cfg.cn.push(this.renderBody());
6180             
6181             if(this.footerShow){
6182                 cfg.cn.push(this.renderFooter());
6183             }
6184             // where does this come from?
6185             //cfg.cls+=  ' TableGrid';
6186         }
6187         
6188         return { cn : [ cfg ] };
6189     },
6190     
6191     initEvents : function()
6192     {   
6193         if(!this.store || !this.cm){
6194             return;
6195         }
6196         if (this.selModel) {
6197             this.selModel.initEvents();
6198         }
6199         
6200         
6201         //Roo.log('initEvents with ds!!!!');
6202         
6203         this.mainBody = this.el.select('tbody', true).first();
6204         this.mainHead = this.el.select('thead', true).first();
6205         this.mainFoot = this.el.select('tfoot', true).first();
6206         
6207         
6208         
6209         var _this = this;
6210         
6211         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6212             e.on('click', _this.sort, _this);
6213         });
6214         
6215         this.mainBody.on("click", this.onClick, this);
6216         this.mainBody.on("dblclick", this.onDblClick, this);
6217         
6218         // why is this done????? = it breaks dialogs??
6219         //this.parent().el.setStyle('position', 'relative');
6220         
6221         
6222         if (this.footer) {
6223             this.footer.parentId = this.id;
6224             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6225             
6226             if(this.lazyLoad){
6227                 this.el.select('tfoot tr td').first().addClass('hide');
6228             }
6229         } 
6230         
6231         if(this.loadMask) {
6232             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6233         }
6234         
6235         this.store.on('load', this.onLoad, this);
6236         this.store.on('beforeload', this.onBeforeLoad, this);
6237         this.store.on('update', this.onUpdate, this);
6238         this.store.on('add', this.onAdd, this);
6239         this.store.on("clear", this.clear, this);
6240         
6241         this.el.on("contextmenu", this.onContextMenu, this);
6242         
6243         this.mainBody.on('scroll', this.onBodyScroll, this);
6244         
6245         this.cm.on("headerchange", this.onHeaderChange, this);
6246         
6247         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6248         
6249     },
6250     
6251     onContextMenu : function(e, t)
6252     {
6253         this.processEvent("contextmenu", e);
6254     },
6255     
6256     processEvent : function(name, e)
6257     {
6258         if (name != 'touchstart' ) {
6259             this.fireEvent(name, e);    
6260         }
6261         
6262         var t = e.getTarget();
6263         
6264         var cell = Roo.get(t);
6265         
6266         if(!cell){
6267             return;
6268         }
6269         
6270         if(cell.findParent('tfoot', false, true)){
6271             return;
6272         }
6273         
6274         if(cell.findParent('thead', false, true)){
6275             
6276             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6277                 cell = Roo.get(t).findParent('th', false, true);
6278                 if (!cell) {
6279                     Roo.log("failed to find th in thead?");
6280                     Roo.log(e.getTarget());
6281                     return;
6282                 }
6283             }
6284             
6285             var cellIndex = cell.dom.cellIndex;
6286             
6287             var ename = name == 'touchstart' ? 'click' : name;
6288             this.fireEvent("header" + ename, this, cellIndex, e);
6289             
6290             return;
6291         }
6292         
6293         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6294             cell = Roo.get(t).findParent('td', false, true);
6295             if (!cell) {
6296                 Roo.log("failed to find th in tbody?");
6297                 Roo.log(e.getTarget());
6298                 return;
6299             }
6300         }
6301         
6302         var row = cell.findParent('tr', false, true);
6303         var cellIndex = cell.dom.cellIndex;
6304         var rowIndex = row.dom.rowIndex - 1;
6305         
6306         if(row !== false){
6307             
6308             this.fireEvent("row" + name, this, rowIndex, e);
6309             
6310             if(cell !== false){
6311             
6312                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6313             }
6314         }
6315         
6316     },
6317     
6318     onMouseover : function(e, el)
6319     {
6320         var cell = Roo.get(el);
6321         
6322         if(!cell){
6323             return;
6324         }
6325         
6326         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6327             cell = cell.findParent('td', false, true);
6328         }
6329         
6330         var row = cell.findParent('tr', false, true);
6331         var cellIndex = cell.dom.cellIndex;
6332         var rowIndex = row.dom.rowIndex - 1; // start from 0
6333         
6334         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6335         
6336     },
6337     
6338     onMouseout : function(e, el)
6339     {
6340         var cell = Roo.get(el);
6341         
6342         if(!cell){
6343             return;
6344         }
6345         
6346         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6347             cell = cell.findParent('td', false, true);
6348         }
6349         
6350         var row = cell.findParent('tr', false, true);
6351         var cellIndex = cell.dom.cellIndex;
6352         var rowIndex = row.dom.rowIndex - 1; // start from 0
6353         
6354         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6355         
6356     },
6357     
6358     onClick : function(e, el)
6359     {
6360         var cell = Roo.get(el);
6361         
6362         if(!cell || (!this.cellSelection && !this.rowSelection)){
6363             return;
6364         }
6365         
6366         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6367             cell = cell.findParent('td', false, true);
6368         }
6369         
6370         if(!cell || typeof(cell) == 'undefined'){
6371             return;
6372         }
6373         
6374         var row = cell.findParent('tr', false, true);
6375         
6376         if(!row || typeof(row) == 'undefined'){
6377             return;
6378         }
6379         
6380         var cellIndex = cell.dom.cellIndex;
6381         var rowIndex = this.getRowIndex(row);
6382         
6383         // why??? - should these not be based on SelectionModel?
6384         if(this.cellSelection){
6385             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6386         }
6387         
6388         if(this.rowSelection){
6389             this.fireEvent('rowclick', this, row, rowIndex, e);
6390         }
6391         
6392         
6393     },
6394         
6395     onDblClick : function(e,el)
6396     {
6397         var cell = Roo.get(el);
6398         
6399         if(!cell || (!this.cellSelection && !this.rowSelection)){
6400             return;
6401         }
6402         
6403         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6404             cell = cell.findParent('td', false, true);
6405         }
6406         
6407         if(!cell || typeof(cell) == 'undefined'){
6408             return;
6409         }
6410         
6411         var row = cell.findParent('tr', false, true);
6412         
6413         if(!row || typeof(row) == 'undefined'){
6414             return;
6415         }
6416         
6417         var cellIndex = cell.dom.cellIndex;
6418         var rowIndex = this.getRowIndex(row);
6419         
6420         if(this.cellSelection){
6421             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6422         }
6423         
6424         if(this.rowSelection){
6425             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6426         }
6427     },
6428     
6429     sort : function(e,el)
6430     {
6431         var col = Roo.get(el);
6432         
6433         if(!col.hasClass('sortable')){
6434             return;
6435         }
6436         
6437         var sort = col.attr('sort');
6438         var dir = 'ASC';
6439         
6440         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6441             dir = 'DESC';
6442         }
6443         
6444         this.store.sortInfo = {field : sort, direction : dir};
6445         
6446         if (this.footer) {
6447             Roo.log("calling footer first");
6448             this.footer.onClick('first');
6449         } else {
6450         
6451             this.store.load({ params : { start : 0 } });
6452         }
6453     },
6454     
6455     renderHeader : function()
6456     {
6457         var header = {
6458             tag: 'thead',
6459             cn : []
6460         };
6461         
6462         var cm = this.cm;
6463         this.totalWidth = 0;
6464         
6465         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6466             
6467             var config = cm.config[i];
6468             
6469             var c = {
6470                 tag: 'th',
6471                 cls : 'x-hcol-' + i,
6472                 style : '',
6473                 html: cm.getColumnHeader(i)
6474             };
6475             
6476             var hh = '';
6477             
6478             if(typeof(config.sortable) != 'undefined' && config.sortable){
6479                 c.cls = 'sortable';
6480                 c.html = '<i class="glyphicon"></i>' + c.html;
6481             }
6482             
6483             if(typeof(config.lgHeader) != 'undefined'){
6484                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6485             }
6486             
6487             if(typeof(config.mdHeader) != 'undefined'){
6488                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6489             }
6490             
6491             if(typeof(config.smHeader) != 'undefined'){
6492                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6493             }
6494             
6495             if(typeof(config.xsHeader) != 'undefined'){
6496                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6497             }
6498             
6499             if(hh.length){
6500                 c.html = hh;
6501             }
6502             
6503             if(typeof(config.tooltip) != 'undefined'){
6504                 c.tooltip = config.tooltip;
6505             }
6506             
6507             if(typeof(config.colspan) != 'undefined'){
6508                 c.colspan = config.colspan;
6509             }
6510             
6511             if(typeof(config.hidden) != 'undefined' && config.hidden){
6512                 c.style += ' display:none;';
6513             }
6514             
6515             if(typeof(config.dataIndex) != 'undefined'){
6516                 c.sort = config.dataIndex;
6517             }
6518             
6519            
6520             
6521             if(typeof(config.align) != 'undefined' && config.align.length){
6522                 c.style += ' text-align:' + config.align + ';';
6523             }
6524             
6525             if(typeof(config.width) != 'undefined'){
6526                 c.style += ' width:' + config.width + 'px;';
6527                 this.totalWidth += config.width;
6528             } else {
6529                 this.totalWidth += 100; // assume minimum of 100 per column?
6530             }
6531             
6532             if(typeof(config.cls) != 'undefined'){
6533                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6534             }
6535             
6536             ['xs','sm','md','lg'].map(function(size){
6537                 
6538                 if(typeof(config[size]) == 'undefined'){
6539                     return;
6540                 }
6541                 
6542                 if (!config[size]) { // 0 = hidden
6543                     c.cls += ' hidden-' + size;
6544                     return;
6545                 }
6546                 
6547                 c.cls += ' col-' + size + '-' + config[size];
6548
6549             });
6550             
6551             header.cn.push(c)
6552         }
6553         
6554         return header;
6555     },
6556     
6557     renderBody : function()
6558     {
6559         var body = {
6560             tag: 'tbody',
6561             cn : [
6562                 {
6563                     tag: 'tr',
6564                     cn : [
6565                         {
6566                             tag : 'td',
6567                             colspan :  this.cm.getColumnCount()
6568                         }
6569                     ]
6570                 }
6571             ]
6572         };
6573         
6574         return body;
6575     },
6576     
6577     renderFooter : function()
6578     {
6579         var footer = {
6580             tag: 'tfoot',
6581             cn : [
6582                 {
6583                     tag: 'tr',
6584                     cn : [
6585                         {
6586                             tag : 'td',
6587                             colspan :  this.cm.getColumnCount()
6588                         }
6589                     ]
6590                 }
6591             ]
6592         };
6593         
6594         return footer;
6595     },
6596     
6597     
6598     
6599     onLoad : function()
6600     {
6601 //        Roo.log('ds onload');
6602         this.clear();
6603         
6604         var _this = this;
6605         var cm = this.cm;
6606         var ds = this.store;
6607         
6608         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6609             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6610             if (_this.store.sortInfo) {
6611                     
6612                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6613                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6614                 }
6615                 
6616                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6617                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6618                 }
6619             }
6620         });
6621         
6622         var tbody =  this.mainBody;
6623               
6624         if(ds.getCount() > 0){
6625             ds.data.each(function(d,rowIndex){
6626                 var row =  this.renderRow(cm, ds, rowIndex);
6627                 
6628                 tbody.createChild(row);
6629                 
6630                 var _this = this;
6631                 
6632                 if(row.cellObjects.length){
6633                     Roo.each(row.cellObjects, function(r){
6634                         _this.renderCellObject(r);
6635                     })
6636                 }
6637                 
6638             }, this);
6639         }
6640         
6641         var tfoot = this.el.select('tfoot', true).first();
6642         
6643         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6644             
6645             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6646             
6647             var total = this.ds.getTotalCount();
6648             
6649             if(this.footer.pageSize < total){
6650                 this.mainFoot.show();
6651             }
6652         }
6653         
6654         Roo.each(this.el.select('tbody td', true).elements, function(e){
6655             e.on('mouseover', _this.onMouseover, _this);
6656         });
6657         
6658         Roo.each(this.el.select('tbody td', true).elements, function(e){
6659             e.on('mouseout', _this.onMouseout, _this);
6660         });
6661         this.fireEvent('rowsrendered', this);
6662         
6663         this.autoSize();
6664     },
6665     
6666     
6667     onUpdate : function(ds,record)
6668     {
6669         this.refreshRow(record);
6670         this.autoSize();
6671     },
6672     
6673     onRemove : function(ds, record, index, isUpdate){
6674         if(isUpdate !== true){
6675             this.fireEvent("beforerowremoved", this, index, record);
6676         }
6677         var bt = this.mainBody.dom;
6678         
6679         var rows = this.el.select('tbody > tr', true).elements;
6680         
6681         if(typeof(rows[index]) != 'undefined'){
6682             bt.removeChild(rows[index].dom);
6683         }
6684         
6685 //        if(bt.rows[index]){
6686 //            bt.removeChild(bt.rows[index]);
6687 //        }
6688         
6689         if(isUpdate !== true){
6690             //this.stripeRows(index);
6691             //this.syncRowHeights(index, index);
6692             //this.layout();
6693             this.fireEvent("rowremoved", this, index, record);
6694         }
6695     },
6696     
6697     onAdd : function(ds, records, rowIndex)
6698     {
6699         //Roo.log('on Add called');
6700         // - note this does not handle multiple adding very well..
6701         var bt = this.mainBody.dom;
6702         for (var i =0 ; i < records.length;i++) {
6703             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6704             //Roo.log(records[i]);
6705             //Roo.log(this.store.getAt(rowIndex+i));
6706             this.insertRow(this.store, rowIndex + i, false);
6707             return;
6708         }
6709         
6710     },
6711     
6712     
6713     refreshRow : function(record){
6714         var ds = this.store, index;
6715         if(typeof record == 'number'){
6716             index = record;
6717             record = ds.getAt(index);
6718         }else{
6719             index = ds.indexOf(record);
6720         }
6721         this.insertRow(ds, index, true);
6722         this.autoSize();
6723         this.onRemove(ds, record, index+1, true);
6724         this.autoSize();
6725         //this.syncRowHeights(index, index);
6726         //this.layout();
6727         this.fireEvent("rowupdated", this, index, record);
6728     },
6729     
6730     insertRow : function(dm, rowIndex, isUpdate){
6731         
6732         if(!isUpdate){
6733             this.fireEvent("beforerowsinserted", this, rowIndex);
6734         }
6735             //var s = this.getScrollState();
6736         var row = this.renderRow(this.cm, this.store, rowIndex);
6737         // insert before rowIndex..
6738         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6739         
6740         var _this = this;
6741                 
6742         if(row.cellObjects.length){
6743             Roo.each(row.cellObjects, function(r){
6744                 _this.renderCellObject(r);
6745             })
6746         }
6747             
6748         if(!isUpdate){
6749             this.fireEvent("rowsinserted", this, rowIndex);
6750             //this.syncRowHeights(firstRow, lastRow);
6751             //this.stripeRows(firstRow);
6752             //this.layout();
6753         }
6754         
6755     },
6756     
6757     
6758     getRowDom : function(rowIndex)
6759     {
6760         var rows = this.el.select('tbody > tr', true).elements;
6761         
6762         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6763         
6764     },
6765     // returns the object tree for a tr..
6766   
6767     
6768     renderRow : function(cm, ds, rowIndex) 
6769     {
6770         var d = ds.getAt(rowIndex);
6771         
6772         var row = {
6773             tag : 'tr',
6774             cls : 'x-row-' + rowIndex,
6775             cn : []
6776         };
6777             
6778         var cellObjects = [];
6779         
6780         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6781             var config = cm.config[i];
6782             
6783             var renderer = cm.getRenderer(i);
6784             var value = '';
6785             var id = false;
6786             
6787             if(typeof(renderer) !== 'undefined'){
6788                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6789             }
6790             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6791             // and are rendered into the cells after the row is rendered - using the id for the element.
6792             
6793             if(typeof(value) === 'object'){
6794                 id = Roo.id();
6795                 cellObjects.push({
6796                     container : id,
6797                     cfg : value 
6798                 })
6799             }
6800             
6801             var rowcfg = {
6802                 record: d,
6803                 rowIndex : rowIndex,
6804                 colIndex : i,
6805                 rowClass : ''
6806             };
6807
6808             this.fireEvent('rowclass', this, rowcfg);
6809             
6810             var td = {
6811                 tag: 'td',
6812                 cls : rowcfg.rowClass + ' x-col-' + i,
6813                 style: '',
6814                 html: (typeof(value) === 'object') ? '' : value
6815             };
6816             
6817             if (id) {
6818                 td.id = id;
6819             }
6820             
6821             if(typeof(config.colspan) != 'undefined'){
6822                 td.colspan = config.colspan;
6823             }
6824             
6825             if(typeof(config.hidden) != 'undefined' && config.hidden){
6826                 td.style += ' display:none;';
6827             }
6828             
6829             if(typeof(config.align) != 'undefined' && config.align.length){
6830                 td.style += ' text-align:' + config.align + ';';
6831             }
6832             if(typeof(config.valign) != 'undefined' && config.valign.length){
6833                 td.style += ' vertical-align:' + config.valign + ';';
6834             }
6835             
6836             if(typeof(config.width) != 'undefined'){
6837                 td.style += ' width:' +  config.width + 'px;';
6838             }
6839             
6840             if(typeof(config.cursor) != 'undefined'){
6841                 td.style += ' cursor:' +  config.cursor + ';';
6842             }
6843             
6844             if(typeof(config.cls) != 'undefined'){
6845                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6846             }
6847             
6848             ['xs','sm','md','lg'].map(function(size){
6849                 
6850                 if(typeof(config[size]) == 'undefined'){
6851                     return;
6852                 }
6853                 
6854                 if (!config[size]) { // 0 = hidden
6855                     td.cls += ' hidden-' + size;
6856                     return;
6857                 }
6858                 
6859                 td.cls += ' col-' + size + '-' + config[size];
6860
6861             });
6862             
6863             row.cn.push(td);
6864            
6865         }
6866         
6867         row.cellObjects = cellObjects;
6868         
6869         return row;
6870           
6871     },
6872     
6873     
6874     
6875     onBeforeLoad : function()
6876     {
6877         
6878     },
6879      /**
6880      * Remove all rows
6881      */
6882     clear : function()
6883     {
6884         this.el.select('tbody', true).first().dom.innerHTML = '';
6885     },
6886     /**
6887      * Show or hide a row.
6888      * @param {Number} rowIndex to show or hide
6889      * @param {Boolean} state hide
6890      */
6891     setRowVisibility : function(rowIndex, state)
6892     {
6893         var bt = this.mainBody.dom;
6894         
6895         var rows = this.el.select('tbody > tr', true).elements;
6896         
6897         if(typeof(rows[rowIndex]) == 'undefined'){
6898             return;
6899         }
6900         rows[rowIndex].dom.style.display = state ? '' : 'none';
6901     },
6902     
6903     
6904     getSelectionModel : function(){
6905         if(!this.selModel){
6906             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6907         }
6908         return this.selModel;
6909     },
6910     /*
6911      * Render the Roo.bootstrap object from renderder
6912      */
6913     renderCellObject : function(r)
6914     {
6915         var _this = this;
6916         
6917         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6918         
6919         var t = r.cfg.render(r.container);
6920         
6921         if(r.cfg.cn){
6922             Roo.each(r.cfg.cn, function(c){
6923                 var child = {
6924                     container: t.getChildContainer(),
6925                     cfg: c
6926                 };
6927                 _this.renderCellObject(child);
6928             })
6929         }
6930     },
6931     
6932     getRowIndex : function(row)
6933     {
6934         var rowIndex = -1;
6935         
6936         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6937             if(el != row){
6938                 return;
6939             }
6940             
6941             rowIndex = index;
6942         });
6943         
6944         return rowIndex;
6945     },
6946      /**
6947      * Returns the grid's underlying element = used by panel.Grid
6948      * @return {Element} The element
6949      */
6950     getGridEl : function(){
6951         return this.el;
6952     },
6953      /**
6954      * Forces a resize - used by panel.Grid
6955      * @return {Element} The element
6956      */
6957     autoSize : function()
6958     {
6959         //var ctr = Roo.get(this.container.dom.parentElement);
6960         var ctr = Roo.get(this.el.dom);
6961         
6962         var thd = this.getGridEl().select('thead',true).first();
6963         var tbd = this.getGridEl().select('tbody', true).first();
6964         var tfd = this.getGridEl().select('tfoot', true).first();
6965         
6966         var cw = ctr.getWidth();
6967         
6968         if (tbd) {
6969             
6970             tbd.setSize(ctr.getWidth(),
6971                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6972             );
6973             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6974             cw -= barsize;
6975         }
6976         cw = Math.max(cw, this.totalWidth);
6977         this.getGridEl().select('tr',true).setWidth(cw);
6978         // resize 'expandable coloumn?
6979         
6980         return; // we doe not have a view in this design..
6981         
6982     },
6983     onBodyScroll: function()
6984     {
6985         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6986         if(this.mainHead){
6987             this.mainHead.setStyle({
6988                 'position' : 'relative',
6989                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6990             });
6991         }
6992         
6993         if(this.lazyLoad){
6994             
6995             var scrollHeight = this.mainBody.dom.scrollHeight;
6996             
6997             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6998             
6999             var height = this.mainBody.getHeight();
7000             
7001             if(scrollHeight - height == scrollTop) {
7002                 
7003                 var total = this.ds.getTotalCount();
7004                 
7005                 if(this.footer.cursor + this.footer.pageSize < total){
7006                     
7007                     this.footer.ds.load({
7008                         params : {
7009                             start : this.footer.cursor + this.footer.pageSize,
7010                             limit : this.footer.pageSize
7011                         },
7012                         add : true
7013                     });
7014                 }
7015             }
7016             
7017         }
7018     },
7019     
7020     onHeaderChange : function()
7021     {
7022         var header = this.renderHeader();
7023         var table = this.el.select('table', true).first();
7024         
7025         this.mainHead.remove();
7026         this.mainHead = table.createChild(header, this.mainBody, false);
7027     },
7028     
7029     onHiddenChange : function(colModel, colIndex, hidden)
7030     {
7031         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7032         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7033         
7034         this.CSS.updateRule(thSelector, "display", "");
7035         this.CSS.updateRule(tdSelector, "display", "");
7036         
7037         if(hidden){
7038             this.CSS.updateRule(thSelector, "display", "none");
7039             this.CSS.updateRule(tdSelector, "display", "none");
7040         }
7041         
7042         this.onHeaderChange();
7043         this.onLoad();
7044         
7045     }
7046     
7047 });
7048
7049  
7050
7051  /*
7052  * - LGPL
7053  *
7054  * table cell
7055  * 
7056  */
7057
7058 /**
7059  * @class Roo.bootstrap.TableCell
7060  * @extends Roo.bootstrap.Component
7061  * Bootstrap TableCell class
7062  * @cfg {String} html cell contain text
7063  * @cfg {String} cls cell class
7064  * @cfg {String} tag cell tag (td|th) default td
7065  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7066  * @cfg {String} align Aligns the content in a cell
7067  * @cfg {String} axis Categorizes cells
7068  * @cfg {String} bgcolor Specifies the background color of a cell
7069  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7070  * @cfg {Number} colspan Specifies the number of columns a cell should span
7071  * @cfg {String} headers Specifies one or more header cells a cell is related to
7072  * @cfg {Number} height Sets the height of a cell
7073  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7074  * @cfg {Number} rowspan Sets the number of rows a cell should span
7075  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7076  * @cfg {String} valign Vertical aligns the content in a cell
7077  * @cfg {Number} width Specifies the width of a cell
7078  * 
7079  * @constructor
7080  * Create a new TableCell
7081  * @param {Object} config The config object
7082  */
7083
7084 Roo.bootstrap.TableCell = function(config){
7085     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7086 };
7087
7088 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7089     
7090     html: false,
7091     cls: false,
7092     tag: false,
7093     abbr: false,
7094     align: false,
7095     axis: false,
7096     bgcolor: false,
7097     charoff: false,
7098     colspan: false,
7099     headers: false,
7100     height: false,
7101     nowrap: false,
7102     rowspan: false,
7103     scope: false,
7104     valign: false,
7105     width: false,
7106     
7107     
7108     getAutoCreate : function(){
7109         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7110         
7111         cfg = {
7112             tag: 'td'
7113         };
7114         
7115         if(this.tag){
7116             cfg.tag = this.tag;
7117         }
7118         
7119         if (this.html) {
7120             cfg.html=this.html
7121         }
7122         if (this.cls) {
7123             cfg.cls=this.cls
7124         }
7125         if (this.abbr) {
7126             cfg.abbr=this.abbr
7127         }
7128         if (this.align) {
7129             cfg.align=this.align
7130         }
7131         if (this.axis) {
7132             cfg.axis=this.axis
7133         }
7134         if (this.bgcolor) {
7135             cfg.bgcolor=this.bgcolor
7136         }
7137         if (this.charoff) {
7138             cfg.charoff=this.charoff
7139         }
7140         if (this.colspan) {
7141             cfg.colspan=this.colspan
7142         }
7143         if (this.headers) {
7144             cfg.headers=this.headers
7145         }
7146         if (this.height) {
7147             cfg.height=this.height
7148         }
7149         if (this.nowrap) {
7150             cfg.nowrap=this.nowrap
7151         }
7152         if (this.rowspan) {
7153             cfg.rowspan=this.rowspan
7154         }
7155         if (this.scope) {
7156             cfg.scope=this.scope
7157         }
7158         if (this.valign) {
7159             cfg.valign=this.valign
7160         }
7161         if (this.width) {
7162             cfg.width=this.width
7163         }
7164         
7165         
7166         return cfg;
7167     }
7168    
7169 });
7170
7171  
7172
7173  /*
7174  * - LGPL
7175  *
7176  * table row
7177  * 
7178  */
7179
7180 /**
7181  * @class Roo.bootstrap.TableRow
7182  * @extends Roo.bootstrap.Component
7183  * Bootstrap TableRow class
7184  * @cfg {String} cls row class
7185  * @cfg {String} align Aligns the content in a table row
7186  * @cfg {String} bgcolor Specifies a background color for a table row
7187  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7188  * @cfg {String} valign Vertical aligns the content in a table row
7189  * 
7190  * @constructor
7191  * Create a new TableRow
7192  * @param {Object} config The config object
7193  */
7194
7195 Roo.bootstrap.TableRow = function(config){
7196     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7197 };
7198
7199 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7200     
7201     cls: false,
7202     align: false,
7203     bgcolor: false,
7204     charoff: false,
7205     valign: false,
7206     
7207     getAutoCreate : function(){
7208         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7209         
7210         cfg = {
7211             tag: 'tr'
7212         };
7213             
7214         if(this.cls){
7215             cfg.cls = this.cls;
7216         }
7217         if(this.align){
7218             cfg.align = this.align;
7219         }
7220         if(this.bgcolor){
7221             cfg.bgcolor = this.bgcolor;
7222         }
7223         if(this.charoff){
7224             cfg.charoff = this.charoff;
7225         }
7226         if(this.valign){
7227             cfg.valign = this.valign;
7228         }
7229         
7230         return cfg;
7231     }
7232    
7233 });
7234
7235  
7236
7237  /*
7238  * - LGPL
7239  *
7240  * table body
7241  * 
7242  */
7243
7244 /**
7245  * @class Roo.bootstrap.TableBody
7246  * @extends Roo.bootstrap.Component
7247  * Bootstrap TableBody class
7248  * @cfg {String} cls element class
7249  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7250  * @cfg {String} align Aligns the content inside the element
7251  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7252  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7253  * 
7254  * @constructor
7255  * Create a new TableBody
7256  * @param {Object} config The config object
7257  */
7258
7259 Roo.bootstrap.TableBody = function(config){
7260     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7261 };
7262
7263 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7264     
7265     cls: false,
7266     tag: false,
7267     align: false,
7268     charoff: false,
7269     valign: false,
7270     
7271     getAutoCreate : function(){
7272         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7273         
7274         cfg = {
7275             tag: 'tbody'
7276         };
7277             
7278         if (this.cls) {
7279             cfg.cls=this.cls
7280         }
7281         if(this.tag){
7282             cfg.tag = this.tag;
7283         }
7284         
7285         if(this.align){
7286             cfg.align = this.align;
7287         }
7288         if(this.charoff){
7289             cfg.charoff = this.charoff;
7290         }
7291         if(this.valign){
7292             cfg.valign = this.valign;
7293         }
7294         
7295         return cfg;
7296     }
7297     
7298     
7299 //    initEvents : function()
7300 //    {
7301 //        
7302 //        if(!this.store){
7303 //            return;
7304 //        }
7305 //        
7306 //        this.store = Roo.factory(this.store, Roo.data);
7307 //        this.store.on('load', this.onLoad, this);
7308 //        
7309 //        this.store.load();
7310 //        
7311 //    },
7312 //    
7313 //    onLoad: function () 
7314 //    {   
7315 //        this.fireEvent('load', this);
7316 //    }
7317 //    
7318 //   
7319 });
7320
7321  
7322
7323  /*
7324  * Based on:
7325  * Ext JS Library 1.1.1
7326  * Copyright(c) 2006-2007, Ext JS, LLC.
7327  *
7328  * Originally Released Under LGPL - original licence link has changed is not relivant.
7329  *
7330  * Fork - LGPL
7331  * <script type="text/javascript">
7332  */
7333
7334 // as we use this in bootstrap.
7335 Roo.namespace('Roo.form');
7336  /**
7337  * @class Roo.form.Action
7338  * Internal Class used to handle form actions
7339  * @constructor
7340  * @param {Roo.form.BasicForm} el The form element or its id
7341  * @param {Object} config Configuration options
7342  */
7343
7344  
7345  
7346 // define the action interface
7347 Roo.form.Action = function(form, options){
7348     this.form = form;
7349     this.options = options || {};
7350 };
7351 /**
7352  * Client Validation Failed
7353  * @const 
7354  */
7355 Roo.form.Action.CLIENT_INVALID = 'client';
7356 /**
7357  * Server Validation Failed
7358  * @const 
7359  */
7360 Roo.form.Action.SERVER_INVALID = 'server';
7361  /**
7362  * Connect to Server Failed
7363  * @const 
7364  */
7365 Roo.form.Action.CONNECT_FAILURE = 'connect';
7366 /**
7367  * Reading Data from Server Failed
7368  * @const 
7369  */
7370 Roo.form.Action.LOAD_FAILURE = 'load';
7371
7372 Roo.form.Action.prototype = {
7373     type : 'default',
7374     failureType : undefined,
7375     response : undefined,
7376     result : undefined,
7377
7378     // interface method
7379     run : function(options){
7380
7381     },
7382
7383     // interface method
7384     success : function(response){
7385
7386     },
7387
7388     // interface method
7389     handleResponse : function(response){
7390
7391     },
7392
7393     // default connection failure
7394     failure : function(response){
7395         
7396         this.response = response;
7397         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7398         this.form.afterAction(this, false);
7399     },
7400
7401     processResponse : function(response){
7402         this.response = response;
7403         if(!response.responseText){
7404             return true;
7405         }
7406         this.result = this.handleResponse(response);
7407         return this.result;
7408     },
7409
7410     // utility functions used internally
7411     getUrl : function(appendParams){
7412         var url = this.options.url || this.form.url || this.form.el.dom.action;
7413         if(appendParams){
7414             var p = this.getParams();
7415             if(p){
7416                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7417             }
7418         }
7419         return url;
7420     },
7421
7422     getMethod : function(){
7423         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7424     },
7425
7426     getParams : function(){
7427         var bp = this.form.baseParams;
7428         var p = this.options.params;
7429         if(p){
7430             if(typeof p == "object"){
7431                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7432             }else if(typeof p == 'string' && bp){
7433                 p += '&' + Roo.urlEncode(bp);
7434             }
7435         }else if(bp){
7436             p = Roo.urlEncode(bp);
7437         }
7438         return p;
7439     },
7440
7441     createCallback : function(){
7442         return {
7443             success: this.success,
7444             failure: this.failure,
7445             scope: this,
7446             timeout: (this.form.timeout*1000),
7447             upload: this.form.fileUpload ? this.success : undefined
7448         };
7449     }
7450 };
7451
7452 Roo.form.Action.Submit = function(form, options){
7453     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7454 };
7455
7456 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7457     type : 'submit',
7458
7459     haveProgress : false,
7460     uploadComplete : false,
7461     
7462     // uploadProgress indicator.
7463     uploadProgress : function()
7464     {
7465         if (!this.form.progressUrl) {
7466             return;
7467         }
7468         
7469         if (!this.haveProgress) {
7470             Roo.MessageBox.progress("Uploading", "Uploading");
7471         }
7472         if (this.uploadComplete) {
7473            Roo.MessageBox.hide();
7474            return;
7475         }
7476         
7477         this.haveProgress = true;
7478    
7479         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7480         
7481         var c = new Roo.data.Connection();
7482         c.request({
7483             url : this.form.progressUrl,
7484             params: {
7485                 id : uid
7486             },
7487             method: 'GET',
7488             success : function(req){
7489                //console.log(data);
7490                 var rdata = false;
7491                 var edata;
7492                 try  {
7493                    rdata = Roo.decode(req.responseText)
7494                 } catch (e) {
7495                     Roo.log("Invalid data from server..");
7496                     Roo.log(edata);
7497                     return;
7498                 }
7499                 if (!rdata || !rdata.success) {
7500                     Roo.log(rdata);
7501                     Roo.MessageBox.alert(Roo.encode(rdata));
7502                     return;
7503                 }
7504                 var data = rdata.data;
7505                 
7506                 if (this.uploadComplete) {
7507                    Roo.MessageBox.hide();
7508                    return;
7509                 }
7510                    
7511                 if (data){
7512                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7513                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7514                     );
7515                 }
7516                 this.uploadProgress.defer(2000,this);
7517             },
7518        
7519             failure: function(data) {
7520                 Roo.log('progress url failed ');
7521                 Roo.log(data);
7522             },
7523             scope : this
7524         });
7525            
7526     },
7527     
7528     
7529     run : function()
7530     {
7531         // run get Values on the form, so it syncs any secondary forms.
7532         this.form.getValues();
7533         
7534         var o = this.options;
7535         var method = this.getMethod();
7536         var isPost = method == 'POST';
7537         if(o.clientValidation === false || this.form.isValid()){
7538             
7539             if (this.form.progressUrl) {
7540                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7541                     (new Date() * 1) + '' + Math.random());
7542                     
7543             } 
7544             
7545             
7546             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7547                 form:this.form.el.dom,
7548                 url:this.getUrl(!isPost),
7549                 method: method,
7550                 params:isPost ? this.getParams() : null,
7551                 isUpload: this.form.fileUpload
7552             }));
7553             
7554             this.uploadProgress();
7555
7556         }else if (o.clientValidation !== false){ // client validation failed
7557             this.failureType = Roo.form.Action.CLIENT_INVALID;
7558             this.form.afterAction(this, false);
7559         }
7560     },
7561
7562     success : function(response)
7563     {
7564         this.uploadComplete= true;
7565         if (this.haveProgress) {
7566             Roo.MessageBox.hide();
7567         }
7568         
7569         
7570         var result = this.processResponse(response);
7571         if(result === true || result.success){
7572             this.form.afterAction(this, true);
7573             return;
7574         }
7575         if(result.errors){
7576             this.form.markInvalid(result.errors);
7577             this.failureType = Roo.form.Action.SERVER_INVALID;
7578         }
7579         this.form.afterAction(this, false);
7580     },
7581     failure : function(response)
7582     {
7583         this.uploadComplete= true;
7584         if (this.haveProgress) {
7585             Roo.MessageBox.hide();
7586         }
7587         
7588         this.response = response;
7589         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7590         this.form.afterAction(this, false);
7591     },
7592     
7593     handleResponse : function(response){
7594         if(this.form.errorReader){
7595             var rs = this.form.errorReader.read(response);
7596             var errors = [];
7597             if(rs.records){
7598                 for(var i = 0, len = rs.records.length; i < len; i++) {
7599                     var r = rs.records[i];
7600                     errors[i] = r.data;
7601                 }
7602             }
7603             if(errors.length < 1){
7604                 errors = null;
7605             }
7606             return {
7607                 success : rs.success,
7608                 errors : errors
7609             };
7610         }
7611         var ret = false;
7612         try {
7613             ret = Roo.decode(response.responseText);
7614         } catch (e) {
7615             ret = {
7616                 success: false,
7617                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7618                 errors : []
7619             };
7620         }
7621         return ret;
7622         
7623     }
7624 });
7625
7626
7627 Roo.form.Action.Load = function(form, options){
7628     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7629     this.reader = this.form.reader;
7630 };
7631
7632 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7633     type : 'load',
7634
7635     run : function(){
7636         
7637         Roo.Ajax.request(Roo.apply(
7638                 this.createCallback(), {
7639                     method:this.getMethod(),
7640                     url:this.getUrl(false),
7641                     params:this.getParams()
7642         }));
7643     },
7644
7645     success : function(response){
7646         
7647         var result = this.processResponse(response);
7648         if(result === true || !result.success || !result.data){
7649             this.failureType = Roo.form.Action.LOAD_FAILURE;
7650             this.form.afterAction(this, false);
7651             return;
7652         }
7653         this.form.clearInvalid();
7654         this.form.setValues(result.data);
7655         this.form.afterAction(this, true);
7656     },
7657
7658     handleResponse : function(response){
7659         if(this.form.reader){
7660             var rs = this.form.reader.read(response);
7661             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7662             return {
7663                 success : rs.success,
7664                 data : data
7665             };
7666         }
7667         return Roo.decode(response.responseText);
7668     }
7669 });
7670
7671 Roo.form.Action.ACTION_TYPES = {
7672     'load' : Roo.form.Action.Load,
7673     'submit' : Roo.form.Action.Submit
7674 };/*
7675  * - LGPL
7676  *
7677  * form
7678  *
7679  */
7680
7681 /**
7682  * @class Roo.bootstrap.Form
7683  * @extends Roo.bootstrap.Component
7684  * Bootstrap Form class
7685  * @cfg {String} method  GET | POST (default POST)
7686  * @cfg {String} labelAlign top | left (default top)
7687  * @cfg {String} align left  | right - for navbars
7688  * @cfg {Boolean} loadMask load mask when submit (default true)
7689
7690  *
7691  * @constructor
7692  * Create a new Form
7693  * @param {Object} config The config object
7694  */
7695
7696
7697 Roo.bootstrap.Form = function(config){
7698     
7699     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7700     
7701     Roo.bootstrap.Form.popover.apply();
7702     
7703     this.addEvents({
7704         /**
7705          * @event clientvalidation
7706          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7707          * @param {Form} this
7708          * @param {Boolean} valid true if the form has passed client-side validation
7709          */
7710         clientvalidation: true,
7711         /**
7712          * @event beforeaction
7713          * Fires before any action is performed. Return false to cancel the action.
7714          * @param {Form} this
7715          * @param {Action} action The action to be performed
7716          */
7717         beforeaction: true,
7718         /**
7719          * @event actionfailed
7720          * Fires when an action fails.
7721          * @param {Form} this
7722          * @param {Action} action The action that failed
7723          */
7724         actionfailed : true,
7725         /**
7726          * @event actioncomplete
7727          * Fires when an action is completed.
7728          * @param {Form} this
7729          * @param {Action} action The action that completed
7730          */
7731         actioncomplete : true
7732     });
7733 };
7734
7735 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7736
7737      /**
7738      * @cfg {String} method
7739      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7740      */
7741     method : 'POST',
7742     /**
7743      * @cfg {String} url
7744      * The URL to use for form actions if one isn't supplied in the action options.
7745      */
7746     /**
7747      * @cfg {Boolean} fileUpload
7748      * Set to true if this form is a file upload.
7749      */
7750
7751     /**
7752      * @cfg {Object} baseParams
7753      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7754      */
7755
7756     /**
7757      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7758      */
7759     timeout: 30,
7760     /**
7761      * @cfg {Sting} align (left|right) for navbar forms
7762      */
7763     align : 'left',
7764
7765     // private
7766     activeAction : null,
7767
7768     /**
7769      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7770      * element by passing it or its id or mask the form itself by passing in true.
7771      * @type Mixed
7772      */
7773     waitMsgTarget : false,
7774
7775     loadMask : true,
7776     
7777     /**
7778      * @cfg {Boolean} errorMask (true|false) default false
7779      */
7780     errorMask : false,
7781     
7782     /**
7783      * @cfg {Number} maskOffset Default 100
7784      */
7785     maskOffset : 100,
7786     
7787     /**
7788      * @cfg {Boolean} maskBody
7789      */
7790     maskBody : false,
7791
7792     getAutoCreate : function(){
7793
7794         var cfg = {
7795             tag: 'form',
7796             method : this.method || 'POST',
7797             id : this.id || Roo.id(),
7798             cls : ''
7799         };
7800         if (this.parent().xtype.match(/^Nav/)) {
7801             cfg.cls = 'navbar-form navbar-' + this.align;
7802
7803         }
7804
7805         if (this.labelAlign == 'left' ) {
7806             cfg.cls += ' form-horizontal';
7807         }
7808
7809
7810         return cfg;
7811     },
7812     initEvents : function()
7813     {
7814         this.el.on('submit', this.onSubmit, this);
7815         // this was added as random key presses on the form where triggering form submit.
7816         this.el.on('keypress', function(e) {
7817             if (e.getCharCode() != 13) {
7818                 return true;
7819             }
7820             // we might need to allow it for textareas.. and some other items.
7821             // check e.getTarget().
7822
7823             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7824                 return true;
7825             }
7826
7827             Roo.log("keypress blocked");
7828
7829             e.preventDefault();
7830             return false;
7831         });
7832         
7833     },
7834     // private
7835     onSubmit : function(e){
7836         e.stopEvent();
7837     },
7838
7839      /**
7840      * Returns true if client-side validation on the form is successful.
7841      * @return Boolean
7842      */
7843     isValid : function(){
7844         var items = this.getItems();
7845         var valid = true;
7846         var target = false;
7847         
7848         items.each(function(f){
7849             
7850             if(f.validate()){
7851                 return;
7852             }
7853             
7854             Roo.log('invalid field: ' + f.name);
7855             
7856             valid = false;
7857
7858             if(!target && f.el.isVisible(true)){
7859                 target = f;
7860             }
7861            
7862         });
7863         
7864         if(this.errorMask && !valid){
7865             Roo.bootstrap.Form.popover.mask(this, target);
7866         }
7867         
7868         return valid;
7869     },
7870     
7871     /**
7872      * Returns true if any fields in this form have changed since their original load.
7873      * @return Boolean
7874      */
7875     isDirty : function(){
7876         var dirty = false;
7877         var items = this.getItems();
7878         items.each(function(f){
7879            if(f.isDirty()){
7880                dirty = true;
7881                return false;
7882            }
7883            return true;
7884         });
7885         return dirty;
7886     },
7887      /**
7888      * Performs a predefined action (submit or load) or custom actions you define on this form.
7889      * @param {String} actionName The name of the action type
7890      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7891      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7892      * accept other config options):
7893      * <pre>
7894 Property          Type             Description
7895 ----------------  ---------------  ----------------------------------------------------------------------------------
7896 url               String           The url for the action (defaults to the form's url)
7897 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7898 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7899 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7900                                    validate the form on the client (defaults to false)
7901      * </pre>
7902      * @return {BasicForm} this
7903      */
7904     doAction : function(action, options){
7905         if(typeof action == 'string'){
7906             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7907         }
7908         if(this.fireEvent('beforeaction', this, action) !== false){
7909             this.beforeAction(action);
7910             action.run.defer(100, action);
7911         }
7912         return this;
7913     },
7914
7915     // private
7916     beforeAction : function(action){
7917         var o = action.options;
7918         
7919         if(this.loadMask){
7920             
7921             if(this.maskBody){
7922                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7923             } else {
7924                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7925             }
7926         }
7927         // not really supported yet.. ??
7928
7929         //if(this.waitMsgTarget === true){
7930         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7931         //}else if(this.waitMsgTarget){
7932         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7933         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7934         //}else {
7935         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7936        // }
7937
7938     },
7939
7940     // private
7941     afterAction : function(action, success){
7942         this.activeAction = null;
7943         var o = action.options;
7944
7945         if(this.loadMask){
7946             
7947             if(this.maskBody){
7948                 Roo.get(document.body).unmask();
7949             } else {
7950                 this.el.unmask();
7951             }
7952         }
7953         
7954         //if(this.waitMsgTarget === true){
7955 //            this.el.unmask();
7956         //}else if(this.waitMsgTarget){
7957         //    this.waitMsgTarget.unmask();
7958         //}else{
7959         //    Roo.MessageBox.updateProgress(1);
7960         //    Roo.MessageBox.hide();
7961        // }
7962         //
7963         if(success){
7964             if(o.reset){
7965                 this.reset();
7966             }
7967             Roo.callback(o.success, o.scope, [this, action]);
7968             this.fireEvent('actioncomplete', this, action);
7969
7970         }else{
7971
7972             // failure condition..
7973             // we have a scenario where updates need confirming.
7974             // eg. if a locking scenario exists..
7975             // we look for { errors : { needs_confirm : true }} in the response.
7976             if (
7977                 (typeof(action.result) != 'undefined')  &&
7978                 (typeof(action.result.errors) != 'undefined')  &&
7979                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7980            ){
7981                 var _t = this;
7982                 Roo.log("not supported yet");
7983                  /*
7984
7985                 Roo.MessageBox.confirm(
7986                     "Change requires confirmation",
7987                     action.result.errorMsg,
7988                     function(r) {
7989                         if (r != 'yes') {
7990                             return;
7991                         }
7992                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7993                     }
7994
7995                 );
7996                 */
7997
7998
7999                 return;
8000             }
8001
8002             Roo.callback(o.failure, o.scope, [this, action]);
8003             // show an error message if no failed handler is set..
8004             if (!this.hasListener('actionfailed')) {
8005                 Roo.log("need to add dialog support");
8006                 /*
8007                 Roo.MessageBox.alert("Error",
8008                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8009                         action.result.errorMsg :
8010                         "Saving Failed, please check your entries or try again"
8011                 );
8012                 */
8013             }
8014
8015             this.fireEvent('actionfailed', this, action);
8016         }
8017
8018     },
8019     /**
8020      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8021      * @param {String} id The value to search for
8022      * @return Field
8023      */
8024     findField : function(id){
8025         var items = this.getItems();
8026         var field = items.get(id);
8027         if(!field){
8028              items.each(function(f){
8029                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8030                     field = f;
8031                     return false;
8032                 }
8033                 return true;
8034             });
8035         }
8036         return field || null;
8037     },
8038      /**
8039      * Mark fields in this form invalid in bulk.
8040      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8041      * @return {BasicForm} this
8042      */
8043     markInvalid : function(errors){
8044         if(errors instanceof Array){
8045             for(var i = 0, len = errors.length; i < len; i++){
8046                 var fieldError = errors[i];
8047                 var f = this.findField(fieldError.id);
8048                 if(f){
8049                     f.markInvalid(fieldError.msg);
8050                 }
8051             }
8052         }else{
8053             var field, id;
8054             for(id in errors){
8055                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8056                     field.markInvalid(errors[id]);
8057                 }
8058             }
8059         }
8060         //Roo.each(this.childForms || [], function (f) {
8061         //    f.markInvalid(errors);
8062         //});
8063
8064         return this;
8065     },
8066
8067     /**
8068      * Set values for fields in this form in bulk.
8069      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8070      * @return {BasicForm} this
8071      */
8072     setValues : function(values){
8073         if(values instanceof Array){ // array of objects
8074             for(var i = 0, len = values.length; i < len; i++){
8075                 var v = values[i];
8076                 var f = this.findField(v.id);
8077                 if(f){
8078                     f.setValue(v.value);
8079                     if(this.trackResetOnLoad){
8080                         f.originalValue = f.getValue();
8081                     }
8082                 }
8083             }
8084         }else{ // object hash
8085             var field, id;
8086             for(id in values){
8087                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8088
8089                     if (field.setFromData &&
8090                         field.valueField &&
8091                         field.displayField &&
8092                         // combos' with local stores can
8093                         // be queried via setValue()
8094                         // to set their value..
8095                         (field.store && !field.store.isLocal)
8096                         ) {
8097                         // it's a combo
8098                         var sd = { };
8099                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8100                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8101                         field.setFromData(sd);
8102
8103                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8104                         
8105                         field.setFromData(values);
8106                         
8107                     } else {
8108                         field.setValue(values[id]);
8109                     }
8110
8111
8112                     if(this.trackResetOnLoad){
8113                         field.originalValue = field.getValue();
8114                     }
8115                 }
8116             }
8117         }
8118
8119         //Roo.each(this.childForms || [], function (f) {
8120         //    f.setValues(values);
8121         //});
8122
8123         return this;
8124     },
8125
8126     /**
8127      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8128      * they are returned as an array.
8129      * @param {Boolean} asString
8130      * @return {Object}
8131      */
8132     getValues : function(asString){
8133         //if (this.childForms) {
8134             // copy values from the child forms
8135         //    Roo.each(this.childForms, function (f) {
8136         //        this.setValues(f.getValues());
8137         //    }, this);
8138         //}
8139
8140
8141
8142         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8143         if(asString === true){
8144             return fs;
8145         }
8146         return Roo.urlDecode(fs);
8147     },
8148
8149     /**
8150      * Returns the fields in this form as an object with key/value pairs.
8151      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8152      * @return {Object}
8153      */
8154     getFieldValues : function(with_hidden)
8155     {
8156         var items = this.getItems();
8157         var ret = {};
8158         items.each(function(f){
8159             
8160             if (!f.getName()) {
8161                 return;
8162             }
8163             
8164             var v = f.getValue();
8165             
8166             if (f.inputType =='radio') {
8167                 if (typeof(ret[f.getName()]) == 'undefined') {
8168                     ret[f.getName()] = ''; // empty..
8169                 }
8170
8171                 if (!f.el.dom.checked) {
8172                     return;
8173
8174                 }
8175                 v = f.el.dom.value;
8176
8177             }
8178             
8179             if(f.xtype == 'MoneyField'){
8180                 ret[f.currencyName] = f.getCurrency();
8181             }
8182
8183             // not sure if this supported any more..
8184             if ((typeof(v) == 'object') && f.getRawValue) {
8185                 v = f.getRawValue() ; // dates..
8186             }
8187             // combo boxes where name != hiddenName...
8188             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8189                 ret[f.name] = f.getRawValue();
8190             }
8191             ret[f.getName()] = v;
8192         });
8193
8194         return ret;
8195     },
8196
8197     /**
8198      * Clears all invalid messages in this form.
8199      * @return {BasicForm} this
8200      */
8201     clearInvalid : function(){
8202         var items = this.getItems();
8203
8204         items.each(function(f){
8205            f.clearInvalid();
8206         });
8207
8208         return this;
8209     },
8210
8211     /**
8212      * Resets this form.
8213      * @return {BasicForm} this
8214      */
8215     reset : function(){
8216         var items = this.getItems();
8217         items.each(function(f){
8218             f.reset();
8219         });
8220
8221         Roo.each(this.childForms || [], function (f) {
8222             f.reset();
8223         });
8224
8225
8226         return this;
8227     },
8228     
8229     getItems : function()
8230     {
8231         var r=new Roo.util.MixedCollection(false, function(o){
8232             return o.id || (o.id = Roo.id());
8233         });
8234         var iter = function(el) {
8235             if (el.inputEl) {
8236                 r.add(el);
8237             }
8238             if (!el.items) {
8239                 return;
8240             }
8241             Roo.each(el.items,function(e) {
8242                 iter(e);
8243             });
8244         };
8245
8246         iter(this);
8247         return r;
8248     },
8249     
8250     hideFields : function(items)
8251     {
8252         Roo.each(items, function(i){
8253             
8254             var f = this.findField(i);
8255             
8256             if(!f){
8257                 return;
8258             }
8259             
8260             if(f.xtype == 'DateField'){
8261                 f.setVisible(false);
8262                 return;
8263             }
8264             
8265             f.hide();
8266             
8267         }, this);
8268     },
8269     
8270     showFields : function(items)
8271     {
8272         Roo.each(items, function(i){
8273             
8274             var f = this.findField(i);
8275             
8276             if(!f){
8277                 return;
8278             }
8279             
8280             if(f.xtype == 'DateField'){
8281                 f.setVisible(true);
8282                 return;
8283             }
8284             
8285             f.show();
8286             
8287         }, this);
8288     }
8289
8290 });
8291
8292 Roo.apply(Roo.bootstrap.Form, {
8293     
8294     popover : {
8295         
8296         padding : 5,
8297         
8298         isApplied : false,
8299         
8300         isMasked : false,
8301         
8302         form : false,
8303         
8304         target : false,
8305         
8306         toolTip : false,
8307         
8308         intervalID : false,
8309         
8310         maskEl : false,
8311         
8312         apply : function()
8313         {
8314             if(this.isApplied){
8315                 return;
8316             }
8317             
8318             this.maskEl = {
8319                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8320                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8321                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8322                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8323             };
8324             
8325             this.maskEl.top.enableDisplayMode("block");
8326             this.maskEl.left.enableDisplayMode("block");
8327             this.maskEl.bottom.enableDisplayMode("block");
8328             this.maskEl.right.enableDisplayMode("block");
8329             
8330             this.toolTip = new Roo.bootstrap.Tooltip({
8331                 cls : 'roo-form-error-popover',
8332                 alignment : {
8333                     'left' : ['r-l', [-2,0], 'right'],
8334                     'right' : ['l-r', [2,0], 'left'],
8335                     'bottom' : ['tl-bl', [0,2], 'top'],
8336                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8337                 }
8338             });
8339             
8340             this.toolTip.render(Roo.get(document.body));
8341
8342             this.toolTip.el.enableDisplayMode("block");
8343             
8344             Roo.get(document.body).on('click', function(){
8345                 this.unmask();
8346             }, this);
8347             
8348             Roo.get(document.body).on('touchstart', function(){
8349                 this.unmask();
8350             }, this);
8351             
8352             this.isApplied = true
8353         },
8354         
8355         mask : function(form, target)
8356         {
8357             this.form = form;
8358             
8359             this.target = target;
8360             
8361             if(!this.form.errorMask || !target.el){
8362                 return;
8363             }
8364             
8365             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8366             
8367             Roo.log(scrollable);
8368             
8369             var ot = this.target.el.calcOffsetsTo(scrollable);
8370             
8371             var scrollTo = ot[1] - this.form.maskOffset;
8372             
8373             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8374             
8375             scrollable.scrollTo('top', scrollTo);
8376             
8377             var box = this.target.el.getBox();
8378             Roo.log(box);
8379             var zIndex = Roo.bootstrap.Modal.zIndex++;
8380
8381             
8382             this.maskEl.top.setStyle('position', 'absolute');
8383             this.maskEl.top.setStyle('z-index', zIndex);
8384             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8385             this.maskEl.top.setLeft(0);
8386             this.maskEl.top.setTop(0);
8387             this.maskEl.top.show();
8388             
8389             this.maskEl.left.setStyle('position', 'absolute');
8390             this.maskEl.left.setStyle('z-index', zIndex);
8391             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8392             this.maskEl.left.setLeft(0);
8393             this.maskEl.left.setTop(box.y - this.padding);
8394             this.maskEl.left.show();
8395
8396             this.maskEl.bottom.setStyle('position', 'absolute');
8397             this.maskEl.bottom.setStyle('z-index', zIndex);
8398             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8399             this.maskEl.bottom.setLeft(0);
8400             this.maskEl.bottom.setTop(box.bottom + this.padding);
8401             this.maskEl.bottom.show();
8402
8403             this.maskEl.right.setStyle('position', 'absolute');
8404             this.maskEl.right.setStyle('z-index', zIndex);
8405             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8406             this.maskEl.right.setLeft(box.right + this.padding);
8407             this.maskEl.right.setTop(box.y - this.padding);
8408             this.maskEl.right.show();
8409
8410             this.toolTip.bindEl = this.target.el;
8411
8412             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8413
8414             var tip = this.target.blankText;
8415
8416             if(this.target.getValue() !== '' ) {
8417                 
8418                 if (this.target.invalidText.length) {
8419                     tip = this.target.invalidText;
8420                 } else if (this.target.regexText.length){
8421                     tip = this.target.regexText;
8422                 }
8423             }
8424
8425             this.toolTip.show(tip);
8426
8427             this.intervalID = window.setInterval(function() {
8428                 Roo.bootstrap.Form.popover.unmask();
8429             }, 10000);
8430
8431             window.onwheel = function(){ return false;};
8432             
8433             (function(){ this.isMasked = true; }).defer(500, this);
8434             
8435         },
8436         
8437         unmask : function()
8438         {
8439             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8440                 return;
8441             }
8442             
8443             this.maskEl.top.setStyle('position', 'absolute');
8444             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8445             this.maskEl.top.hide();
8446
8447             this.maskEl.left.setStyle('position', 'absolute');
8448             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8449             this.maskEl.left.hide();
8450
8451             this.maskEl.bottom.setStyle('position', 'absolute');
8452             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8453             this.maskEl.bottom.hide();
8454
8455             this.maskEl.right.setStyle('position', 'absolute');
8456             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8457             this.maskEl.right.hide();
8458             
8459             this.toolTip.hide();
8460             
8461             this.toolTip.el.hide();
8462             
8463             window.onwheel = function(){ return true;};
8464             
8465             if(this.intervalID){
8466                 window.clearInterval(this.intervalID);
8467                 this.intervalID = false;
8468             }
8469             
8470             this.isMasked = false;
8471             
8472         }
8473         
8474     }
8475     
8476 });
8477
8478 /*
8479  * Based on:
8480  * Ext JS Library 1.1.1
8481  * Copyright(c) 2006-2007, Ext JS, LLC.
8482  *
8483  * Originally Released Under LGPL - original licence link has changed is not relivant.
8484  *
8485  * Fork - LGPL
8486  * <script type="text/javascript">
8487  */
8488 /**
8489  * @class Roo.form.VTypes
8490  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8491  * @singleton
8492  */
8493 Roo.form.VTypes = function(){
8494     // closure these in so they are only created once.
8495     var alpha = /^[a-zA-Z_]+$/;
8496     var alphanum = /^[a-zA-Z0-9_]+$/;
8497     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8498     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8499
8500     // All these messages and functions are configurable
8501     return {
8502         /**
8503          * The function used to validate email addresses
8504          * @param {String} value The email address
8505          */
8506         'email' : function(v){
8507             return email.test(v);
8508         },
8509         /**
8510          * The error text to display when the email validation function returns false
8511          * @type String
8512          */
8513         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8514         /**
8515          * The keystroke filter mask to be applied on email input
8516          * @type RegExp
8517          */
8518         'emailMask' : /[a-z0-9_\.\-@]/i,
8519
8520         /**
8521          * The function used to validate URLs
8522          * @param {String} value The URL
8523          */
8524         'url' : function(v){
8525             return url.test(v);
8526         },
8527         /**
8528          * The error text to display when the url validation function returns false
8529          * @type String
8530          */
8531         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8532         
8533         /**
8534          * The function used to validate alpha values
8535          * @param {String} value The value
8536          */
8537         'alpha' : function(v){
8538             return alpha.test(v);
8539         },
8540         /**
8541          * The error text to display when the alpha validation function returns false
8542          * @type String
8543          */
8544         'alphaText' : 'This field should only contain letters and _',
8545         /**
8546          * The keystroke filter mask to be applied on alpha input
8547          * @type RegExp
8548          */
8549         'alphaMask' : /[a-z_]/i,
8550
8551         /**
8552          * The function used to validate alphanumeric values
8553          * @param {String} value The value
8554          */
8555         'alphanum' : function(v){
8556             return alphanum.test(v);
8557         },
8558         /**
8559          * The error text to display when the alphanumeric validation function returns false
8560          * @type String
8561          */
8562         'alphanumText' : 'This field should only contain letters, numbers and _',
8563         /**
8564          * The keystroke filter mask to be applied on alphanumeric input
8565          * @type RegExp
8566          */
8567         'alphanumMask' : /[a-z0-9_]/i
8568     };
8569 }();/*
8570  * - LGPL
8571  *
8572  * Input
8573  * 
8574  */
8575
8576 /**
8577  * @class Roo.bootstrap.Input
8578  * @extends Roo.bootstrap.Component
8579  * Bootstrap Input class
8580  * @cfg {Boolean} disabled is it disabled
8581  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8582  * @cfg {String} name name of the input
8583  * @cfg {string} fieldLabel - the label associated
8584  * @cfg {string} placeholder - placeholder to put in text.
8585  * @cfg {string}  before - input group add on before
8586  * @cfg {string} after - input group add on after
8587  * @cfg {string} size - (lg|sm) or leave empty..
8588  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8589  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8590  * @cfg {Number} md colspan out of 12 for computer-sized screens
8591  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8592  * @cfg {string} value default value of the input
8593  * @cfg {Number} labelWidth set the width of label 
8594  * @cfg {Number} labellg set the width of label (1-12)
8595  * @cfg {Number} labelmd set the width of label (1-12)
8596  * @cfg {Number} labelsm set the width of label (1-12)
8597  * @cfg {Number} labelxs set the width of label (1-12)
8598  * @cfg {String} labelAlign (top|left)
8599  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8600  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8601  * @cfg {String} indicatorpos (left|right) default left
8602  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8603  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8604
8605  * @cfg {String} align (left|center|right) Default left
8606  * @cfg {Boolean} forceFeedback (true|false) Default false
8607  * 
8608  * @constructor
8609  * Create a new Input
8610  * @param {Object} config The config object
8611  */
8612
8613 Roo.bootstrap.Input = function(config){
8614     
8615     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8616     
8617     this.addEvents({
8618         /**
8619          * @event focus
8620          * Fires when this field receives input focus.
8621          * @param {Roo.form.Field} this
8622          */
8623         focus : true,
8624         /**
8625          * @event blur
8626          * Fires when this field loses input focus.
8627          * @param {Roo.form.Field} this
8628          */
8629         blur : true,
8630         /**
8631          * @event specialkey
8632          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8633          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8634          * @param {Roo.form.Field} this
8635          * @param {Roo.EventObject} e The event object
8636          */
8637         specialkey : true,
8638         /**
8639          * @event change
8640          * Fires just before the field blurs if the field value has changed.
8641          * @param {Roo.form.Field} this
8642          * @param {Mixed} newValue The new value
8643          * @param {Mixed} oldValue The original value
8644          */
8645         change : true,
8646         /**
8647          * @event invalid
8648          * Fires after the field has been marked as invalid.
8649          * @param {Roo.form.Field} this
8650          * @param {String} msg The validation message
8651          */
8652         invalid : true,
8653         /**
8654          * @event valid
8655          * Fires after the field has been validated with no errors.
8656          * @param {Roo.form.Field} this
8657          */
8658         valid : true,
8659          /**
8660          * @event keyup
8661          * Fires after the key up
8662          * @param {Roo.form.Field} this
8663          * @param {Roo.EventObject}  e The event Object
8664          */
8665         keyup : true
8666     });
8667 };
8668
8669 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8670      /**
8671      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8672       automatic validation (defaults to "keyup").
8673      */
8674     validationEvent : "keyup",
8675      /**
8676      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8677      */
8678     validateOnBlur : true,
8679     /**
8680      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8681      */
8682     validationDelay : 250,
8683      /**
8684      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8685      */
8686     focusClass : "x-form-focus",  // not needed???
8687     
8688        
8689     /**
8690      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8691      */
8692     invalidClass : "has-warning",
8693     
8694     /**
8695      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8696      */
8697     validClass : "has-success",
8698     
8699     /**
8700      * @cfg {Boolean} hasFeedback (true|false) default true
8701      */
8702     hasFeedback : true,
8703     
8704     /**
8705      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8706      */
8707     invalidFeedbackClass : "glyphicon-warning-sign",
8708     
8709     /**
8710      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8711      */
8712     validFeedbackClass : "glyphicon-ok",
8713     
8714     /**
8715      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8716      */
8717     selectOnFocus : false,
8718     
8719      /**
8720      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8721      */
8722     maskRe : null,
8723        /**
8724      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8725      */
8726     vtype : null,
8727     
8728       /**
8729      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8730      */
8731     disableKeyFilter : false,
8732     
8733        /**
8734      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8735      */
8736     disabled : false,
8737      /**
8738      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8739      */
8740     allowBlank : true,
8741     /**
8742      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8743      */
8744     blankText : "Please complete this mandatory field",
8745     
8746      /**
8747      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8748      */
8749     minLength : 0,
8750     /**
8751      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8752      */
8753     maxLength : Number.MAX_VALUE,
8754     /**
8755      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8756      */
8757     minLengthText : "The minimum length for this field is {0}",
8758     /**
8759      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8760      */
8761     maxLengthText : "The maximum length for this field is {0}",
8762   
8763     
8764     /**
8765      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8766      * If available, this function will be called only after the basic validators all return true, and will be passed the
8767      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8768      */
8769     validator : null,
8770     /**
8771      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8772      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8773      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8774      */
8775     regex : null,
8776     /**
8777      * @cfg {String} regexText -- Depricated - use Invalid Text
8778      */
8779     regexText : "",
8780     
8781     /**
8782      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8783      */
8784     invalidText : "",
8785     
8786     
8787     
8788     autocomplete: false,
8789     
8790     
8791     fieldLabel : '',
8792     inputType : 'text',
8793     
8794     name : false,
8795     placeholder: false,
8796     before : false,
8797     after : false,
8798     size : false,
8799     hasFocus : false,
8800     preventMark: false,
8801     isFormField : true,
8802     value : '',
8803     labelWidth : 2,
8804     labelAlign : false,
8805     readOnly : false,
8806     align : false,
8807     formatedValue : false,
8808     forceFeedback : false,
8809     
8810     indicatorpos : 'left',
8811     
8812     labellg : 0,
8813     labelmd : 0,
8814     labelsm : 0,
8815     labelxs : 0,
8816     
8817     capture : '',
8818     accept : '',
8819     
8820     parentLabelAlign : function()
8821     {
8822         var parent = this;
8823         while (parent.parent()) {
8824             parent = parent.parent();
8825             if (typeof(parent.labelAlign) !='undefined') {
8826                 return parent.labelAlign;
8827             }
8828         }
8829         return 'left';
8830         
8831     },
8832     
8833     getAutoCreate : function()
8834     {
8835         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8836         
8837         var id = Roo.id();
8838         
8839         var cfg = {};
8840         
8841         if(this.inputType != 'hidden'){
8842             cfg.cls = 'form-group' //input-group
8843         }
8844         
8845         var input =  {
8846             tag: 'input',
8847             id : id,
8848             type : this.inputType,
8849             value : this.value,
8850             cls : 'form-control',
8851             placeholder : this.placeholder || '',
8852             autocomplete : this.autocomplete || 'new-password'
8853         };
8854         
8855         if(this.capture.length){
8856             input.capture = this.capture;
8857         }
8858         
8859         if(this.accept.length){
8860             input.accept = this.accept + "/*";
8861         }
8862         
8863         if(this.align){
8864             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8865         }
8866         
8867         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8868             input.maxLength = this.maxLength;
8869         }
8870         
8871         if (this.disabled) {
8872             input.disabled=true;
8873         }
8874         
8875         if (this.readOnly) {
8876             input.readonly=true;
8877         }
8878         
8879         if (this.name) {
8880             input.name = this.name;
8881         }
8882         
8883         if (this.size) {
8884             input.cls += ' input-' + this.size;
8885         }
8886         
8887         var settings=this;
8888         ['xs','sm','md','lg'].map(function(size){
8889             if (settings[size]) {
8890                 cfg.cls += ' col-' + size + '-' + settings[size];
8891             }
8892         });
8893         
8894         var inputblock = input;
8895         
8896         var feedback = {
8897             tag: 'span',
8898             cls: 'glyphicon form-control-feedback'
8899         };
8900             
8901         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8902             
8903             inputblock = {
8904                 cls : 'has-feedback',
8905                 cn :  [
8906                     input,
8907                     feedback
8908                 ] 
8909             };  
8910         }
8911         
8912         if (this.before || this.after) {
8913             
8914             inputblock = {
8915                 cls : 'input-group',
8916                 cn :  [] 
8917             };
8918             
8919             if (this.before && typeof(this.before) == 'string') {
8920                 
8921                 inputblock.cn.push({
8922                     tag :'span',
8923                     cls : 'roo-input-before input-group-addon',
8924                     html : this.before
8925                 });
8926             }
8927             if (this.before && typeof(this.before) == 'object') {
8928                 this.before = Roo.factory(this.before);
8929                 
8930                 inputblock.cn.push({
8931                     tag :'span',
8932                     cls : 'roo-input-before input-group-' +
8933                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8934                 });
8935             }
8936             
8937             inputblock.cn.push(input);
8938             
8939             if (this.after && typeof(this.after) == 'string') {
8940                 inputblock.cn.push({
8941                     tag :'span',
8942                     cls : 'roo-input-after input-group-addon',
8943                     html : this.after
8944                 });
8945             }
8946             if (this.after && typeof(this.after) == 'object') {
8947                 this.after = Roo.factory(this.after);
8948                 
8949                 inputblock.cn.push({
8950                     tag :'span',
8951                     cls : 'roo-input-after input-group-' +
8952                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8953                 });
8954             }
8955             
8956             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8957                 inputblock.cls += ' has-feedback';
8958                 inputblock.cn.push(feedback);
8959             }
8960         };
8961         
8962         if (align ==='left' && this.fieldLabel.length) {
8963             
8964             cfg.cls += ' roo-form-group-label-left';
8965             
8966             cfg.cn = [
8967                 {
8968                     tag : 'i',
8969                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8970                     tooltip : 'This field is required'
8971                 },
8972                 {
8973                     tag: 'label',
8974                     'for' :  id,
8975                     cls : 'control-label',
8976                     html : this.fieldLabel
8977
8978                 },
8979                 {
8980                     cls : "", 
8981                     cn: [
8982                         inputblock
8983                     ]
8984                 }
8985             ];
8986             
8987             var labelCfg = cfg.cn[1];
8988             var contentCfg = cfg.cn[2];
8989             
8990             if(this.indicatorpos == 'right'){
8991                 cfg.cn = [
8992                     {
8993                         tag: 'label',
8994                         'for' :  id,
8995                         cls : 'control-label',
8996                         cn : [
8997                             {
8998                                 tag : 'span',
8999                                 html : this.fieldLabel
9000                             },
9001                             {
9002                                 tag : 'i',
9003                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9004                                 tooltip : 'This field is required'
9005                             }
9006                         ]
9007                     },
9008                     {
9009                         cls : "",
9010                         cn: [
9011                             inputblock
9012                         ]
9013                     }
9014
9015                 ];
9016                 
9017                 labelCfg = cfg.cn[0];
9018                 contentCfg = cfg.cn[1];
9019             
9020             }
9021             
9022             if(this.labelWidth > 12){
9023                 labelCfg.style = "width: " + this.labelWidth + 'px';
9024             }
9025             
9026             if(this.labelWidth < 13 && this.labelmd == 0){
9027                 this.labelmd = this.labelWidth;
9028             }
9029             
9030             if(this.labellg > 0){
9031                 labelCfg.cls += ' col-lg-' + this.labellg;
9032                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9033             }
9034             
9035             if(this.labelmd > 0){
9036                 labelCfg.cls += ' col-md-' + this.labelmd;
9037                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9038             }
9039             
9040             if(this.labelsm > 0){
9041                 labelCfg.cls += ' col-sm-' + this.labelsm;
9042                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9043             }
9044             
9045             if(this.labelxs > 0){
9046                 labelCfg.cls += ' col-xs-' + this.labelxs;
9047                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9048             }
9049             
9050             
9051         } else if ( this.fieldLabel.length) {
9052                 
9053             cfg.cn = [
9054                 {
9055                     tag : 'i',
9056                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9057                     tooltip : 'This field is required'
9058                 },
9059                 {
9060                     tag: 'label',
9061                    //cls : 'input-group-addon',
9062                     html : this.fieldLabel
9063
9064                 },
9065
9066                inputblock
9067
9068            ];
9069            
9070            if(this.indicatorpos == 'right'){
9071                 
9072                 cfg.cn = [
9073                     {
9074                         tag: 'label',
9075                        //cls : 'input-group-addon',
9076                         html : this.fieldLabel
9077
9078                     },
9079                     {
9080                         tag : 'i',
9081                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9082                         tooltip : 'This field is required'
9083                     },
9084
9085                    inputblock
9086
9087                ];
9088
9089             }
9090
9091         } else {
9092             
9093             cfg.cn = [
9094
9095                     inputblock
9096
9097             ];
9098                 
9099                 
9100         };
9101         
9102         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9103            cfg.cls += ' navbar-form';
9104         }
9105         
9106         if (this.parentType === 'NavGroup') {
9107            cfg.cls += ' navbar-form';
9108            cfg.tag = 'li';
9109         }
9110         
9111         return cfg;
9112         
9113     },
9114     /**
9115      * return the real input element.
9116      */
9117     inputEl: function ()
9118     {
9119         return this.el.select('input.form-control',true).first();
9120     },
9121     
9122     tooltipEl : function()
9123     {
9124         return this.inputEl();
9125     },
9126     
9127     indicatorEl : function()
9128     {
9129         var indicator = this.el.select('i.roo-required-indicator',true).first();
9130         
9131         if(!indicator){
9132             return false;
9133         }
9134         
9135         return indicator;
9136         
9137     },
9138     
9139     setDisabled : function(v)
9140     {
9141         var i  = this.inputEl().dom;
9142         if (!v) {
9143             i.removeAttribute('disabled');
9144             return;
9145             
9146         }
9147         i.setAttribute('disabled','true');
9148     },
9149     initEvents : function()
9150     {
9151           
9152         this.inputEl().on("keydown" , this.fireKey,  this);
9153         this.inputEl().on("focus", this.onFocus,  this);
9154         this.inputEl().on("blur", this.onBlur,  this);
9155         
9156         this.inputEl().relayEvent('keyup', this);
9157         
9158         this.indicator = this.indicatorEl();
9159         
9160         if(this.indicator){
9161             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9162         }
9163  
9164         // reference to original value for reset
9165         this.originalValue = this.getValue();
9166         //Roo.form.TextField.superclass.initEvents.call(this);
9167         if(this.validationEvent == 'keyup'){
9168             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9169             this.inputEl().on('keyup', this.filterValidation, this);
9170         }
9171         else if(this.validationEvent !== false){
9172             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9173         }
9174         
9175         if(this.selectOnFocus){
9176             this.on("focus", this.preFocus, this);
9177             
9178         }
9179         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9180             this.inputEl().on("keypress", this.filterKeys, this);
9181         } else {
9182             this.inputEl().relayEvent('keypress', this);
9183         }
9184        /* if(this.grow){
9185             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9186             this.el.on("click", this.autoSize,  this);
9187         }
9188         */
9189         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9190             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9191         }
9192         
9193         if (typeof(this.before) == 'object') {
9194             this.before.render(this.el.select('.roo-input-before',true).first());
9195         }
9196         if (typeof(this.after) == 'object') {
9197             this.after.render(this.el.select('.roo-input-after',true).first());
9198         }
9199         
9200         this.inputEl().on('change', this.onChange, this);
9201         
9202     },
9203     filterValidation : function(e){
9204         if(!e.isNavKeyPress()){
9205             this.validationTask.delay(this.validationDelay);
9206         }
9207     },
9208      /**
9209      * Validates the field value
9210      * @return {Boolean} True if the value is valid, else false
9211      */
9212     validate : function(){
9213         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9214         if(this.disabled || this.validateValue(this.getRawValue())){
9215             this.markValid();
9216             return true;
9217         }
9218         
9219         this.markInvalid();
9220         return false;
9221     },
9222     
9223     
9224     /**
9225      * Validates a value according to the field's validation rules and marks the field as invalid
9226      * if the validation fails
9227      * @param {Mixed} value The value to validate
9228      * @return {Boolean} True if the value is valid, else false
9229      */
9230     validateValue : function(value)
9231     {
9232         if(this.getVisibilityEl().hasClass('hidden')){
9233             return true;
9234         }
9235         
9236         if(value.length < 1)  { // if it's blank
9237             if(this.allowBlank){
9238                 return true;
9239             }
9240             return false;
9241         }
9242         
9243         if(value.length < this.minLength){
9244             return false;
9245         }
9246         if(value.length > this.maxLength){
9247             return false;
9248         }
9249         if(this.vtype){
9250             var vt = Roo.form.VTypes;
9251             if(!vt[this.vtype](value, this)){
9252                 return false;
9253             }
9254         }
9255         if(typeof this.validator == "function"){
9256             var msg = this.validator(value);
9257             if(msg !== true){
9258                 return false;
9259             }
9260             if (typeof(msg) == 'string') {
9261                 this.invalidText = msg;
9262             }
9263         }
9264         
9265         if(this.regex && !this.regex.test(value)){
9266             return false;
9267         }
9268         
9269         return true;
9270     },
9271     
9272      // private
9273     fireKey : function(e){
9274         //Roo.log('field ' + e.getKey());
9275         if(e.isNavKeyPress()){
9276             this.fireEvent("specialkey", this, e);
9277         }
9278     },
9279     focus : function (selectText){
9280         if(this.rendered){
9281             this.inputEl().focus();
9282             if(selectText === true){
9283                 this.inputEl().dom.select();
9284             }
9285         }
9286         return this;
9287     } ,
9288     
9289     onFocus : function(){
9290         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9291            // this.el.addClass(this.focusClass);
9292         }
9293         if(!this.hasFocus){
9294             this.hasFocus = true;
9295             this.startValue = this.getValue();
9296             this.fireEvent("focus", this);
9297         }
9298     },
9299     
9300     beforeBlur : Roo.emptyFn,
9301
9302     
9303     // private
9304     onBlur : function(){
9305         this.beforeBlur();
9306         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9307             //this.el.removeClass(this.focusClass);
9308         }
9309         this.hasFocus = false;
9310         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9311             this.validate();
9312         }
9313         var v = this.getValue();
9314         if(String(v) !== String(this.startValue)){
9315             this.fireEvent('change', this, v, this.startValue);
9316         }
9317         this.fireEvent("blur", this);
9318     },
9319     
9320     onChange : function(e)
9321     {
9322         var v = this.getValue();
9323         if(String(v) !== String(this.startValue)){
9324             this.fireEvent('change', this, v, this.startValue);
9325         }
9326         
9327     },
9328     
9329     /**
9330      * Resets the current field value to the originally loaded value and clears any validation messages
9331      */
9332     reset : function(){
9333         this.setValue(this.originalValue);
9334         this.validate();
9335     },
9336      /**
9337      * Returns the name of the field
9338      * @return {Mixed} name The name field
9339      */
9340     getName: function(){
9341         return this.name;
9342     },
9343      /**
9344      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9345      * @return {Mixed} value The field value
9346      */
9347     getValue : function(){
9348         
9349         var v = this.inputEl().getValue();
9350         
9351         return v;
9352     },
9353     /**
9354      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9355      * @return {Mixed} value The field value
9356      */
9357     getRawValue : function(){
9358         var v = this.inputEl().getValue();
9359         
9360         return v;
9361     },
9362     
9363     /**
9364      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9365      * @param {Mixed} value The value to set
9366      */
9367     setRawValue : function(v){
9368         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9369     },
9370     
9371     selectText : function(start, end){
9372         var v = this.getRawValue();
9373         if(v.length > 0){
9374             start = start === undefined ? 0 : start;
9375             end = end === undefined ? v.length : end;
9376             var d = this.inputEl().dom;
9377             if(d.setSelectionRange){
9378                 d.setSelectionRange(start, end);
9379             }else if(d.createTextRange){
9380                 var range = d.createTextRange();
9381                 range.moveStart("character", start);
9382                 range.moveEnd("character", v.length-end);
9383                 range.select();
9384             }
9385         }
9386     },
9387     
9388     /**
9389      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9390      * @param {Mixed} value The value to set
9391      */
9392     setValue : function(v){
9393         this.value = v;
9394         if(this.rendered){
9395             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9396             this.validate();
9397         }
9398     },
9399     
9400     /*
9401     processValue : function(value){
9402         if(this.stripCharsRe){
9403             var newValue = value.replace(this.stripCharsRe, '');
9404             if(newValue !== value){
9405                 this.setRawValue(newValue);
9406                 return newValue;
9407             }
9408         }
9409         return value;
9410     },
9411   */
9412     preFocus : function(){
9413         
9414         if(this.selectOnFocus){
9415             this.inputEl().dom.select();
9416         }
9417     },
9418     filterKeys : function(e){
9419         var k = e.getKey();
9420         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9421             return;
9422         }
9423         var c = e.getCharCode(), cc = String.fromCharCode(c);
9424         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9425             return;
9426         }
9427         if(!this.maskRe.test(cc)){
9428             e.stopEvent();
9429         }
9430     },
9431      /**
9432      * Clear any invalid styles/messages for this field
9433      */
9434     clearInvalid : function(){
9435         
9436         if(!this.el || this.preventMark){ // not rendered
9437             return;
9438         }
9439         
9440      
9441         this.el.removeClass(this.invalidClass);
9442         
9443         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9444             
9445             var feedback = this.el.select('.form-control-feedback', true).first();
9446             
9447             if(feedback){
9448                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9449             }
9450             
9451         }
9452         
9453         if(this.indicator){
9454             this.indicator.removeClass('visible');
9455             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9456         }
9457         
9458         this.fireEvent('valid', this);
9459     },
9460     
9461      /**
9462      * Mark this field as valid
9463      */
9464     markValid : function()
9465     {
9466         if(!this.el  || this.preventMark){ // not rendered...
9467             return;
9468         }
9469         
9470         this.el.removeClass([this.invalidClass, this.validClass]);
9471         
9472         var feedback = this.el.select('.form-control-feedback', true).first();
9473             
9474         if(feedback){
9475             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9476         }
9477         
9478         if(this.indicator){
9479             this.indicator.removeClass('visible');
9480             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9481         }
9482         
9483         if(this.disabled){
9484             return;
9485         }
9486         
9487         if(this.allowBlank && !this.getRawValue().length){
9488             return;
9489         }
9490         
9491         this.el.addClass(this.validClass);
9492         
9493         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9494             
9495             var feedback = this.el.select('.form-control-feedback', true).first();
9496             
9497             if(feedback){
9498                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9499                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9500             }
9501             
9502         }
9503         
9504         this.fireEvent('valid', this);
9505     },
9506     
9507      /**
9508      * Mark this field as invalid
9509      * @param {String} msg The validation message
9510      */
9511     markInvalid : function(msg)
9512     {
9513         if(!this.el  || this.preventMark){ // not rendered
9514             return;
9515         }
9516         
9517         this.el.removeClass([this.invalidClass, this.validClass]);
9518         
9519         var feedback = this.el.select('.form-control-feedback', true).first();
9520             
9521         if(feedback){
9522             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9523         }
9524
9525         if(this.disabled){
9526             return;
9527         }
9528         
9529         if(this.allowBlank && !this.getRawValue().length){
9530             return;
9531         }
9532         
9533         if(this.indicator){
9534             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9535             this.indicator.addClass('visible');
9536         }
9537         
9538         this.el.addClass(this.invalidClass);
9539         
9540         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9541             
9542             var feedback = this.el.select('.form-control-feedback', true).first();
9543             
9544             if(feedback){
9545                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9546                 
9547                 if(this.getValue().length || this.forceFeedback){
9548                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9549                 }
9550                 
9551             }
9552             
9553         }
9554         
9555         this.fireEvent('invalid', this, msg);
9556     },
9557     // private
9558     SafariOnKeyDown : function(event)
9559     {
9560         // this is a workaround for a password hang bug on chrome/ webkit.
9561         if (this.inputEl().dom.type != 'password') {
9562             return;
9563         }
9564         
9565         var isSelectAll = false;
9566         
9567         if(this.inputEl().dom.selectionEnd > 0){
9568             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9569         }
9570         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9571             event.preventDefault();
9572             this.setValue('');
9573             return;
9574         }
9575         
9576         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9577             
9578             event.preventDefault();
9579             // this is very hacky as keydown always get's upper case.
9580             //
9581             var cc = String.fromCharCode(event.getCharCode());
9582             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9583             
9584         }
9585     },
9586     adjustWidth : function(tag, w){
9587         tag = tag.toLowerCase();
9588         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9589             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9590                 if(tag == 'input'){
9591                     return w + 2;
9592                 }
9593                 if(tag == 'textarea'){
9594                     return w-2;
9595                 }
9596             }else if(Roo.isOpera){
9597                 if(tag == 'input'){
9598                     return w + 2;
9599                 }
9600                 if(tag == 'textarea'){
9601                     return w-2;
9602                 }
9603             }
9604         }
9605         return w;
9606     },
9607     
9608     setFieldLabel : function(v)
9609     {
9610         if(!this.rendered){
9611             return;
9612         }
9613         
9614         if(this.indicator){
9615             var ar = this.el.select('label > span',true);
9616             
9617             if (ar.elements.length) {
9618                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9619                 this.fieldLabel = v;
9620                 return;
9621             }
9622             
9623             var br = this.el.select('label',true);
9624             
9625             if(br.elements.length) {
9626                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9627                 this.fieldLabel = v;
9628                 return;
9629             }
9630             
9631             Roo.log('Cannot Found any of label > span || label in input');
9632             return;
9633         }
9634         
9635         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9636         this.fieldLabel = v;
9637         
9638         
9639     }
9640 });
9641
9642  
9643 /*
9644  * - LGPL
9645  *
9646  * Input
9647  * 
9648  */
9649
9650 /**
9651  * @class Roo.bootstrap.TextArea
9652  * @extends Roo.bootstrap.Input
9653  * Bootstrap TextArea class
9654  * @cfg {Number} cols Specifies the visible width of a text area
9655  * @cfg {Number} rows Specifies the visible number of lines in a text area
9656  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9657  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9658  * @cfg {string} html text
9659  * 
9660  * @constructor
9661  * Create a new TextArea
9662  * @param {Object} config The config object
9663  */
9664
9665 Roo.bootstrap.TextArea = function(config){
9666     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9667    
9668 };
9669
9670 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9671      
9672     cols : false,
9673     rows : 5,
9674     readOnly : false,
9675     warp : 'soft',
9676     resize : false,
9677     value: false,
9678     html: false,
9679     
9680     getAutoCreate : function(){
9681         
9682         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9683         
9684         var id = Roo.id();
9685         
9686         var cfg = {};
9687         
9688         if(this.inputType != 'hidden'){
9689             cfg.cls = 'form-group' //input-group
9690         }
9691         
9692         var input =  {
9693             tag: 'textarea',
9694             id : id,
9695             warp : this.warp,
9696             rows : this.rows,
9697             value : this.value || '',
9698             html: this.html || '',
9699             cls : 'form-control',
9700             placeholder : this.placeholder || '' 
9701             
9702         };
9703         
9704         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9705             input.maxLength = this.maxLength;
9706         }
9707         
9708         if(this.resize){
9709             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9710         }
9711         
9712         if(this.cols){
9713             input.cols = this.cols;
9714         }
9715         
9716         if (this.readOnly) {
9717             input.readonly = true;
9718         }
9719         
9720         if (this.name) {
9721             input.name = this.name;
9722         }
9723         
9724         if (this.size) {
9725             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9726         }
9727         
9728         var settings=this;
9729         ['xs','sm','md','lg'].map(function(size){
9730             if (settings[size]) {
9731                 cfg.cls += ' col-' + size + '-' + settings[size];
9732             }
9733         });
9734         
9735         var inputblock = input;
9736         
9737         if(this.hasFeedback && !this.allowBlank){
9738             
9739             var feedback = {
9740                 tag: 'span',
9741                 cls: 'glyphicon form-control-feedback'
9742             };
9743
9744             inputblock = {
9745                 cls : 'has-feedback',
9746                 cn :  [
9747                     input,
9748                     feedback
9749                 ] 
9750             };  
9751         }
9752         
9753         
9754         if (this.before || this.after) {
9755             
9756             inputblock = {
9757                 cls : 'input-group',
9758                 cn :  [] 
9759             };
9760             if (this.before) {
9761                 inputblock.cn.push({
9762                     tag :'span',
9763                     cls : 'input-group-addon',
9764                     html : this.before
9765                 });
9766             }
9767             
9768             inputblock.cn.push(input);
9769             
9770             if(this.hasFeedback && !this.allowBlank){
9771                 inputblock.cls += ' has-feedback';
9772                 inputblock.cn.push(feedback);
9773             }
9774             
9775             if (this.after) {
9776                 inputblock.cn.push({
9777                     tag :'span',
9778                     cls : 'input-group-addon',
9779                     html : this.after
9780                 });
9781             }
9782             
9783         }
9784         
9785         if (align ==='left' && this.fieldLabel.length) {
9786             cfg.cn = [
9787                 {
9788                     tag: 'label',
9789                     'for' :  id,
9790                     cls : 'control-label',
9791                     html : this.fieldLabel
9792                 },
9793                 {
9794                     cls : "",
9795                     cn: [
9796                         inputblock
9797                     ]
9798                 }
9799
9800             ];
9801             
9802             if(this.labelWidth > 12){
9803                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9804             }
9805
9806             if(this.labelWidth < 13 && this.labelmd == 0){
9807                 this.labelmd = this.labelWidth;
9808             }
9809
9810             if(this.labellg > 0){
9811                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9812                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9813             }
9814
9815             if(this.labelmd > 0){
9816                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9817                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9818             }
9819
9820             if(this.labelsm > 0){
9821                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9822                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9823             }
9824
9825             if(this.labelxs > 0){
9826                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9827                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9828             }
9829             
9830         } else if ( this.fieldLabel.length) {
9831             cfg.cn = [
9832
9833                {
9834                    tag: 'label',
9835                    //cls : 'input-group-addon',
9836                    html : this.fieldLabel
9837
9838                },
9839
9840                inputblock
9841
9842            ];
9843
9844         } else {
9845
9846             cfg.cn = [
9847
9848                 inputblock
9849
9850             ];
9851                 
9852         }
9853         
9854         if (this.disabled) {
9855             input.disabled=true;
9856         }
9857         
9858         return cfg;
9859         
9860     },
9861     /**
9862      * return the real textarea element.
9863      */
9864     inputEl: function ()
9865     {
9866         return this.el.select('textarea.form-control',true).first();
9867     },
9868     
9869     /**
9870      * Clear any invalid styles/messages for this field
9871      */
9872     clearInvalid : function()
9873     {
9874         
9875         if(!this.el || this.preventMark){ // not rendered
9876             return;
9877         }
9878         
9879         var label = this.el.select('label', true).first();
9880         var icon = this.el.select('i.fa-star', true).first();
9881         
9882         if(label && icon){
9883             icon.remove();
9884         }
9885         
9886         this.el.removeClass(this.invalidClass);
9887         
9888         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9889             
9890             var feedback = this.el.select('.form-control-feedback', true).first();
9891             
9892             if(feedback){
9893                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9894             }
9895             
9896         }
9897         
9898         this.fireEvent('valid', this);
9899     },
9900     
9901      /**
9902      * Mark this field as valid
9903      */
9904     markValid : function()
9905     {
9906         if(!this.el  || this.preventMark){ // not rendered
9907             return;
9908         }
9909         
9910         this.el.removeClass([this.invalidClass, this.validClass]);
9911         
9912         var feedback = this.el.select('.form-control-feedback', true).first();
9913             
9914         if(feedback){
9915             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9916         }
9917
9918         if(this.disabled || this.allowBlank){
9919             return;
9920         }
9921         
9922         var label = this.el.select('label', true).first();
9923         var icon = this.el.select('i.fa-star', true).first();
9924         
9925         if(label && icon){
9926             icon.remove();
9927         }
9928         
9929         this.el.addClass(this.validClass);
9930         
9931         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9932             
9933             var feedback = this.el.select('.form-control-feedback', true).first();
9934             
9935             if(feedback){
9936                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9937                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9938             }
9939             
9940         }
9941         
9942         this.fireEvent('valid', this);
9943     },
9944     
9945      /**
9946      * Mark this field as invalid
9947      * @param {String} msg The validation message
9948      */
9949     markInvalid : function(msg)
9950     {
9951         if(!this.el  || this.preventMark){ // not rendered
9952             return;
9953         }
9954         
9955         this.el.removeClass([this.invalidClass, this.validClass]);
9956         
9957         var feedback = this.el.select('.form-control-feedback', true).first();
9958             
9959         if(feedback){
9960             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9961         }
9962
9963         if(this.disabled || this.allowBlank){
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(!this.getValue().length && label && !icon){
9971             this.el.createChild({
9972                 tag : 'i',
9973                 cls : 'text-danger fa fa-lg fa-star',
9974                 tooltip : 'This field is required',
9975                 style : 'margin-right:5px;'
9976             }, label, true);
9977         }
9978
9979         this.el.addClass(this.invalidClass);
9980         
9981         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9982             
9983             var feedback = this.el.select('.form-control-feedback', true).first();
9984             
9985             if(feedback){
9986                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9987                 
9988                 if(this.getValue().length || this.forceFeedback){
9989                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9990                 }
9991                 
9992             }
9993             
9994         }
9995         
9996         this.fireEvent('invalid', this, msg);
9997     }
9998 });
9999
10000  
10001 /*
10002  * - LGPL
10003  *
10004  * trigger field - base class for combo..
10005  * 
10006  */
10007  
10008 /**
10009  * @class Roo.bootstrap.TriggerField
10010  * @extends Roo.bootstrap.Input
10011  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10012  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10013  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10014  * for which you can provide a custom implementation.  For example:
10015  * <pre><code>
10016 var trigger = new Roo.bootstrap.TriggerField();
10017 trigger.onTriggerClick = myTriggerFn;
10018 trigger.applyTo('my-field');
10019 </code></pre>
10020  *
10021  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10022  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10023  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10024  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10025  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10026
10027  * @constructor
10028  * Create a new TriggerField.
10029  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10030  * to the base TextField)
10031  */
10032 Roo.bootstrap.TriggerField = function(config){
10033     this.mimicing = false;
10034     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10035 };
10036
10037 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10038     /**
10039      * @cfg {String} triggerClass A CSS class to apply to the trigger
10040      */
10041      /**
10042      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10043      */
10044     hideTrigger:false,
10045
10046     /**
10047      * @cfg {Boolean} removable (true|false) special filter default false
10048      */
10049     removable : false,
10050     
10051     /** @cfg {Boolean} grow @hide */
10052     /** @cfg {Number} growMin @hide */
10053     /** @cfg {Number} growMax @hide */
10054
10055     /**
10056      * @hide 
10057      * @method
10058      */
10059     autoSize: Roo.emptyFn,
10060     // private
10061     monitorTab : true,
10062     // private
10063     deferHeight : true,
10064
10065     
10066     actionMode : 'wrap',
10067     
10068     caret : false,
10069     
10070     
10071     getAutoCreate : function(){
10072        
10073         var align = this.labelAlign || this.parentLabelAlign();
10074         
10075         var id = Roo.id();
10076         
10077         var cfg = {
10078             cls: 'form-group' //input-group
10079         };
10080         
10081         
10082         var input =  {
10083             tag: 'input',
10084             id : id,
10085             type : this.inputType,
10086             cls : 'form-control',
10087             autocomplete: 'new-password',
10088             placeholder : this.placeholder || '' 
10089             
10090         };
10091         if (this.name) {
10092             input.name = this.name;
10093         }
10094         if (this.size) {
10095             input.cls += ' input-' + this.size;
10096         }
10097         
10098         if (this.disabled) {
10099             input.disabled=true;
10100         }
10101         
10102         var inputblock = input;
10103         
10104         if(this.hasFeedback && !this.allowBlank){
10105             
10106             var feedback = {
10107                 tag: 'span',
10108                 cls: 'glyphicon form-control-feedback'
10109             };
10110             
10111             if(this.removable && !this.editable && !this.tickable){
10112                 inputblock = {
10113                     cls : 'has-feedback',
10114                     cn :  [
10115                         inputblock,
10116                         {
10117                             tag: 'button',
10118                             html : 'x',
10119                             cls : 'roo-combo-removable-btn close'
10120                         },
10121                         feedback
10122                     ] 
10123                 };
10124             } else {
10125                 inputblock = {
10126                     cls : 'has-feedback',
10127                     cn :  [
10128                         inputblock,
10129                         feedback
10130                     ] 
10131                 };
10132             }
10133
10134         } else {
10135             if(this.removable && !this.editable && !this.tickable){
10136                 inputblock = {
10137                     cls : 'roo-removable',
10138                     cn :  [
10139                         inputblock,
10140                         {
10141                             tag: 'button',
10142                             html : 'x',
10143                             cls : 'roo-combo-removable-btn close'
10144                         }
10145                     ] 
10146                 };
10147             }
10148         }
10149         
10150         if (this.before || this.after) {
10151             
10152             inputblock = {
10153                 cls : 'input-group',
10154                 cn :  [] 
10155             };
10156             if (this.before) {
10157                 inputblock.cn.push({
10158                     tag :'span',
10159                     cls : 'input-group-addon',
10160                     html : this.before
10161                 });
10162             }
10163             
10164             inputblock.cn.push(input);
10165             
10166             if(this.hasFeedback && !this.allowBlank){
10167                 inputblock.cls += ' has-feedback';
10168                 inputblock.cn.push(feedback);
10169             }
10170             
10171             if (this.after) {
10172                 inputblock.cn.push({
10173                     tag :'span',
10174                     cls : 'input-group-addon',
10175                     html : this.after
10176                 });
10177             }
10178             
10179         };
10180         
10181         var box = {
10182             tag: 'div',
10183             cn: [
10184                 {
10185                     tag: 'input',
10186                     type : 'hidden',
10187                     cls: 'form-hidden-field'
10188                 },
10189                 inputblock
10190             ]
10191             
10192         };
10193         
10194         if(this.multiple){
10195             box = {
10196                 tag: 'div',
10197                 cn: [
10198                     {
10199                         tag: 'input',
10200                         type : 'hidden',
10201                         cls: 'form-hidden-field'
10202                     },
10203                     {
10204                         tag: 'ul',
10205                         cls: 'roo-select2-choices',
10206                         cn:[
10207                             {
10208                                 tag: 'li',
10209                                 cls: 'roo-select2-search-field',
10210                                 cn: [
10211
10212                                     inputblock
10213                                 ]
10214                             }
10215                         ]
10216                     }
10217                 ]
10218             }
10219         };
10220         
10221         var combobox = {
10222             cls: 'roo-select2-container input-group',
10223             cn: [
10224                 box
10225 //                {
10226 //                    tag: 'ul',
10227 //                    cls: 'typeahead typeahead-long dropdown-menu',
10228 //                    style: 'display:none'
10229 //                }
10230             ]
10231         };
10232         
10233         if(!this.multiple && this.showToggleBtn){
10234             
10235             var caret = {
10236                         tag: 'span',
10237                         cls: 'caret'
10238              };
10239             if (this.caret != false) {
10240                 caret = {
10241                      tag: 'i',
10242                      cls: 'fa fa-' + this.caret
10243                 };
10244                 
10245             }
10246             
10247             combobox.cn.push({
10248                 tag :'span',
10249                 cls : 'input-group-addon btn dropdown-toggle',
10250                 cn : [
10251                     caret,
10252                     {
10253                         tag: 'span',
10254                         cls: 'combobox-clear',
10255                         cn  : [
10256                             {
10257                                 tag : 'i',
10258                                 cls: 'icon-remove'
10259                             }
10260                         ]
10261                     }
10262                 ]
10263
10264             })
10265         }
10266         
10267         if(this.multiple){
10268             combobox.cls += ' roo-select2-container-multi';
10269         }
10270         
10271         if (align ==='left' && this.fieldLabel.length) {
10272             
10273             cfg.cls += ' roo-form-group-label-left';
10274
10275             cfg.cn = [
10276                 {
10277                     tag : 'i',
10278                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10279                     tooltip : 'This field is required'
10280                 },
10281                 {
10282                     tag: 'label',
10283                     'for' :  id,
10284                     cls : 'control-label',
10285                     html : this.fieldLabel
10286
10287                 },
10288                 {
10289                     cls : "", 
10290                     cn: [
10291                         combobox
10292                     ]
10293                 }
10294
10295             ];
10296             
10297             var labelCfg = cfg.cn[1];
10298             var contentCfg = cfg.cn[2];
10299             
10300             if(this.indicatorpos == 'right'){
10301                 cfg.cn = [
10302                     {
10303                         tag: 'label',
10304                         'for' :  id,
10305                         cls : 'control-label',
10306                         cn : [
10307                             {
10308                                 tag : 'span',
10309                                 html : this.fieldLabel
10310                             },
10311                             {
10312                                 tag : 'i',
10313                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10314                                 tooltip : 'This field is required'
10315                             }
10316                         ]
10317                     },
10318                     {
10319                         cls : "", 
10320                         cn: [
10321                             combobox
10322                         ]
10323                     }
10324
10325                 ];
10326                 
10327                 labelCfg = cfg.cn[0];
10328                 contentCfg = cfg.cn[1];
10329             }
10330             
10331             if(this.labelWidth > 12){
10332                 labelCfg.style = "width: " + this.labelWidth + 'px';
10333             }
10334             
10335             if(this.labelWidth < 13 && this.labelmd == 0){
10336                 this.labelmd = this.labelWidth;
10337             }
10338             
10339             if(this.labellg > 0){
10340                 labelCfg.cls += ' col-lg-' + this.labellg;
10341                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10342             }
10343             
10344             if(this.labelmd > 0){
10345                 labelCfg.cls += ' col-md-' + this.labelmd;
10346                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10347             }
10348             
10349             if(this.labelsm > 0){
10350                 labelCfg.cls += ' col-sm-' + this.labelsm;
10351                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10352             }
10353             
10354             if(this.labelxs > 0){
10355                 labelCfg.cls += ' col-xs-' + this.labelxs;
10356                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10357             }
10358             
10359         } else if ( this.fieldLabel.length) {
10360 //                Roo.log(" label");
10361             cfg.cn = [
10362                 {
10363                    tag : 'i',
10364                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10365                    tooltip : 'This field is required'
10366                },
10367                {
10368                    tag: 'label',
10369                    //cls : 'input-group-addon',
10370                    html : this.fieldLabel
10371
10372                },
10373
10374                combobox
10375
10376             ];
10377             
10378             if(this.indicatorpos == 'right'){
10379                 
10380                 cfg.cn = [
10381                     {
10382                        tag: 'label',
10383                        cn : [
10384                            {
10385                                tag : 'span',
10386                                html : this.fieldLabel
10387                            },
10388                            {
10389                               tag : 'i',
10390                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10391                               tooltip : 'This field is required'
10392                            }
10393                        ]
10394
10395                     },
10396                     combobox
10397
10398                 ];
10399
10400             }
10401
10402         } else {
10403             
10404 //                Roo.log(" no label && no align");
10405                 cfg = combobox
10406                      
10407                 
10408         }
10409         
10410         var settings=this;
10411         ['xs','sm','md','lg'].map(function(size){
10412             if (settings[size]) {
10413                 cfg.cls += ' col-' + size + '-' + settings[size];
10414             }
10415         });
10416         
10417         return cfg;
10418         
10419     },
10420     
10421     
10422     
10423     // private
10424     onResize : function(w, h){
10425 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10426 //        if(typeof w == 'number'){
10427 //            var x = w - this.trigger.getWidth();
10428 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10429 //            this.trigger.setStyle('left', x+'px');
10430 //        }
10431     },
10432
10433     // private
10434     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10435
10436     // private
10437     getResizeEl : function(){
10438         return this.inputEl();
10439     },
10440
10441     // private
10442     getPositionEl : function(){
10443         return this.inputEl();
10444     },
10445
10446     // private
10447     alignErrorIcon : function(){
10448         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10449     },
10450
10451     // private
10452     initEvents : function(){
10453         
10454         this.createList();
10455         
10456         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10457         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10458         if(!this.multiple && this.showToggleBtn){
10459             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10460             if(this.hideTrigger){
10461                 this.trigger.setDisplayed(false);
10462             }
10463             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10464         }
10465         
10466         if(this.multiple){
10467             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10468         }
10469         
10470         if(this.removable && !this.editable && !this.tickable){
10471             var close = this.closeTriggerEl();
10472             
10473             if(close){
10474                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10475                 close.on('click', this.removeBtnClick, this, close);
10476             }
10477         }
10478         
10479         //this.trigger.addClassOnOver('x-form-trigger-over');
10480         //this.trigger.addClassOnClick('x-form-trigger-click');
10481         
10482         //if(!this.width){
10483         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10484         //}
10485     },
10486     
10487     closeTriggerEl : function()
10488     {
10489         var close = this.el.select('.roo-combo-removable-btn', true).first();
10490         return close ? close : false;
10491     },
10492     
10493     removeBtnClick : function(e, h, el)
10494     {
10495         e.preventDefault();
10496         
10497         if(this.fireEvent("remove", this) !== false){
10498             this.reset();
10499             this.fireEvent("afterremove", this)
10500         }
10501     },
10502     
10503     createList : function()
10504     {
10505         this.list = Roo.get(document.body).createChild({
10506             tag: 'ul',
10507             cls: 'typeahead typeahead-long dropdown-menu',
10508             style: 'display:none'
10509         });
10510         
10511         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10512         
10513     },
10514
10515     // private
10516     initTrigger : function(){
10517        
10518     },
10519
10520     // private
10521     onDestroy : function(){
10522         if(this.trigger){
10523             this.trigger.removeAllListeners();
10524           //  this.trigger.remove();
10525         }
10526         //if(this.wrap){
10527         //    this.wrap.remove();
10528         //}
10529         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10530     },
10531
10532     // private
10533     onFocus : function(){
10534         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10535         /*
10536         if(!this.mimicing){
10537             this.wrap.addClass('x-trigger-wrap-focus');
10538             this.mimicing = true;
10539             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10540             if(this.monitorTab){
10541                 this.el.on("keydown", this.checkTab, this);
10542             }
10543         }
10544         */
10545     },
10546
10547     // private
10548     checkTab : function(e){
10549         if(e.getKey() == e.TAB){
10550             this.triggerBlur();
10551         }
10552     },
10553
10554     // private
10555     onBlur : function(){
10556         // do nothing
10557     },
10558
10559     // private
10560     mimicBlur : function(e, t){
10561         /*
10562         if(!this.wrap.contains(t) && this.validateBlur()){
10563             this.triggerBlur();
10564         }
10565         */
10566     },
10567
10568     // private
10569     triggerBlur : function(){
10570         this.mimicing = false;
10571         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10572         if(this.monitorTab){
10573             this.el.un("keydown", this.checkTab, this);
10574         }
10575         //this.wrap.removeClass('x-trigger-wrap-focus');
10576         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10577     },
10578
10579     // private
10580     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10581     validateBlur : function(e, t){
10582         return true;
10583     },
10584
10585     // private
10586     onDisable : function(){
10587         this.inputEl().dom.disabled = true;
10588         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10589         //if(this.wrap){
10590         //    this.wrap.addClass('x-item-disabled');
10591         //}
10592     },
10593
10594     // private
10595     onEnable : function(){
10596         this.inputEl().dom.disabled = false;
10597         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10598         //if(this.wrap){
10599         //    this.el.removeClass('x-item-disabled');
10600         //}
10601     },
10602
10603     // private
10604     onShow : function(){
10605         var ae = this.getActionEl();
10606         
10607         if(ae){
10608             ae.dom.style.display = '';
10609             ae.dom.style.visibility = 'visible';
10610         }
10611     },
10612
10613     // private
10614     
10615     onHide : function(){
10616         var ae = this.getActionEl();
10617         ae.dom.style.display = 'none';
10618     },
10619
10620     /**
10621      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10622      * by an implementing function.
10623      * @method
10624      * @param {EventObject} e
10625      */
10626     onTriggerClick : Roo.emptyFn
10627 });
10628  /*
10629  * Based on:
10630  * Ext JS Library 1.1.1
10631  * Copyright(c) 2006-2007, Ext JS, LLC.
10632  *
10633  * Originally Released Under LGPL - original licence link has changed is not relivant.
10634  *
10635  * Fork - LGPL
10636  * <script type="text/javascript">
10637  */
10638
10639
10640 /**
10641  * @class Roo.data.SortTypes
10642  * @singleton
10643  * Defines the default sorting (casting?) comparison functions used when sorting data.
10644  */
10645 Roo.data.SortTypes = {
10646     /**
10647      * Default sort that does nothing
10648      * @param {Mixed} s The value being converted
10649      * @return {Mixed} The comparison value
10650      */
10651     none : function(s){
10652         return s;
10653     },
10654     
10655     /**
10656      * The regular expression used to strip tags
10657      * @type {RegExp}
10658      * @property
10659      */
10660     stripTagsRE : /<\/?[^>]+>/gi,
10661     
10662     /**
10663      * Strips all HTML tags to sort on text only
10664      * @param {Mixed} s The value being converted
10665      * @return {String} The comparison value
10666      */
10667     asText : function(s){
10668         return String(s).replace(this.stripTagsRE, "");
10669     },
10670     
10671     /**
10672      * Strips all HTML tags to sort on text only - Case insensitive
10673      * @param {Mixed} s The value being converted
10674      * @return {String} The comparison value
10675      */
10676     asUCText : function(s){
10677         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10678     },
10679     
10680     /**
10681      * Case insensitive string
10682      * @param {Mixed} s The value being converted
10683      * @return {String} The comparison value
10684      */
10685     asUCString : function(s) {
10686         return String(s).toUpperCase();
10687     },
10688     
10689     /**
10690      * Date sorting
10691      * @param {Mixed} s The value being converted
10692      * @return {Number} The comparison value
10693      */
10694     asDate : function(s) {
10695         if(!s){
10696             return 0;
10697         }
10698         if(s instanceof Date){
10699             return s.getTime();
10700         }
10701         return Date.parse(String(s));
10702     },
10703     
10704     /**
10705      * Float sorting
10706      * @param {Mixed} s The value being converted
10707      * @return {Float} The comparison value
10708      */
10709     asFloat : function(s) {
10710         var val = parseFloat(String(s).replace(/,/g, ""));
10711         if(isNaN(val)) {
10712             val = 0;
10713         }
10714         return val;
10715     },
10716     
10717     /**
10718      * Integer sorting
10719      * @param {Mixed} s The value being converted
10720      * @return {Number} The comparison value
10721      */
10722     asInt : function(s) {
10723         var val = parseInt(String(s).replace(/,/g, ""));
10724         if(isNaN(val)) {
10725             val = 0;
10726         }
10727         return val;
10728     }
10729 };/*
10730  * Based on:
10731  * Ext JS Library 1.1.1
10732  * Copyright(c) 2006-2007, Ext JS, LLC.
10733  *
10734  * Originally Released Under LGPL - original licence link has changed is not relivant.
10735  *
10736  * Fork - LGPL
10737  * <script type="text/javascript">
10738  */
10739
10740 /**
10741 * @class Roo.data.Record
10742  * Instances of this class encapsulate both record <em>definition</em> information, and record
10743  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10744  * to access Records cached in an {@link Roo.data.Store} object.<br>
10745  * <p>
10746  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10747  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10748  * objects.<br>
10749  * <p>
10750  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10751  * @constructor
10752  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10753  * {@link #create}. The parameters are the same.
10754  * @param {Array} data An associative Array of data values keyed by the field name.
10755  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10756  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10757  * not specified an integer id is generated.
10758  */
10759 Roo.data.Record = function(data, id){
10760     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10761     this.data = data;
10762 };
10763
10764 /**
10765  * Generate a constructor for a specific record layout.
10766  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10767  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10768  * Each field definition object may contain the following properties: <ul>
10769  * <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,
10770  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10771  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10772  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10773  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10774  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10775  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10776  * this may be omitted.</p></li>
10777  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10778  * <ul><li>auto (Default, implies no conversion)</li>
10779  * <li>string</li>
10780  * <li>int</li>
10781  * <li>float</li>
10782  * <li>boolean</li>
10783  * <li>date</li></ul></p></li>
10784  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10785  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10786  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10787  * by the Reader into an object that will be stored in the Record. It is passed the
10788  * following parameters:<ul>
10789  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10790  * </ul></p></li>
10791  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10792  * </ul>
10793  * <br>usage:<br><pre><code>
10794 var TopicRecord = Roo.data.Record.create(
10795     {name: 'title', mapping: 'topic_title'},
10796     {name: 'author', mapping: 'username'},
10797     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10798     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10799     {name: 'lastPoster', mapping: 'user2'},
10800     {name: 'excerpt', mapping: 'post_text'}
10801 );
10802
10803 var myNewRecord = new TopicRecord({
10804     title: 'Do my job please',
10805     author: 'noobie',
10806     totalPosts: 1,
10807     lastPost: new Date(),
10808     lastPoster: 'Animal',
10809     excerpt: 'No way dude!'
10810 });
10811 myStore.add(myNewRecord);
10812 </code></pre>
10813  * @method create
10814  * @static
10815  */
10816 Roo.data.Record.create = function(o){
10817     var f = function(){
10818         f.superclass.constructor.apply(this, arguments);
10819     };
10820     Roo.extend(f, Roo.data.Record);
10821     var p = f.prototype;
10822     p.fields = new Roo.util.MixedCollection(false, function(field){
10823         return field.name;
10824     });
10825     for(var i = 0, len = o.length; i < len; i++){
10826         p.fields.add(new Roo.data.Field(o[i]));
10827     }
10828     f.getField = function(name){
10829         return p.fields.get(name);  
10830     };
10831     return f;
10832 };
10833
10834 Roo.data.Record.AUTO_ID = 1000;
10835 Roo.data.Record.EDIT = 'edit';
10836 Roo.data.Record.REJECT = 'reject';
10837 Roo.data.Record.COMMIT = 'commit';
10838
10839 Roo.data.Record.prototype = {
10840     /**
10841      * Readonly flag - true if this record has been modified.
10842      * @type Boolean
10843      */
10844     dirty : false,
10845     editing : false,
10846     error: null,
10847     modified: null,
10848
10849     // private
10850     join : function(store){
10851         this.store = store;
10852     },
10853
10854     /**
10855      * Set the named field to the specified value.
10856      * @param {String} name The name of the field to set.
10857      * @param {Object} value The value to set the field to.
10858      */
10859     set : function(name, value){
10860         if(this.data[name] == value){
10861             return;
10862         }
10863         this.dirty = true;
10864         if(!this.modified){
10865             this.modified = {};
10866         }
10867         if(typeof this.modified[name] == 'undefined'){
10868             this.modified[name] = this.data[name];
10869         }
10870         this.data[name] = value;
10871         if(!this.editing && this.store){
10872             this.store.afterEdit(this);
10873         }       
10874     },
10875
10876     /**
10877      * Get the value of the named field.
10878      * @param {String} name The name of the field to get the value of.
10879      * @return {Object} The value of the field.
10880      */
10881     get : function(name){
10882         return this.data[name]; 
10883     },
10884
10885     // private
10886     beginEdit : function(){
10887         this.editing = true;
10888         this.modified = {}; 
10889     },
10890
10891     // private
10892     cancelEdit : function(){
10893         this.editing = false;
10894         delete this.modified;
10895     },
10896
10897     // private
10898     endEdit : function(){
10899         this.editing = false;
10900         if(this.dirty && this.store){
10901             this.store.afterEdit(this);
10902         }
10903     },
10904
10905     /**
10906      * Usually called by the {@link Roo.data.Store} which owns the Record.
10907      * Rejects all changes made to the Record since either creation, or the last commit operation.
10908      * Modified fields are reverted to their original values.
10909      * <p>
10910      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10911      * of reject operations.
10912      */
10913     reject : function(){
10914         var m = this.modified;
10915         for(var n in m){
10916             if(typeof m[n] != "function"){
10917                 this.data[n] = m[n];
10918             }
10919         }
10920         this.dirty = false;
10921         delete this.modified;
10922         this.editing = false;
10923         if(this.store){
10924             this.store.afterReject(this);
10925         }
10926     },
10927
10928     /**
10929      * Usually called by the {@link Roo.data.Store} which owns the Record.
10930      * Commits all changes made to the Record since either creation, or the last commit operation.
10931      * <p>
10932      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10933      * of commit operations.
10934      */
10935     commit : function(){
10936         this.dirty = false;
10937         delete this.modified;
10938         this.editing = false;
10939         if(this.store){
10940             this.store.afterCommit(this);
10941         }
10942     },
10943
10944     // private
10945     hasError : function(){
10946         return this.error != null;
10947     },
10948
10949     // private
10950     clearError : function(){
10951         this.error = null;
10952     },
10953
10954     /**
10955      * Creates a copy of this record.
10956      * @param {String} id (optional) A new record id if you don't want to use this record's id
10957      * @return {Record}
10958      */
10959     copy : function(newId) {
10960         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10961     }
10962 };/*
10963  * Based on:
10964  * Ext JS Library 1.1.1
10965  * Copyright(c) 2006-2007, Ext JS, LLC.
10966  *
10967  * Originally Released Under LGPL - original licence link has changed is not relivant.
10968  *
10969  * Fork - LGPL
10970  * <script type="text/javascript">
10971  */
10972
10973
10974
10975 /**
10976  * @class Roo.data.Store
10977  * @extends Roo.util.Observable
10978  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10979  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10980  * <p>
10981  * 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
10982  * has no knowledge of the format of the data returned by the Proxy.<br>
10983  * <p>
10984  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10985  * instances from the data object. These records are cached and made available through accessor functions.
10986  * @constructor
10987  * Creates a new Store.
10988  * @param {Object} config A config object containing the objects needed for the Store to access data,
10989  * and read the data into Records.
10990  */
10991 Roo.data.Store = function(config){
10992     this.data = new Roo.util.MixedCollection(false);
10993     this.data.getKey = function(o){
10994         return o.id;
10995     };
10996     this.baseParams = {};
10997     // private
10998     this.paramNames = {
10999         "start" : "start",
11000         "limit" : "limit",
11001         "sort" : "sort",
11002         "dir" : "dir",
11003         "multisort" : "_multisort"
11004     };
11005
11006     if(config && config.data){
11007         this.inlineData = config.data;
11008         delete config.data;
11009     }
11010
11011     Roo.apply(this, config);
11012     
11013     if(this.reader){ // reader passed
11014         this.reader = Roo.factory(this.reader, Roo.data);
11015         this.reader.xmodule = this.xmodule || false;
11016         if(!this.recordType){
11017             this.recordType = this.reader.recordType;
11018         }
11019         if(this.reader.onMetaChange){
11020             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11021         }
11022     }
11023
11024     if(this.recordType){
11025         this.fields = this.recordType.prototype.fields;
11026     }
11027     this.modified = [];
11028
11029     this.addEvents({
11030         /**
11031          * @event datachanged
11032          * Fires when the data cache has changed, and a widget which is using this Store
11033          * as a Record cache should refresh its view.
11034          * @param {Store} this
11035          */
11036         datachanged : true,
11037         /**
11038          * @event metachange
11039          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11040          * @param {Store} this
11041          * @param {Object} meta The JSON metadata
11042          */
11043         metachange : true,
11044         /**
11045          * @event add
11046          * Fires when Records have been added to the Store
11047          * @param {Store} this
11048          * @param {Roo.data.Record[]} records The array of Records added
11049          * @param {Number} index The index at which the record(s) were added
11050          */
11051         add : true,
11052         /**
11053          * @event remove
11054          * Fires when a Record has been removed from the Store
11055          * @param {Store} this
11056          * @param {Roo.data.Record} record The Record that was removed
11057          * @param {Number} index The index at which the record was removed
11058          */
11059         remove : true,
11060         /**
11061          * @event update
11062          * Fires when a Record has been updated
11063          * @param {Store} this
11064          * @param {Roo.data.Record} record The Record that was updated
11065          * @param {String} operation The update operation being performed.  Value may be one of:
11066          * <pre><code>
11067  Roo.data.Record.EDIT
11068  Roo.data.Record.REJECT
11069  Roo.data.Record.COMMIT
11070          * </code></pre>
11071          */
11072         update : true,
11073         /**
11074          * @event clear
11075          * Fires when the data cache has been cleared.
11076          * @param {Store} this
11077          */
11078         clear : true,
11079         /**
11080          * @event beforeload
11081          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11082          * the load action will be canceled.
11083          * @param {Store} this
11084          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11085          */
11086         beforeload : true,
11087         /**
11088          * @event beforeloadadd
11089          * Fires after a new set of Records has been loaded.
11090          * @param {Store} this
11091          * @param {Roo.data.Record[]} records The Records that were loaded
11092          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11093          */
11094         beforeloadadd : true,
11095         /**
11096          * @event load
11097          * Fires after a new set of Records has been loaded, before they are added to the store.
11098          * @param {Store} this
11099          * @param {Roo.data.Record[]} records The Records that were loaded
11100          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11101          * @params {Object} return from reader
11102          */
11103         load : true,
11104         /**
11105          * @event loadexception
11106          * Fires if an exception occurs in the Proxy during loading.
11107          * Called with the signature of the Proxy's "loadexception" event.
11108          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11109          * 
11110          * @param {Proxy} 
11111          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11112          * @param {Object} load options 
11113          * @param {Object} jsonData from your request (normally this contains the Exception)
11114          */
11115         loadexception : true
11116     });
11117     
11118     if(this.proxy){
11119         this.proxy = Roo.factory(this.proxy, Roo.data);
11120         this.proxy.xmodule = this.xmodule || false;
11121         this.relayEvents(this.proxy,  ["loadexception"]);
11122     }
11123     this.sortToggle = {};
11124     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11125
11126     Roo.data.Store.superclass.constructor.call(this);
11127
11128     if(this.inlineData){
11129         this.loadData(this.inlineData);
11130         delete this.inlineData;
11131     }
11132 };
11133
11134 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11135      /**
11136     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11137     * without a remote query - used by combo/forms at present.
11138     */
11139     
11140     /**
11141     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11142     */
11143     /**
11144     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11145     */
11146     /**
11147     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11148     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11149     */
11150     /**
11151     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11152     * on any HTTP request
11153     */
11154     /**
11155     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11156     */
11157     /**
11158     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11159     */
11160     multiSort: false,
11161     /**
11162     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11163     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11164     */
11165     remoteSort : false,
11166
11167     /**
11168     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11169      * loaded or when a record is removed. (defaults to false).
11170     */
11171     pruneModifiedRecords : false,
11172
11173     // private
11174     lastOptions : null,
11175
11176     /**
11177      * Add Records to the Store and fires the add event.
11178      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11179      */
11180     add : function(records){
11181         records = [].concat(records);
11182         for(var i = 0, len = records.length; i < len; i++){
11183             records[i].join(this);
11184         }
11185         var index = this.data.length;
11186         this.data.addAll(records);
11187         this.fireEvent("add", this, records, index);
11188     },
11189
11190     /**
11191      * Remove a Record from the Store and fires the remove event.
11192      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11193      */
11194     remove : function(record){
11195         var index = this.data.indexOf(record);
11196         this.data.removeAt(index);
11197  
11198         if(this.pruneModifiedRecords){
11199             this.modified.remove(record);
11200         }
11201         this.fireEvent("remove", this, record, index);
11202     },
11203
11204     /**
11205      * Remove all Records from the Store and fires the clear event.
11206      */
11207     removeAll : function(){
11208         this.data.clear();
11209         if(this.pruneModifiedRecords){
11210             this.modified = [];
11211         }
11212         this.fireEvent("clear", this);
11213     },
11214
11215     /**
11216      * Inserts Records to the Store at the given index and fires the add event.
11217      * @param {Number} index The start index at which to insert the passed Records.
11218      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11219      */
11220     insert : function(index, records){
11221         records = [].concat(records);
11222         for(var i = 0, len = records.length; i < len; i++){
11223             this.data.insert(index, records[i]);
11224             records[i].join(this);
11225         }
11226         this.fireEvent("add", this, records, index);
11227     },
11228
11229     /**
11230      * Get the index within the cache of the passed Record.
11231      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11232      * @return {Number} The index of the passed Record. Returns -1 if not found.
11233      */
11234     indexOf : function(record){
11235         return this.data.indexOf(record);
11236     },
11237
11238     /**
11239      * Get the index within the cache of the Record with the passed id.
11240      * @param {String} id The id of the Record to find.
11241      * @return {Number} The index of the Record. Returns -1 if not found.
11242      */
11243     indexOfId : function(id){
11244         return this.data.indexOfKey(id);
11245     },
11246
11247     /**
11248      * Get the Record with the specified id.
11249      * @param {String} id The id of the Record to find.
11250      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11251      */
11252     getById : function(id){
11253         return this.data.key(id);
11254     },
11255
11256     /**
11257      * Get the Record at the specified index.
11258      * @param {Number} index The index of the Record to find.
11259      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11260      */
11261     getAt : function(index){
11262         return this.data.itemAt(index);
11263     },
11264
11265     /**
11266      * Returns a range of Records between specified indices.
11267      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11268      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11269      * @return {Roo.data.Record[]} An array of Records
11270      */
11271     getRange : function(start, end){
11272         return this.data.getRange(start, end);
11273     },
11274
11275     // private
11276     storeOptions : function(o){
11277         o = Roo.apply({}, o);
11278         delete o.callback;
11279         delete o.scope;
11280         this.lastOptions = o;
11281     },
11282
11283     /**
11284      * Loads the Record cache from the configured Proxy using the configured Reader.
11285      * <p>
11286      * If using remote paging, then the first load call must specify the <em>start</em>
11287      * and <em>limit</em> properties in the options.params property to establish the initial
11288      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11289      * <p>
11290      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11291      * and this call will return before the new data has been loaded. Perform any post-processing
11292      * in a callback function, or in a "load" event handler.</strong>
11293      * <p>
11294      * @param {Object} options An object containing properties which control loading options:<ul>
11295      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11296      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11297      * passed the following arguments:<ul>
11298      * <li>r : Roo.data.Record[]</li>
11299      * <li>options: Options object from the load call</li>
11300      * <li>success: Boolean success indicator</li></ul></li>
11301      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11302      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11303      * </ul>
11304      */
11305     load : function(options){
11306         options = options || {};
11307         if(this.fireEvent("beforeload", this, options) !== false){
11308             this.storeOptions(options);
11309             var p = Roo.apply(options.params || {}, this.baseParams);
11310             // if meta was not loaded from remote source.. try requesting it.
11311             if (!this.reader.metaFromRemote) {
11312                 p._requestMeta = 1;
11313             }
11314             if(this.sortInfo && this.remoteSort){
11315                 var pn = this.paramNames;
11316                 p[pn["sort"]] = this.sortInfo.field;
11317                 p[pn["dir"]] = this.sortInfo.direction;
11318             }
11319             if (this.multiSort) {
11320                 var pn = this.paramNames;
11321                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11322             }
11323             
11324             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11325         }
11326     },
11327
11328     /**
11329      * Reloads the Record cache from the configured Proxy using the configured Reader and
11330      * the options from the last load operation performed.
11331      * @param {Object} options (optional) An object containing properties which may override the options
11332      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11333      * the most recently used options are reused).
11334      */
11335     reload : function(options){
11336         this.load(Roo.applyIf(options||{}, this.lastOptions));
11337     },
11338
11339     // private
11340     // Called as a callback by the Reader during a load operation.
11341     loadRecords : function(o, options, success){
11342         if(!o || success === false){
11343             if(success !== false){
11344                 this.fireEvent("load", this, [], options, o);
11345             }
11346             if(options.callback){
11347                 options.callback.call(options.scope || this, [], options, false);
11348             }
11349             return;
11350         }
11351         // if data returned failure - throw an exception.
11352         if (o.success === false) {
11353             // show a message if no listener is registered.
11354             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11355                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11356             }
11357             // loadmask wil be hooked into this..
11358             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11359             return;
11360         }
11361         var r = o.records, t = o.totalRecords || r.length;
11362         
11363         this.fireEvent("beforeloadadd", this, r, options, o);
11364         
11365         if(!options || options.add !== true){
11366             if(this.pruneModifiedRecords){
11367                 this.modified = [];
11368             }
11369             for(var i = 0, len = r.length; i < len; i++){
11370                 r[i].join(this);
11371             }
11372             if(this.snapshot){
11373                 this.data = this.snapshot;
11374                 delete this.snapshot;
11375             }
11376             this.data.clear();
11377             this.data.addAll(r);
11378             this.totalLength = t;
11379             this.applySort();
11380             this.fireEvent("datachanged", this);
11381         }else{
11382             this.totalLength = Math.max(t, this.data.length+r.length);
11383             this.add(r);
11384         }
11385         
11386         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11387                 
11388             var e = new Roo.data.Record({});
11389
11390             e.set(this.parent.displayField, this.parent.emptyTitle);
11391             e.set(this.parent.valueField, '');
11392
11393             this.insert(0, e);
11394         }
11395             
11396         this.fireEvent("load", this, r, options, o);
11397         if(options.callback){
11398             options.callback.call(options.scope || this, r, options, true);
11399         }
11400     },
11401
11402
11403     /**
11404      * Loads data from a passed data block. A Reader which understands the format of the data
11405      * must have been configured in the constructor.
11406      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11407      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11408      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11409      */
11410     loadData : function(o, append){
11411         var r = this.reader.readRecords(o);
11412         this.loadRecords(r, {add: append}, true);
11413     },
11414
11415     /**
11416      * Gets the number of cached records.
11417      * <p>
11418      * <em>If using paging, this may not be the total size of the dataset. If the data object
11419      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11420      * the data set size</em>
11421      */
11422     getCount : function(){
11423         return this.data.length || 0;
11424     },
11425
11426     /**
11427      * Gets the total number of records in the dataset as returned by the server.
11428      * <p>
11429      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11430      * the dataset size</em>
11431      */
11432     getTotalCount : function(){
11433         return this.totalLength || 0;
11434     },
11435
11436     /**
11437      * Returns the sort state of the Store as an object with two properties:
11438      * <pre><code>
11439  field {String} The name of the field by which the Records are sorted
11440  direction {String} The sort order, "ASC" or "DESC"
11441      * </code></pre>
11442      */
11443     getSortState : function(){
11444         return this.sortInfo;
11445     },
11446
11447     // private
11448     applySort : function(){
11449         if(this.sortInfo && !this.remoteSort){
11450             var s = this.sortInfo, f = s.field;
11451             var st = this.fields.get(f).sortType;
11452             var fn = function(r1, r2){
11453                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11454                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11455             };
11456             this.data.sort(s.direction, fn);
11457             if(this.snapshot && this.snapshot != this.data){
11458                 this.snapshot.sort(s.direction, fn);
11459             }
11460         }
11461     },
11462
11463     /**
11464      * Sets the default sort column and order to be used by the next load operation.
11465      * @param {String} fieldName The name of the field to sort by.
11466      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11467      */
11468     setDefaultSort : function(field, dir){
11469         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11470     },
11471
11472     /**
11473      * Sort the Records.
11474      * If remote sorting is used, the sort is performed on the server, and the cache is
11475      * reloaded. If local sorting is used, the cache is sorted internally.
11476      * @param {String} fieldName The name of the field to sort by.
11477      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11478      */
11479     sort : function(fieldName, dir){
11480         var f = this.fields.get(fieldName);
11481         if(!dir){
11482             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11483             
11484             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11485                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11486             }else{
11487                 dir = f.sortDir;
11488             }
11489         }
11490         this.sortToggle[f.name] = dir;
11491         this.sortInfo = {field: f.name, direction: dir};
11492         if(!this.remoteSort){
11493             this.applySort();
11494             this.fireEvent("datachanged", this);
11495         }else{
11496             this.load(this.lastOptions);
11497         }
11498     },
11499
11500     /**
11501      * Calls the specified function for each of the Records in the cache.
11502      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11503      * Returning <em>false</em> aborts and exits the iteration.
11504      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11505      */
11506     each : function(fn, scope){
11507         this.data.each(fn, scope);
11508     },
11509
11510     /**
11511      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11512      * (e.g., during paging).
11513      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11514      */
11515     getModifiedRecords : function(){
11516         return this.modified;
11517     },
11518
11519     // private
11520     createFilterFn : function(property, value, anyMatch){
11521         if(!value.exec){ // not a regex
11522             value = String(value);
11523             if(value.length == 0){
11524                 return false;
11525             }
11526             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11527         }
11528         return function(r){
11529             return value.test(r.data[property]);
11530         };
11531     },
11532
11533     /**
11534      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11535      * @param {String} property A field on your records
11536      * @param {Number} start The record index to start at (defaults to 0)
11537      * @param {Number} end The last record index to include (defaults to length - 1)
11538      * @return {Number} The sum
11539      */
11540     sum : function(property, start, end){
11541         var rs = this.data.items, v = 0;
11542         start = start || 0;
11543         end = (end || end === 0) ? end : rs.length-1;
11544
11545         for(var i = start; i <= end; i++){
11546             v += (rs[i].data[property] || 0);
11547         }
11548         return v;
11549     },
11550
11551     /**
11552      * Filter the records by a specified property.
11553      * @param {String} field A field on your records
11554      * @param {String/RegExp} value Either a string that the field
11555      * should start with or a RegExp to test against the field
11556      * @param {Boolean} anyMatch True to match any part not just the beginning
11557      */
11558     filter : function(property, value, anyMatch){
11559         var fn = this.createFilterFn(property, value, anyMatch);
11560         return fn ? this.filterBy(fn) : this.clearFilter();
11561     },
11562
11563     /**
11564      * Filter by a function. The specified function will be called with each
11565      * record in this data source. If the function returns true the record is included,
11566      * otherwise it is filtered.
11567      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11568      * @param {Object} scope (optional) The scope of the function (defaults to this)
11569      */
11570     filterBy : function(fn, scope){
11571         this.snapshot = this.snapshot || this.data;
11572         this.data = this.queryBy(fn, scope||this);
11573         this.fireEvent("datachanged", this);
11574     },
11575
11576     /**
11577      * Query the records by a specified property.
11578      * @param {String} field A field on your records
11579      * @param {String/RegExp} value Either a string that the field
11580      * should start with or a RegExp to test against the field
11581      * @param {Boolean} anyMatch True to match any part not just the beginning
11582      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11583      */
11584     query : function(property, value, anyMatch){
11585         var fn = this.createFilterFn(property, value, anyMatch);
11586         return fn ? this.queryBy(fn) : this.data.clone();
11587     },
11588
11589     /**
11590      * Query by a function. The specified function will be called with each
11591      * record in this data source. If the function returns true the record is included
11592      * in the results.
11593      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11594      * @param {Object} scope (optional) The scope of the function (defaults to this)
11595       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11596      **/
11597     queryBy : function(fn, scope){
11598         var data = this.snapshot || this.data;
11599         return data.filterBy(fn, scope||this);
11600     },
11601
11602     /**
11603      * Collects unique values for a particular dataIndex from this store.
11604      * @param {String} dataIndex The property to collect
11605      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11606      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11607      * @return {Array} An array of the unique values
11608      **/
11609     collect : function(dataIndex, allowNull, bypassFilter){
11610         var d = (bypassFilter === true && this.snapshot) ?
11611                 this.snapshot.items : this.data.items;
11612         var v, sv, r = [], l = {};
11613         for(var i = 0, len = d.length; i < len; i++){
11614             v = d[i].data[dataIndex];
11615             sv = String(v);
11616             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11617                 l[sv] = true;
11618                 r[r.length] = v;
11619             }
11620         }
11621         return r;
11622     },
11623
11624     /**
11625      * Revert to a view of the Record cache with no filtering applied.
11626      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11627      */
11628     clearFilter : function(suppressEvent){
11629         if(this.snapshot && this.snapshot != this.data){
11630             this.data = this.snapshot;
11631             delete this.snapshot;
11632             if(suppressEvent !== true){
11633                 this.fireEvent("datachanged", this);
11634             }
11635         }
11636     },
11637
11638     // private
11639     afterEdit : function(record){
11640         if(this.modified.indexOf(record) == -1){
11641             this.modified.push(record);
11642         }
11643         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11644     },
11645     
11646     // private
11647     afterReject : function(record){
11648         this.modified.remove(record);
11649         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11650     },
11651
11652     // private
11653     afterCommit : function(record){
11654         this.modified.remove(record);
11655         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11656     },
11657
11658     /**
11659      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11660      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11661      */
11662     commitChanges : function(){
11663         var m = this.modified.slice(0);
11664         this.modified = [];
11665         for(var i = 0, len = m.length; i < len; i++){
11666             m[i].commit();
11667         }
11668     },
11669
11670     /**
11671      * Cancel outstanding changes on all changed records.
11672      */
11673     rejectChanges : function(){
11674         var m = this.modified.slice(0);
11675         this.modified = [];
11676         for(var i = 0, len = m.length; i < len; i++){
11677             m[i].reject();
11678         }
11679     },
11680
11681     onMetaChange : function(meta, rtype, o){
11682         this.recordType = rtype;
11683         this.fields = rtype.prototype.fields;
11684         delete this.snapshot;
11685         this.sortInfo = meta.sortInfo || this.sortInfo;
11686         this.modified = [];
11687         this.fireEvent('metachange', this, this.reader.meta);
11688     },
11689     
11690     moveIndex : function(data, type)
11691     {
11692         var index = this.indexOf(data);
11693         
11694         var newIndex = index + type;
11695         
11696         this.remove(data);
11697         
11698         this.insert(newIndex, data);
11699         
11700     }
11701 });/*
11702  * Based on:
11703  * Ext JS Library 1.1.1
11704  * Copyright(c) 2006-2007, Ext JS, LLC.
11705  *
11706  * Originally Released Under LGPL - original licence link has changed is not relivant.
11707  *
11708  * Fork - LGPL
11709  * <script type="text/javascript">
11710  */
11711
11712 /**
11713  * @class Roo.data.SimpleStore
11714  * @extends Roo.data.Store
11715  * Small helper class to make creating Stores from Array data easier.
11716  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11717  * @cfg {Array} fields An array of field definition objects, or field name strings.
11718  * @cfg {Array} data The multi-dimensional array of data
11719  * @constructor
11720  * @param {Object} config
11721  */
11722 Roo.data.SimpleStore = function(config){
11723     Roo.data.SimpleStore.superclass.constructor.call(this, {
11724         isLocal : true,
11725         reader: new Roo.data.ArrayReader({
11726                 id: config.id
11727             },
11728             Roo.data.Record.create(config.fields)
11729         ),
11730         proxy : new Roo.data.MemoryProxy(config.data)
11731     });
11732     this.load();
11733 };
11734 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11735  * Based on:
11736  * Ext JS Library 1.1.1
11737  * Copyright(c) 2006-2007, Ext JS, LLC.
11738  *
11739  * Originally Released Under LGPL - original licence link has changed is not relivant.
11740  *
11741  * Fork - LGPL
11742  * <script type="text/javascript">
11743  */
11744
11745 /**
11746 /**
11747  * @extends Roo.data.Store
11748  * @class Roo.data.JsonStore
11749  * Small helper class to make creating Stores for JSON data easier. <br/>
11750 <pre><code>
11751 var store = new Roo.data.JsonStore({
11752     url: 'get-images.php',
11753     root: 'images',
11754     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11755 });
11756 </code></pre>
11757  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11758  * JsonReader and HttpProxy (unless inline data is provided).</b>
11759  * @cfg {Array} fields An array of field definition objects, or field name strings.
11760  * @constructor
11761  * @param {Object} config
11762  */
11763 Roo.data.JsonStore = function(c){
11764     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11765         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11766         reader: new Roo.data.JsonReader(c, c.fields)
11767     }));
11768 };
11769 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11770  * Based on:
11771  * Ext JS Library 1.1.1
11772  * Copyright(c) 2006-2007, Ext JS, LLC.
11773  *
11774  * Originally Released Under LGPL - original licence link has changed is not relivant.
11775  *
11776  * Fork - LGPL
11777  * <script type="text/javascript">
11778  */
11779
11780  
11781 Roo.data.Field = function(config){
11782     if(typeof config == "string"){
11783         config = {name: config};
11784     }
11785     Roo.apply(this, config);
11786     
11787     if(!this.type){
11788         this.type = "auto";
11789     }
11790     
11791     var st = Roo.data.SortTypes;
11792     // named sortTypes are supported, here we look them up
11793     if(typeof this.sortType == "string"){
11794         this.sortType = st[this.sortType];
11795     }
11796     
11797     // set default sortType for strings and dates
11798     if(!this.sortType){
11799         switch(this.type){
11800             case "string":
11801                 this.sortType = st.asUCString;
11802                 break;
11803             case "date":
11804                 this.sortType = st.asDate;
11805                 break;
11806             default:
11807                 this.sortType = st.none;
11808         }
11809     }
11810
11811     // define once
11812     var stripRe = /[\$,%]/g;
11813
11814     // prebuilt conversion function for this field, instead of
11815     // switching every time we're reading a value
11816     if(!this.convert){
11817         var cv, dateFormat = this.dateFormat;
11818         switch(this.type){
11819             case "":
11820             case "auto":
11821             case undefined:
11822                 cv = function(v){ return v; };
11823                 break;
11824             case "string":
11825                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11826                 break;
11827             case "int":
11828                 cv = function(v){
11829                     return v !== undefined && v !== null && v !== '' ?
11830                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11831                     };
11832                 break;
11833             case "float":
11834                 cv = function(v){
11835                     return v !== undefined && v !== null && v !== '' ?
11836                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11837                     };
11838                 break;
11839             case "bool":
11840             case "boolean":
11841                 cv = function(v){ return v === true || v === "true" || v == 1; };
11842                 break;
11843             case "date":
11844                 cv = function(v){
11845                     if(!v){
11846                         return '';
11847                     }
11848                     if(v instanceof Date){
11849                         return v;
11850                     }
11851                     if(dateFormat){
11852                         if(dateFormat == "timestamp"){
11853                             return new Date(v*1000);
11854                         }
11855                         return Date.parseDate(v, dateFormat);
11856                     }
11857                     var parsed = Date.parse(v);
11858                     return parsed ? new Date(parsed) : null;
11859                 };
11860              break;
11861             
11862         }
11863         this.convert = cv;
11864     }
11865 };
11866
11867 Roo.data.Field.prototype = {
11868     dateFormat: null,
11869     defaultValue: "",
11870     mapping: null,
11871     sortType : null,
11872     sortDir : "ASC"
11873 };/*
11874  * Based on:
11875  * Ext JS Library 1.1.1
11876  * Copyright(c) 2006-2007, Ext JS, LLC.
11877  *
11878  * Originally Released Under LGPL - original licence link has changed is not relivant.
11879  *
11880  * Fork - LGPL
11881  * <script type="text/javascript">
11882  */
11883  
11884 // Base class for reading structured data from a data source.  This class is intended to be
11885 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11886
11887 /**
11888  * @class Roo.data.DataReader
11889  * Base class for reading structured data from a data source.  This class is intended to be
11890  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11891  */
11892
11893 Roo.data.DataReader = function(meta, recordType){
11894     
11895     this.meta = meta;
11896     
11897     this.recordType = recordType instanceof Array ? 
11898         Roo.data.Record.create(recordType) : recordType;
11899 };
11900
11901 Roo.data.DataReader.prototype = {
11902      /**
11903      * Create an empty record
11904      * @param {Object} data (optional) - overlay some values
11905      * @return {Roo.data.Record} record created.
11906      */
11907     newRow :  function(d) {
11908         var da =  {};
11909         this.recordType.prototype.fields.each(function(c) {
11910             switch( c.type) {
11911                 case 'int' : da[c.name] = 0; break;
11912                 case 'date' : da[c.name] = new Date(); break;
11913                 case 'float' : da[c.name] = 0.0; break;
11914                 case 'boolean' : da[c.name] = false; break;
11915                 default : da[c.name] = ""; break;
11916             }
11917             
11918         });
11919         return new this.recordType(Roo.apply(da, d));
11920     }
11921     
11922 };/*
11923  * Based on:
11924  * Ext JS Library 1.1.1
11925  * Copyright(c) 2006-2007, Ext JS, LLC.
11926  *
11927  * Originally Released Under LGPL - original licence link has changed is not relivant.
11928  *
11929  * Fork - LGPL
11930  * <script type="text/javascript">
11931  */
11932
11933 /**
11934  * @class Roo.data.DataProxy
11935  * @extends Roo.data.Observable
11936  * This class is an abstract base class for implementations which provide retrieval of
11937  * unformatted data objects.<br>
11938  * <p>
11939  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11940  * (of the appropriate type which knows how to parse the data object) to provide a block of
11941  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11942  * <p>
11943  * Custom implementations must implement the load method as described in
11944  * {@link Roo.data.HttpProxy#load}.
11945  */
11946 Roo.data.DataProxy = function(){
11947     this.addEvents({
11948         /**
11949          * @event beforeload
11950          * Fires before a network request is made to retrieve a data object.
11951          * @param {Object} This DataProxy object.
11952          * @param {Object} params The params parameter to the load function.
11953          */
11954         beforeload : true,
11955         /**
11956          * @event load
11957          * Fires before the load method's callback is called.
11958          * @param {Object} This DataProxy object.
11959          * @param {Object} o The data object.
11960          * @param {Object} arg The callback argument object passed to the load function.
11961          */
11962         load : true,
11963         /**
11964          * @event loadexception
11965          * Fires if an Exception occurs during data retrieval.
11966          * @param {Object} This DataProxy object.
11967          * @param {Object} o The data object.
11968          * @param {Object} arg The callback argument object passed to the load function.
11969          * @param {Object} e The Exception.
11970          */
11971         loadexception : true
11972     });
11973     Roo.data.DataProxy.superclass.constructor.call(this);
11974 };
11975
11976 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11977
11978     /**
11979      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11980      */
11981 /*
11982  * Based on:
11983  * Ext JS Library 1.1.1
11984  * Copyright(c) 2006-2007, Ext JS, LLC.
11985  *
11986  * Originally Released Under LGPL - original licence link has changed is not relivant.
11987  *
11988  * Fork - LGPL
11989  * <script type="text/javascript">
11990  */
11991 /**
11992  * @class Roo.data.MemoryProxy
11993  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11994  * to the Reader when its load method is called.
11995  * @constructor
11996  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11997  */
11998 Roo.data.MemoryProxy = function(data){
11999     if (data.data) {
12000         data = data.data;
12001     }
12002     Roo.data.MemoryProxy.superclass.constructor.call(this);
12003     this.data = data;
12004 };
12005
12006 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12007     
12008     /**
12009      * Load data from the requested source (in this case an in-memory
12010      * data object passed to the constructor), read the data object into
12011      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12012      * process that block using the passed callback.
12013      * @param {Object} params This parameter is not used by the MemoryProxy class.
12014      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12015      * object into a block of Roo.data.Records.
12016      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12017      * The function must be passed <ul>
12018      * <li>The Record block object</li>
12019      * <li>The "arg" argument from the load function</li>
12020      * <li>A boolean success indicator</li>
12021      * </ul>
12022      * @param {Object} scope The scope in which to call the callback
12023      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12024      */
12025     load : function(params, reader, callback, scope, arg){
12026         params = params || {};
12027         var result;
12028         try {
12029             result = reader.readRecords(this.data);
12030         }catch(e){
12031             this.fireEvent("loadexception", this, arg, null, e);
12032             callback.call(scope, null, arg, false);
12033             return;
12034         }
12035         callback.call(scope, result, arg, true);
12036     },
12037     
12038     // private
12039     update : function(params, records){
12040         
12041     }
12042 });/*
12043  * Based on:
12044  * Ext JS Library 1.1.1
12045  * Copyright(c) 2006-2007, Ext JS, LLC.
12046  *
12047  * Originally Released Under LGPL - original licence link has changed is not relivant.
12048  *
12049  * Fork - LGPL
12050  * <script type="text/javascript">
12051  */
12052 /**
12053  * @class Roo.data.HttpProxy
12054  * @extends Roo.data.DataProxy
12055  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12056  * configured to reference a certain URL.<br><br>
12057  * <p>
12058  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12059  * from which the running page was served.<br><br>
12060  * <p>
12061  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12062  * <p>
12063  * Be aware that to enable the browser to parse an XML document, the server must set
12064  * the Content-Type header in the HTTP response to "text/xml".
12065  * @constructor
12066  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12067  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12068  * will be used to make the request.
12069  */
12070 Roo.data.HttpProxy = function(conn){
12071     Roo.data.HttpProxy.superclass.constructor.call(this);
12072     // is conn a conn config or a real conn?
12073     this.conn = conn;
12074     this.useAjax = !conn || !conn.events;
12075   
12076 };
12077
12078 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12079     // thse are take from connection...
12080     
12081     /**
12082      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12083      */
12084     /**
12085      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12086      * extra parameters to each request made by this object. (defaults to undefined)
12087      */
12088     /**
12089      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12090      *  to each request made by this object. (defaults to undefined)
12091      */
12092     /**
12093      * @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)
12094      */
12095     /**
12096      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12097      */
12098      /**
12099      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12100      * @type Boolean
12101      */
12102   
12103
12104     /**
12105      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12106      * @type Boolean
12107      */
12108     /**
12109      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12110      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12111      * a finer-grained basis than the DataProxy events.
12112      */
12113     getConnection : function(){
12114         return this.useAjax ? Roo.Ajax : this.conn;
12115     },
12116
12117     /**
12118      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12119      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12120      * process that block using the passed callback.
12121      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12122      * for the request to the remote server.
12123      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12124      * object into a block of Roo.data.Records.
12125      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12126      * The function must be passed <ul>
12127      * <li>The Record block object</li>
12128      * <li>The "arg" argument from the load function</li>
12129      * <li>A boolean success indicator</li>
12130      * </ul>
12131      * @param {Object} scope The scope in which to call the callback
12132      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12133      */
12134     load : function(params, reader, callback, scope, arg){
12135         if(this.fireEvent("beforeload", this, params) !== false){
12136             var  o = {
12137                 params : params || {},
12138                 request: {
12139                     callback : callback,
12140                     scope : scope,
12141                     arg : arg
12142                 },
12143                 reader: reader,
12144                 callback : this.loadResponse,
12145                 scope: this
12146             };
12147             if(this.useAjax){
12148                 Roo.applyIf(o, this.conn);
12149                 if(this.activeRequest){
12150                     Roo.Ajax.abort(this.activeRequest);
12151                 }
12152                 this.activeRequest = Roo.Ajax.request(o);
12153             }else{
12154                 this.conn.request(o);
12155             }
12156         }else{
12157             callback.call(scope||this, null, arg, false);
12158         }
12159     },
12160
12161     // private
12162     loadResponse : function(o, success, response){
12163         delete this.activeRequest;
12164         if(!success){
12165             this.fireEvent("loadexception", this, o, response);
12166             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12167             return;
12168         }
12169         var result;
12170         try {
12171             result = o.reader.read(response);
12172         }catch(e){
12173             this.fireEvent("loadexception", this, o, response, e);
12174             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12175             return;
12176         }
12177         
12178         this.fireEvent("load", this, o, o.request.arg);
12179         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12180     },
12181
12182     // private
12183     update : function(dataSet){
12184
12185     },
12186
12187     // private
12188     updateResponse : function(dataSet){
12189
12190     }
12191 });/*
12192  * Based on:
12193  * Ext JS Library 1.1.1
12194  * Copyright(c) 2006-2007, Ext JS, LLC.
12195  *
12196  * Originally Released Under LGPL - original licence link has changed is not relivant.
12197  *
12198  * Fork - LGPL
12199  * <script type="text/javascript">
12200  */
12201
12202 /**
12203  * @class Roo.data.ScriptTagProxy
12204  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12205  * other than the originating domain of the running page.<br><br>
12206  * <p>
12207  * <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
12208  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12209  * <p>
12210  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12211  * source code that is used as the source inside a &lt;script> tag.<br><br>
12212  * <p>
12213  * In order for the browser to process the returned data, the server must wrap the data object
12214  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12215  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12216  * depending on whether the callback name was passed:
12217  * <p>
12218  * <pre><code>
12219 boolean scriptTag = false;
12220 String cb = request.getParameter("callback");
12221 if (cb != null) {
12222     scriptTag = true;
12223     response.setContentType("text/javascript");
12224 } else {
12225     response.setContentType("application/x-json");
12226 }
12227 Writer out = response.getWriter();
12228 if (scriptTag) {
12229     out.write(cb + "(");
12230 }
12231 out.print(dataBlock.toJsonString());
12232 if (scriptTag) {
12233     out.write(");");
12234 }
12235 </pre></code>
12236  *
12237  * @constructor
12238  * @param {Object} config A configuration object.
12239  */
12240 Roo.data.ScriptTagProxy = function(config){
12241     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12242     Roo.apply(this, config);
12243     this.head = document.getElementsByTagName("head")[0];
12244 };
12245
12246 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12247
12248 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12249     /**
12250      * @cfg {String} url The URL from which to request the data object.
12251      */
12252     /**
12253      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12254      */
12255     timeout : 30000,
12256     /**
12257      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12258      * the server the name of the callback function set up by the load call to process the returned data object.
12259      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12260      * javascript output which calls this named function passing the data object as its only parameter.
12261      */
12262     callbackParam : "callback",
12263     /**
12264      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12265      * name to the request.
12266      */
12267     nocache : true,
12268
12269     /**
12270      * Load data from the configured URL, read the data object into
12271      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12272      * process that block using the passed callback.
12273      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12274      * for the request to the remote server.
12275      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12276      * object into a block of Roo.data.Records.
12277      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12278      * The function must be passed <ul>
12279      * <li>The Record block object</li>
12280      * <li>The "arg" argument from the load function</li>
12281      * <li>A boolean success indicator</li>
12282      * </ul>
12283      * @param {Object} scope The scope in which to call the callback
12284      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12285      */
12286     load : function(params, reader, callback, scope, arg){
12287         if(this.fireEvent("beforeload", this, params) !== false){
12288
12289             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12290
12291             var url = this.url;
12292             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12293             if(this.nocache){
12294                 url += "&_dc=" + (new Date().getTime());
12295             }
12296             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12297             var trans = {
12298                 id : transId,
12299                 cb : "stcCallback"+transId,
12300                 scriptId : "stcScript"+transId,
12301                 params : params,
12302                 arg : arg,
12303                 url : url,
12304                 callback : callback,
12305                 scope : scope,
12306                 reader : reader
12307             };
12308             var conn = this;
12309
12310             window[trans.cb] = function(o){
12311                 conn.handleResponse(o, trans);
12312             };
12313
12314             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12315
12316             if(this.autoAbort !== false){
12317                 this.abort();
12318             }
12319
12320             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12321
12322             var script = document.createElement("script");
12323             script.setAttribute("src", url);
12324             script.setAttribute("type", "text/javascript");
12325             script.setAttribute("id", trans.scriptId);
12326             this.head.appendChild(script);
12327
12328             this.trans = trans;
12329         }else{
12330             callback.call(scope||this, null, arg, false);
12331         }
12332     },
12333
12334     // private
12335     isLoading : function(){
12336         return this.trans ? true : false;
12337     },
12338
12339     /**
12340      * Abort the current server request.
12341      */
12342     abort : function(){
12343         if(this.isLoading()){
12344             this.destroyTrans(this.trans);
12345         }
12346     },
12347
12348     // private
12349     destroyTrans : function(trans, isLoaded){
12350         this.head.removeChild(document.getElementById(trans.scriptId));
12351         clearTimeout(trans.timeoutId);
12352         if(isLoaded){
12353             window[trans.cb] = undefined;
12354             try{
12355                 delete window[trans.cb];
12356             }catch(e){}
12357         }else{
12358             // if hasn't been loaded, wait for load to remove it to prevent script error
12359             window[trans.cb] = function(){
12360                 window[trans.cb] = undefined;
12361                 try{
12362                     delete window[trans.cb];
12363                 }catch(e){}
12364             };
12365         }
12366     },
12367
12368     // private
12369     handleResponse : function(o, trans){
12370         this.trans = false;
12371         this.destroyTrans(trans, true);
12372         var result;
12373         try {
12374             result = trans.reader.readRecords(o);
12375         }catch(e){
12376             this.fireEvent("loadexception", this, o, trans.arg, e);
12377             trans.callback.call(trans.scope||window, null, trans.arg, false);
12378             return;
12379         }
12380         this.fireEvent("load", this, o, trans.arg);
12381         trans.callback.call(trans.scope||window, result, trans.arg, true);
12382     },
12383
12384     // private
12385     handleFailure : function(trans){
12386         this.trans = false;
12387         this.destroyTrans(trans, false);
12388         this.fireEvent("loadexception", this, null, trans.arg);
12389         trans.callback.call(trans.scope||window, null, trans.arg, false);
12390     }
12391 });/*
12392  * Based on:
12393  * Ext JS Library 1.1.1
12394  * Copyright(c) 2006-2007, Ext JS, LLC.
12395  *
12396  * Originally Released Under LGPL - original licence link has changed is not relivant.
12397  *
12398  * Fork - LGPL
12399  * <script type="text/javascript">
12400  */
12401
12402 /**
12403  * @class Roo.data.JsonReader
12404  * @extends Roo.data.DataReader
12405  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12406  * based on mappings in a provided Roo.data.Record constructor.
12407  * 
12408  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12409  * in the reply previously. 
12410  * 
12411  * <p>
12412  * Example code:
12413  * <pre><code>
12414 var RecordDef = Roo.data.Record.create([
12415     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12416     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12417 ]);
12418 var myReader = new Roo.data.JsonReader({
12419     totalProperty: "results",    // The property which contains the total dataset size (optional)
12420     root: "rows",                // The property which contains an Array of row objects
12421     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12422 }, RecordDef);
12423 </code></pre>
12424  * <p>
12425  * This would consume a JSON file like this:
12426  * <pre><code>
12427 { 'results': 2, 'rows': [
12428     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12429     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12430 }
12431 </code></pre>
12432  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12433  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12434  * paged from the remote server.
12435  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12436  * @cfg {String} root name of the property which contains the Array of row objects.
12437  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12438  * @cfg {Array} fields Array of field definition objects
12439  * @constructor
12440  * Create a new JsonReader
12441  * @param {Object} meta Metadata configuration options
12442  * @param {Object} recordType Either an Array of field definition objects,
12443  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12444  */
12445 Roo.data.JsonReader = function(meta, recordType){
12446     
12447     meta = meta || {};
12448     // set some defaults:
12449     Roo.applyIf(meta, {
12450         totalProperty: 'total',
12451         successProperty : 'success',
12452         root : 'data',
12453         id : 'id'
12454     });
12455     
12456     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12457 };
12458 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12459     
12460     /**
12461      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12462      * Used by Store query builder to append _requestMeta to params.
12463      * 
12464      */
12465     metaFromRemote : false,
12466     /**
12467      * This method is only used by a DataProxy which has retrieved data from a remote server.
12468      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12469      * @return {Object} data A data block which is used by an Roo.data.Store object as
12470      * a cache of Roo.data.Records.
12471      */
12472     read : function(response){
12473         var json = response.responseText;
12474        
12475         var o = /* eval:var:o */ eval("("+json+")");
12476         if(!o) {
12477             throw {message: "JsonReader.read: Json object not found"};
12478         }
12479         
12480         if(o.metaData){
12481             
12482             delete this.ef;
12483             this.metaFromRemote = true;
12484             this.meta = o.metaData;
12485             this.recordType = Roo.data.Record.create(o.metaData.fields);
12486             this.onMetaChange(this.meta, this.recordType, o);
12487         }
12488         return this.readRecords(o);
12489     },
12490
12491     // private function a store will implement
12492     onMetaChange : function(meta, recordType, o){
12493
12494     },
12495
12496     /**
12497          * @ignore
12498          */
12499     simpleAccess: function(obj, subsc) {
12500         return obj[subsc];
12501     },
12502
12503         /**
12504          * @ignore
12505          */
12506     getJsonAccessor: function(){
12507         var re = /[\[\.]/;
12508         return function(expr) {
12509             try {
12510                 return(re.test(expr))
12511                     ? new Function("obj", "return obj." + expr)
12512                     : function(obj){
12513                         return obj[expr];
12514                     };
12515             } catch(e){}
12516             return Roo.emptyFn;
12517         };
12518     }(),
12519
12520     /**
12521      * Create a data block containing Roo.data.Records from an XML document.
12522      * @param {Object} o An object which contains an Array of row objects in the property specified
12523      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12524      * which contains the total size of the dataset.
12525      * @return {Object} data A data block which is used by an Roo.data.Store object as
12526      * a cache of Roo.data.Records.
12527      */
12528     readRecords : function(o){
12529         /**
12530          * After any data loads, the raw JSON data is available for further custom processing.
12531          * @type Object
12532          */
12533         this.o = o;
12534         var s = this.meta, Record = this.recordType,
12535             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12536
12537 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12538         if (!this.ef) {
12539             if(s.totalProperty) {
12540                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12541                 }
12542                 if(s.successProperty) {
12543                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12544                 }
12545                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12546                 if (s.id) {
12547                         var g = this.getJsonAccessor(s.id);
12548                         this.getId = function(rec) {
12549                                 var r = g(rec);  
12550                                 return (r === undefined || r === "") ? null : r;
12551                         };
12552                 } else {
12553                         this.getId = function(){return null;};
12554                 }
12555             this.ef = [];
12556             for(var jj = 0; jj < fl; jj++){
12557                 f = fi[jj];
12558                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12559                 this.ef[jj] = this.getJsonAccessor(map);
12560             }
12561         }
12562
12563         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12564         if(s.totalProperty){
12565             var vt = parseInt(this.getTotal(o), 10);
12566             if(!isNaN(vt)){
12567                 totalRecords = vt;
12568             }
12569         }
12570         if(s.successProperty){
12571             var vs = this.getSuccess(o);
12572             if(vs === false || vs === 'false'){
12573                 success = false;
12574             }
12575         }
12576         var records = [];
12577         for(var i = 0; i < c; i++){
12578                 var n = root[i];
12579             var values = {};
12580             var id = this.getId(n);
12581             for(var j = 0; j < fl; j++){
12582                 f = fi[j];
12583             var v = this.ef[j](n);
12584             if (!f.convert) {
12585                 Roo.log('missing convert for ' + f.name);
12586                 Roo.log(f);
12587                 continue;
12588             }
12589             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12590             }
12591             var record = new Record(values, id);
12592             record.json = n;
12593             records[i] = record;
12594         }
12595         return {
12596             raw : o,
12597             success : success,
12598             records : records,
12599             totalRecords : totalRecords
12600         };
12601     }
12602 });/*
12603  * Based on:
12604  * Ext JS Library 1.1.1
12605  * Copyright(c) 2006-2007, Ext JS, LLC.
12606  *
12607  * Originally Released Under LGPL - original licence link has changed is not relivant.
12608  *
12609  * Fork - LGPL
12610  * <script type="text/javascript">
12611  */
12612
12613 /**
12614  * @class Roo.data.ArrayReader
12615  * @extends Roo.data.DataReader
12616  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12617  * Each element of that Array represents a row of data fields. The
12618  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12619  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12620  * <p>
12621  * Example code:.
12622  * <pre><code>
12623 var RecordDef = Roo.data.Record.create([
12624     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12625     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12626 ]);
12627 var myReader = new Roo.data.ArrayReader({
12628     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12629 }, RecordDef);
12630 </code></pre>
12631  * <p>
12632  * This would consume an Array like this:
12633  * <pre><code>
12634 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12635   </code></pre>
12636  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12637  * @constructor
12638  * Create a new JsonReader
12639  * @param {Object} meta Metadata configuration options.
12640  * @param {Object} recordType Either an Array of field definition objects
12641  * as specified to {@link Roo.data.Record#create},
12642  * or an {@link Roo.data.Record} object
12643  * created using {@link Roo.data.Record#create}.
12644  */
12645 Roo.data.ArrayReader = function(meta, recordType){
12646     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12647 };
12648
12649 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12650     /**
12651      * Create a data block containing Roo.data.Records from an XML document.
12652      * @param {Object} o An Array of row objects which represents the dataset.
12653      * @return {Object} data A data block which is used by an Roo.data.Store object as
12654      * a cache of Roo.data.Records.
12655      */
12656     readRecords : function(o){
12657         var sid = this.meta ? this.meta.id : null;
12658         var recordType = this.recordType, fields = recordType.prototype.fields;
12659         var records = [];
12660         var root = o;
12661             for(var i = 0; i < root.length; i++){
12662                     var n = root[i];
12663                 var values = {};
12664                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12665                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12666                 var f = fields.items[j];
12667                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12668                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12669                 v = f.convert(v);
12670                 values[f.name] = v;
12671             }
12672                 var record = new recordType(values, id);
12673                 record.json = n;
12674                 records[records.length] = record;
12675             }
12676             return {
12677                 records : records,
12678                 totalRecords : records.length
12679             };
12680     }
12681 });/*
12682  * - LGPL
12683  * * 
12684  */
12685
12686 /**
12687  * @class Roo.bootstrap.ComboBox
12688  * @extends Roo.bootstrap.TriggerField
12689  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12690  * @cfg {Boolean} append (true|false) default false
12691  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12692  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12693  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12694  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12695  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12696  * @cfg {Boolean} animate default true
12697  * @cfg {Boolean} emptyResultText only for touch device
12698  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12699  * @cfg {String} emptyTitle default ''
12700  * @constructor
12701  * Create a new ComboBox.
12702  * @param {Object} config Configuration options
12703  */
12704 Roo.bootstrap.ComboBox = function(config){
12705     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12706     this.addEvents({
12707         /**
12708          * @event expand
12709          * Fires when the dropdown list is expanded
12710         * @param {Roo.bootstrap.ComboBox} combo This combo box
12711         */
12712         'expand' : true,
12713         /**
12714          * @event collapse
12715          * Fires when the dropdown list is collapsed
12716         * @param {Roo.bootstrap.ComboBox} combo This combo box
12717         */
12718         'collapse' : true,
12719         /**
12720          * @event beforeselect
12721          * Fires before a list item is selected. Return false to cancel the selection.
12722         * @param {Roo.bootstrap.ComboBox} combo This combo box
12723         * @param {Roo.data.Record} record The data record returned from the underlying store
12724         * @param {Number} index The index of the selected item in the dropdown list
12725         */
12726         'beforeselect' : true,
12727         /**
12728          * @event select
12729          * Fires when a list item is selected
12730         * @param {Roo.bootstrap.ComboBox} combo This combo box
12731         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12732         * @param {Number} index The index of the selected item in the dropdown list
12733         */
12734         'select' : true,
12735         /**
12736          * @event beforequery
12737          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12738          * The event object passed has these properties:
12739         * @param {Roo.bootstrap.ComboBox} combo This combo box
12740         * @param {String} query The query
12741         * @param {Boolean} forceAll true to force "all" query
12742         * @param {Boolean} cancel true to cancel the query
12743         * @param {Object} e The query event object
12744         */
12745         'beforequery': true,
12746          /**
12747          * @event add
12748          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12749         * @param {Roo.bootstrap.ComboBox} combo This combo box
12750         */
12751         'add' : true,
12752         /**
12753          * @event edit
12754          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12755         * @param {Roo.bootstrap.ComboBox} combo This combo box
12756         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12757         */
12758         'edit' : true,
12759         /**
12760          * @event remove
12761          * Fires when the remove value from the combobox array
12762         * @param {Roo.bootstrap.ComboBox} combo This combo box
12763         */
12764         'remove' : true,
12765         /**
12766          * @event afterremove
12767          * Fires when the remove value from the combobox array
12768         * @param {Roo.bootstrap.ComboBox} combo This combo box
12769         */
12770         'afterremove' : true,
12771         /**
12772          * @event specialfilter
12773          * Fires when specialfilter
12774             * @param {Roo.bootstrap.ComboBox} combo This combo box
12775             */
12776         'specialfilter' : true,
12777         /**
12778          * @event tick
12779          * Fires when tick the element
12780             * @param {Roo.bootstrap.ComboBox} combo This combo box
12781             */
12782         'tick' : true,
12783         /**
12784          * @event touchviewdisplay
12785          * Fires when touch view require special display (default is using displayField)
12786             * @param {Roo.bootstrap.ComboBox} combo This combo box
12787             * @param {Object} cfg set html .
12788             */
12789         'touchviewdisplay' : true
12790         
12791     });
12792     
12793     this.item = [];
12794     this.tickItems = [];
12795     
12796     this.selectedIndex = -1;
12797     if(this.mode == 'local'){
12798         if(config.queryDelay === undefined){
12799             this.queryDelay = 10;
12800         }
12801         if(config.minChars === undefined){
12802             this.minChars = 0;
12803         }
12804     }
12805 };
12806
12807 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12808      
12809     /**
12810      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12811      * rendering into an Roo.Editor, defaults to false)
12812      */
12813     /**
12814      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12815      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12816      */
12817     /**
12818      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12819      */
12820     /**
12821      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12822      * the dropdown list (defaults to undefined, with no header element)
12823      */
12824
12825      /**
12826      * @cfg {String/Roo.Template} tpl The template to use to render the output
12827      */
12828      
12829      /**
12830      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12831      */
12832     listWidth: undefined,
12833     /**
12834      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12835      * mode = 'remote' or 'text' if mode = 'local')
12836      */
12837     displayField: undefined,
12838     
12839     /**
12840      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12841      * mode = 'remote' or 'value' if mode = 'local'). 
12842      * Note: use of a valueField requires the user make a selection
12843      * in order for a value to be mapped.
12844      */
12845     valueField: undefined,
12846     /**
12847      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12848      */
12849     modalTitle : '',
12850     
12851     /**
12852      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12853      * field's data value (defaults to the underlying DOM element's name)
12854      */
12855     hiddenName: undefined,
12856     /**
12857      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12858      */
12859     listClass: '',
12860     /**
12861      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12862      */
12863     selectedClass: 'active',
12864     
12865     /**
12866      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12867      */
12868     shadow:'sides',
12869     /**
12870      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12871      * anchor positions (defaults to 'tl-bl')
12872      */
12873     listAlign: 'tl-bl?',
12874     /**
12875      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12876      */
12877     maxHeight: 300,
12878     /**
12879      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12880      * query specified by the allQuery config option (defaults to 'query')
12881      */
12882     triggerAction: 'query',
12883     /**
12884      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12885      * (defaults to 4, does not apply if editable = false)
12886      */
12887     minChars : 4,
12888     /**
12889      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12890      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12891      */
12892     typeAhead: false,
12893     /**
12894      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12895      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12896      */
12897     queryDelay: 500,
12898     /**
12899      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12900      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12901      */
12902     pageSize: 0,
12903     /**
12904      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12905      * when editable = true (defaults to false)
12906      */
12907     selectOnFocus:false,
12908     /**
12909      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12910      */
12911     queryParam: 'query',
12912     /**
12913      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12914      * when mode = 'remote' (defaults to 'Loading...')
12915      */
12916     loadingText: 'Loading...',
12917     /**
12918      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12919      */
12920     resizable: false,
12921     /**
12922      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12923      */
12924     handleHeight : 8,
12925     /**
12926      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12927      * traditional select (defaults to true)
12928      */
12929     editable: true,
12930     /**
12931      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12932      */
12933     allQuery: '',
12934     /**
12935      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12936      */
12937     mode: 'remote',
12938     /**
12939      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12940      * listWidth has a higher value)
12941      */
12942     minListWidth : 70,
12943     /**
12944      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12945      * allow the user to set arbitrary text into the field (defaults to false)
12946      */
12947     forceSelection:false,
12948     /**
12949      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12950      * if typeAhead = true (defaults to 250)
12951      */
12952     typeAheadDelay : 250,
12953     /**
12954      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12955      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12956      */
12957     valueNotFoundText : undefined,
12958     /**
12959      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12960      */
12961     blockFocus : false,
12962     
12963     /**
12964      * @cfg {Boolean} disableClear Disable showing of clear button.
12965      */
12966     disableClear : false,
12967     /**
12968      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12969      */
12970     alwaysQuery : false,
12971     
12972     /**
12973      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12974      */
12975     multiple : false,
12976     
12977     /**
12978      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12979      */
12980     invalidClass : "has-warning",
12981     
12982     /**
12983      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12984      */
12985     validClass : "has-success",
12986     
12987     /**
12988      * @cfg {Boolean} specialFilter (true|false) special filter default false
12989      */
12990     specialFilter : false,
12991     
12992     /**
12993      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12994      */
12995     mobileTouchView : true,
12996     
12997     /**
12998      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12999      */
13000     useNativeIOS : false,
13001     
13002     ios_options : false,
13003     
13004     //private
13005     addicon : false,
13006     editicon: false,
13007     
13008     page: 0,
13009     hasQuery: false,
13010     append: false,
13011     loadNext: false,
13012     autoFocus : true,
13013     tickable : false,
13014     btnPosition : 'right',
13015     triggerList : true,
13016     showToggleBtn : true,
13017     animate : true,
13018     emptyResultText: 'Empty',
13019     triggerText : 'Select',
13020     emptyTitle : '',
13021     
13022     // element that contains real text value.. (when hidden is used..)
13023     
13024     getAutoCreate : function()
13025     {   
13026         var cfg = false;
13027         //render
13028         /*
13029          * Render classic select for iso
13030          */
13031         
13032         if(Roo.isIOS && this.useNativeIOS){
13033             cfg = this.getAutoCreateNativeIOS();
13034             return cfg;
13035         }
13036         
13037         /*
13038          * Touch Devices
13039          */
13040         
13041         if(Roo.isTouch && this.mobileTouchView){
13042             cfg = this.getAutoCreateTouchView();
13043             return cfg;;
13044         }
13045         
13046         /*
13047          *  Normal ComboBox
13048          */
13049         if(!this.tickable){
13050             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13051             return cfg;
13052         }
13053         
13054         /*
13055          *  ComboBox with tickable selections
13056          */
13057              
13058         var align = this.labelAlign || this.parentLabelAlign();
13059         
13060         cfg = {
13061             cls : 'form-group roo-combobox-tickable' //input-group
13062         };
13063         
13064         var btn_text_select = '';
13065         var btn_text_done = '';
13066         var btn_text_cancel = '';
13067         
13068         if (this.btn_text_show) {
13069             btn_text_select = 'Select';
13070             btn_text_done = 'Done';
13071             btn_text_cancel = 'Cancel'; 
13072         }
13073         
13074         var buttons = {
13075             tag : 'div',
13076             cls : 'tickable-buttons',
13077             cn : [
13078                 {
13079                     tag : 'button',
13080                     type : 'button',
13081                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13082                     //html : this.triggerText
13083                     html: btn_text_select
13084                 },
13085                 {
13086                     tag : 'button',
13087                     type : 'button',
13088                     name : 'ok',
13089                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13090                     //html : 'Done'
13091                     html: btn_text_done
13092                 },
13093                 {
13094                     tag : 'button',
13095                     type : 'button',
13096                     name : 'cancel',
13097                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13098                     //html : 'Cancel'
13099                     html: btn_text_cancel
13100                 }
13101             ]
13102         };
13103         
13104         if(this.editable){
13105             buttons.cn.unshift({
13106                 tag: 'input',
13107                 cls: 'roo-select2-search-field-input'
13108             });
13109         }
13110         
13111         var _this = this;
13112         
13113         Roo.each(buttons.cn, function(c){
13114             if (_this.size) {
13115                 c.cls += ' btn-' + _this.size;
13116             }
13117
13118             if (_this.disabled) {
13119                 c.disabled = true;
13120             }
13121         });
13122         
13123         var box = {
13124             tag: 'div',
13125             cn: [
13126                 {
13127                     tag: 'input',
13128                     type : 'hidden',
13129                     cls: 'form-hidden-field'
13130                 },
13131                 {
13132                     tag: 'ul',
13133                     cls: 'roo-select2-choices',
13134                     cn:[
13135                         {
13136                             tag: 'li',
13137                             cls: 'roo-select2-search-field',
13138                             cn: [
13139                                 buttons
13140                             ]
13141                         }
13142                     ]
13143                 }
13144             ]
13145         };
13146         
13147         var combobox = {
13148             cls: 'roo-select2-container input-group roo-select2-container-multi',
13149             cn: [
13150                 box
13151 //                {
13152 //                    tag: 'ul',
13153 //                    cls: 'typeahead typeahead-long dropdown-menu',
13154 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13155 //                }
13156             ]
13157         };
13158         
13159         if(this.hasFeedback && !this.allowBlank){
13160             
13161             var feedback = {
13162                 tag: 'span',
13163                 cls: 'glyphicon form-control-feedback'
13164             };
13165
13166             combobox.cn.push(feedback);
13167         }
13168         
13169         
13170         if (align ==='left' && this.fieldLabel.length) {
13171             
13172             cfg.cls += ' roo-form-group-label-left';
13173             
13174             cfg.cn = [
13175                 {
13176                     tag : 'i',
13177                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13178                     tooltip : 'This field is required'
13179                 },
13180                 {
13181                     tag: 'label',
13182                     'for' :  id,
13183                     cls : 'control-label',
13184                     html : this.fieldLabel
13185
13186                 },
13187                 {
13188                     cls : "", 
13189                     cn: [
13190                         combobox
13191                     ]
13192                 }
13193
13194             ];
13195             
13196             var labelCfg = cfg.cn[1];
13197             var contentCfg = cfg.cn[2];
13198             
13199
13200             if(this.indicatorpos == 'right'){
13201                 
13202                 cfg.cn = [
13203                     {
13204                         tag: 'label',
13205                         'for' :  id,
13206                         cls : 'control-label',
13207                         cn : [
13208                             {
13209                                 tag : 'span',
13210                                 html : this.fieldLabel
13211                             },
13212                             {
13213                                 tag : 'i',
13214                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13215                                 tooltip : 'This field is required'
13216                             }
13217                         ]
13218                     },
13219                     {
13220                         cls : "",
13221                         cn: [
13222                             combobox
13223                         ]
13224                     }
13225
13226                 ];
13227                 
13228                 
13229                 
13230                 labelCfg = cfg.cn[0];
13231                 contentCfg = cfg.cn[1];
13232             
13233             }
13234             
13235             if(this.labelWidth > 12){
13236                 labelCfg.style = "width: " + this.labelWidth + 'px';
13237             }
13238             
13239             if(this.labelWidth < 13 && this.labelmd == 0){
13240                 this.labelmd = this.labelWidth;
13241             }
13242             
13243             if(this.labellg > 0){
13244                 labelCfg.cls += ' col-lg-' + this.labellg;
13245                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13246             }
13247             
13248             if(this.labelmd > 0){
13249                 labelCfg.cls += ' col-md-' + this.labelmd;
13250                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13251             }
13252             
13253             if(this.labelsm > 0){
13254                 labelCfg.cls += ' col-sm-' + this.labelsm;
13255                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13256             }
13257             
13258             if(this.labelxs > 0){
13259                 labelCfg.cls += ' col-xs-' + this.labelxs;
13260                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13261             }
13262                 
13263                 
13264         } else if ( this.fieldLabel.length) {
13265 //                Roo.log(" label");
13266                  cfg.cn = [
13267                     {
13268                         tag : 'i',
13269                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13270                         tooltip : 'This field is required'
13271                     },
13272                     {
13273                         tag: 'label',
13274                         //cls : 'input-group-addon',
13275                         html : this.fieldLabel
13276                     },
13277                     combobox
13278                 ];
13279                 
13280                 if(this.indicatorpos == 'right'){
13281                     cfg.cn = [
13282                         {
13283                             tag: 'label',
13284                             //cls : 'input-group-addon',
13285                             html : this.fieldLabel
13286                         },
13287                         {
13288                             tag : 'i',
13289                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13290                             tooltip : 'This field is required'
13291                         },
13292                         combobox
13293                     ];
13294                     
13295                 }
13296
13297         } else {
13298             
13299 //                Roo.log(" no label && no align");
13300                 cfg = combobox
13301                      
13302                 
13303         }
13304          
13305         var settings=this;
13306         ['xs','sm','md','lg'].map(function(size){
13307             if (settings[size]) {
13308                 cfg.cls += ' col-' + size + '-' + settings[size];
13309             }
13310         });
13311         
13312         return cfg;
13313         
13314     },
13315     
13316     _initEventsCalled : false,
13317     
13318     // private
13319     initEvents: function()
13320     {   
13321         if (this._initEventsCalled) { // as we call render... prevent looping...
13322             return;
13323         }
13324         this._initEventsCalled = true;
13325         
13326         if (!this.store) {
13327             throw "can not find store for combo";
13328         }
13329         
13330         this.indicator = this.indicatorEl();
13331         
13332         this.store = Roo.factory(this.store, Roo.data);
13333         this.store.parent = this;
13334         
13335         // if we are building from html. then this element is so complex, that we can not really
13336         // use the rendered HTML.
13337         // so we have to trash and replace the previous code.
13338         if (Roo.XComponent.build_from_html) {
13339             // remove this element....
13340             var e = this.el.dom, k=0;
13341             while (e ) { e = e.previousSibling;  ++k;}
13342
13343             this.el.remove();
13344             
13345             this.el=false;
13346             this.rendered = false;
13347             
13348             this.render(this.parent().getChildContainer(true), k);
13349         }
13350         
13351         if(Roo.isIOS && this.useNativeIOS){
13352             this.initIOSView();
13353             return;
13354         }
13355         
13356         /*
13357          * Touch Devices
13358          */
13359         
13360         if(Roo.isTouch && this.mobileTouchView){
13361             this.initTouchView();
13362             return;
13363         }
13364         
13365         if(this.tickable){
13366             this.initTickableEvents();
13367             return;
13368         }
13369         
13370         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13371         
13372         if(this.hiddenName){
13373             
13374             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13375             
13376             this.hiddenField.dom.value =
13377                 this.hiddenValue !== undefined ? this.hiddenValue :
13378                 this.value !== undefined ? this.value : '';
13379
13380             // prevent input submission
13381             this.el.dom.removeAttribute('name');
13382             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13383              
13384              
13385         }
13386         //if(Roo.isGecko){
13387         //    this.el.dom.setAttribute('autocomplete', 'off');
13388         //}
13389         
13390         var cls = 'x-combo-list';
13391         
13392         //this.list = new Roo.Layer({
13393         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13394         //});
13395         
13396         var _this = this;
13397         
13398         (function(){
13399             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13400             _this.list.setWidth(lw);
13401         }).defer(100);
13402         
13403         this.list.on('mouseover', this.onViewOver, this);
13404         this.list.on('mousemove', this.onViewMove, this);
13405         this.list.on('scroll', this.onViewScroll, this);
13406         
13407         /*
13408         this.list.swallowEvent('mousewheel');
13409         this.assetHeight = 0;
13410
13411         if(this.title){
13412             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13413             this.assetHeight += this.header.getHeight();
13414         }
13415
13416         this.innerList = this.list.createChild({cls:cls+'-inner'});
13417         this.innerList.on('mouseover', this.onViewOver, this);
13418         this.innerList.on('mousemove', this.onViewMove, this);
13419         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13420         
13421         if(this.allowBlank && !this.pageSize && !this.disableClear){
13422             this.footer = this.list.createChild({cls:cls+'-ft'});
13423             this.pageTb = new Roo.Toolbar(this.footer);
13424            
13425         }
13426         if(this.pageSize){
13427             this.footer = this.list.createChild({cls:cls+'-ft'});
13428             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13429                     {pageSize: this.pageSize});
13430             
13431         }
13432         
13433         if (this.pageTb && this.allowBlank && !this.disableClear) {
13434             var _this = this;
13435             this.pageTb.add(new Roo.Toolbar.Fill(), {
13436                 cls: 'x-btn-icon x-btn-clear',
13437                 text: '&#160;',
13438                 handler: function()
13439                 {
13440                     _this.collapse();
13441                     _this.clearValue();
13442                     _this.onSelect(false, -1);
13443                 }
13444             });
13445         }
13446         if (this.footer) {
13447             this.assetHeight += this.footer.getHeight();
13448         }
13449         */
13450             
13451         if(!this.tpl){
13452             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13453         }
13454
13455         this.view = new Roo.View(this.list, this.tpl, {
13456             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13457         });
13458         //this.view.wrapEl.setDisplayed(false);
13459         this.view.on('click', this.onViewClick, this);
13460         
13461         
13462         this.store.on('beforeload', this.onBeforeLoad, this);
13463         this.store.on('load', this.onLoad, this);
13464         this.store.on('loadexception', this.onLoadException, this);
13465         /*
13466         if(this.resizable){
13467             this.resizer = new Roo.Resizable(this.list,  {
13468                pinned:true, handles:'se'
13469             });
13470             this.resizer.on('resize', function(r, w, h){
13471                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13472                 this.listWidth = w;
13473                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13474                 this.restrictHeight();
13475             }, this);
13476             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13477         }
13478         */
13479         if(!this.editable){
13480             this.editable = true;
13481             this.setEditable(false);
13482         }
13483         
13484         /*
13485         
13486         if (typeof(this.events.add.listeners) != 'undefined') {
13487             
13488             this.addicon = this.wrap.createChild(
13489                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13490        
13491             this.addicon.on('click', function(e) {
13492                 this.fireEvent('add', this);
13493             }, this);
13494         }
13495         if (typeof(this.events.edit.listeners) != 'undefined') {
13496             
13497             this.editicon = this.wrap.createChild(
13498                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13499             if (this.addicon) {
13500                 this.editicon.setStyle('margin-left', '40px');
13501             }
13502             this.editicon.on('click', function(e) {
13503                 
13504                 // we fire even  if inothing is selected..
13505                 this.fireEvent('edit', this, this.lastData );
13506                 
13507             }, this);
13508         }
13509         */
13510         
13511         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13512             "up" : function(e){
13513                 this.inKeyMode = true;
13514                 this.selectPrev();
13515             },
13516
13517             "down" : function(e){
13518                 if(!this.isExpanded()){
13519                     this.onTriggerClick();
13520                 }else{
13521                     this.inKeyMode = true;
13522                     this.selectNext();
13523                 }
13524             },
13525
13526             "enter" : function(e){
13527 //                this.onViewClick();
13528                 //return true;
13529                 this.collapse();
13530                 
13531                 if(this.fireEvent("specialkey", this, e)){
13532                     this.onViewClick(false);
13533                 }
13534                 
13535                 return true;
13536             },
13537
13538             "esc" : function(e){
13539                 this.collapse();
13540             },
13541
13542             "tab" : function(e){
13543                 this.collapse();
13544                 
13545                 if(this.fireEvent("specialkey", this, e)){
13546                     this.onViewClick(false);
13547                 }
13548                 
13549                 return true;
13550             },
13551
13552             scope : this,
13553
13554             doRelay : function(foo, bar, hname){
13555                 if(hname == 'down' || this.scope.isExpanded()){
13556                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13557                 }
13558                 return true;
13559             },
13560
13561             forceKeyDown: true
13562         });
13563         
13564         
13565         this.queryDelay = Math.max(this.queryDelay || 10,
13566                 this.mode == 'local' ? 10 : 250);
13567         
13568         
13569         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13570         
13571         if(this.typeAhead){
13572             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13573         }
13574         if(this.editable !== false){
13575             this.inputEl().on("keyup", this.onKeyUp, this);
13576         }
13577         if(this.forceSelection){
13578             this.inputEl().on('blur', this.doForce, this);
13579         }
13580         
13581         if(this.multiple){
13582             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13583             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13584         }
13585     },
13586     
13587     initTickableEvents: function()
13588     {   
13589         this.createList();
13590         
13591         if(this.hiddenName){
13592             
13593             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13594             
13595             this.hiddenField.dom.value =
13596                 this.hiddenValue !== undefined ? this.hiddenValue :
13597                 this.value !== undefined ? this.value : '';
13598
13599             // prevent input submission
13600             this.el.dom.removeAttribute('name');
13601             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13602              
13603              
13604         }
13605         
13606 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13607         
13608         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13609         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13610         if(this.triggerList){
13611             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13612         }
13613          
13614         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13615         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13616         
13617         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13618         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13619         
13620         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13621         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13622         
13623         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13624         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13625         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13626         
13627         this.okBtn.hide();
13628         this.cancelBtn.hide();
13629         
13630         var _this = this;
13631         
13632         (function(){
13633             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13634             _this.list.setWidth(lw);
13635         }).defer(100);
13636         
13637         this.list.on('mouseover', this.onViewOver, this);
13638         this.list.on('mousemove', this.onViewMove, this);
13639         
13640         this.list.on('scroll', this.onViewScroll, this);
13641         
13642         if(!this.tpl){
13643             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13644                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13645         }
13646
13647         this.view = new Roo.View(this.list, this.tpl, {
13648             singleSelect:true,
13649             tickable:true,
13650             parent:this,
13651             store: this.store,
13652             selectedClass: this.selectedClass
13653         });
13654         
13655         //this.view.wrapEl.setDisplayed(false);
13656         this.view.on('click', this.onViewClick, this);
13657         
13658         
13659         
13660         this.store.on('beforeload', this.onBeforeLoad, this);
13661         this.store.on('load', this.onLoad, this);
13662         this.store.on('loadexception', this.onLoadException, this);
13663         
13664         if(this.editable){
13665             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13666                 "up" : function(e){
13667                     this.inKeyMode = true;
13668                     this.selectPrev();
13669                 },
13670
13671                 "down" : function(e){
13672                     this.inKeyMode = true;
13673                     this.selectNext();
13674                 },
13675
13676                 "enter" : function(e){
13677                     if(this.fireEvent("specialkey", this, e)){
13678                         this.onViewClick(false);
13679                     }
13680                     
13681                     return true;
13682                 },
13683
13684                 "esc" : function(e){
13685                     this.onTickableFooterButtonClick(e, false, false);
13686                 },
13687
13688                 "tab" : function(e){
13689                     this.fireEvent("specialkey", this, e);
13690                     
13691                     this.onTickableFooterButtonClick(e, false, false);
13692                     
13693                     return true;
13694                 },
13695
13696                 scope : this,
13697
13698                 doRelay : function(e, fn, key){
13699                     if(this.scope.isExpanded()){
13700                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13701                     }
13702                     return true;
13703                 },
13704
13705                 forceKeyDown: true
13706             });
13707         }
13708         
13709         this.queryDelay = Math.max(this.queryDelay || 10,
13710                 this.mode == 'local' ? 10 : 250);
13711         
13712         
13713         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13714         
13715         if(this.typeAhead){
13716             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13717         }
13718         
13719         if(this.editable !== false){
13720             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13721         }
13722         
13723         this.indicator = this.indicatorEl();
13724         
13725         if(this.indicator){
13726             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13727             this.indicator.hide();
13728         }
13729         
13730     },
13731
13732     onDestroy : function(){
13733         if(this.view){
13734             this.view.setStore(null);
13735             this.view.el.removeAllListeners();
13736             this.view.el.remove();
13737             this.view.purgeListeners();
13738         }
13739         if(this.list){
13740             this.list.dom.innerHTML  = '';
13741         }
13742         
13743         if(this.store){
13744             this.store.un('beforeload', this.onBeforeLoad, this);
13745             this.store.un('load', this.onLoad, this);
13746             this.store.un('loadexception', this.onLoadException, this);
13747         }
13748         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13749     },
13750
13751     // private
13752     fireKey : function(e){
13753         if(e.isNavKeyPress() && !this.list.isVisible()){
13754             this.fireEvent("specialkey", this, e);
13755         }
13756     },
13757
13758     // private
13759     onResize: function(w, h){
13760 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13761 //        
13762 //        if(typeof w != 'number'){
13763 //            // we do not handle it!?!?
13764 //            return;
13765 //        }
13766 //        var tw = this.trigger.getWidth();
13767 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13768 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13769 //        var x = w - tw;
13770 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13771 //            
13772 //        //this.trigger.setStyle('left', x+'px');
13773 //        
13774 //        if(this.list && this.listWidth === undefined){
13775 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13776 //            this.list.setWidth(lw);
13777 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13778 //        }
13779         
13780     
13781         
13782     },
13783
13784     /**
13785      * Allow or prevent the user from directly editing the field text.  If false is passed,
13786      * the user will only be able to select from the items defined in the dropdown list.  This method
13787      * is the runtime equivalent of setting the 'editable' config option at config time.
13788      * @param {Boolean} value True to allow the user to directly edit the field text
13789      */
13790     setEditable : function(value){
13791         if(value == this.editable){
13792             return;
13793         }
13794         this.editable = value;
13795         if(!value){
13796             this.inputEl().dom.setAttribute('readOnly', true);
13797             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13798             this.inputEl().addClass('x-combo-noedit');
13799         }else{
13800             this.inputEl().dom.setAttribute('readOnly', false);
13801             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13802             this.inputEl().removeClass('x-combo-noedit');
13803         }
13804     },
13805
13806     // private
13807     
13808     onBeforeLoad : function(combo,opts){
13809         if(!this.hasFocus){
13810             return;
13811         }
13812          if (!opts.add) {
13813             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13814          }
13815         this.restrictHeight();
13816         this.selectedIndex = -1;
13817     },
13818
13819     // private
13820     onLoad : function(){
13821         
13822         this.hasQuery = false;
13823         
13824         if(!this.hasFocus){
13825             return;
13826         }
13827         
13828         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13829             this.loading.hide();
13830         }
13831         
13832         if(this.store.getCount() > 0){
13833             
13834             this.expand();
13835             this.restrictHeight();
13836             if(this.lastQuery == this.allQuery){
13837                 if(this.editable && !this.tickable){
13838                     this.inputEl().dom.select();
13839                 }
13840                 
13841                 if(
13842                     !this.selectByValue(this.value, true) &&
13843                     this.autoFocus && 
13844                     (
13845                         !this.store.lastOptions ||
13846                         typeof(this.store.lastOptions.add) == 'undefined' || 
13847                         this.store.lastOptions.add != true
13848                     )
13849                 ){
13850                     this.select(0, true);
13851                 }
13852             }else{
13853                 if(this.autoFocus){
13854                     this.selectNext();
13855                 }
13856                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13857                     this.taTask.delay(this.typeAheadDelay);
13858                 }
13859             }
13860         }else{
13861             this.onEmptyResults();
13862         }
13863         
13864         //this.el.focus();
13865     },
13866     // private
13867     onLoadException : function()
13868     {
13869         this.hasQuery = false;
13870         
13871         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13872             this.loading.hide();
13873         }
13874         
13875         if(this.tickable && this.editable){
13876             return;
13877         }
13878         
13879         this.collapse();
13880         // only causes errors at present
13881         //Roo.log(this.store.reader.jsonData);
13882         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13883             // fixme
13884             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13885         //}
13886         
13887         
13888     },
13889     // private
13890     onTypeAhead : function(){
13891         if(this.store.getCount() > 0){
13892             var r = this.store.getAt(0);
13893             var newValue = r.data[this.displayField];
13894             var len = newValue.length;
13895             var selStart = this.getRawValue().length;
13896             
13897             if(selStart != len){
13898                 this.setRawValue(newValue);
13899                 this.selectText(selStart, newValue.length);
13900             }
13901         }
13902     },
13903
13904     // private
13905     onSelect : function(record, index){
13906         
13907         if(this.fireEvent('beforeselect', this, record, index) !== false){
13908         
13909             this.setFromData(index > -1 ? record.data : false);
13910             
13911             this.collapse();
13912             this.fireEvent('select', this, record, index);
13913         }
13914     },
13915
13916     /**
13917      * Returns the currently selected field value or empty string if no value is set.
13918      * @return {String} value The selected value
13919      */
13920     getValue : function()
13921     {
13922         if(Roo.isIOS && this.useNativeIOS){
13923             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13924         }
13925         
13926         if(this.multiple){
13927             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13928         }
13929         
13930         if(this.valueField){
13931             return typeof this.value != 'undefined' ? this.value : '';
13932         }else{
13933             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13934         }
13935     },
13936     
13937     getRawValue : function()
13938     {
13939         if(Roo.isIOS && this.useNativeIOS){
13940             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13941         }
13942         
13943         var v = this.inputEl().getValue();
13944         
13945         return v;
13946     },
13947
13948     /**
13949      * Clears any text/value currently set in the field
13950      */
13951     clearValue : function(){
13952         
13953         if(this.hiddenField){
13954             this.hiddenField.dom.value = '';
13955         }
13956         this.value = '';
13957         this.setRawValue('');
13958         this.lastSelectionText = '';
13959         this.lastData = false;
13960         
13961         var close = this.closeTriggerEl();
13962         
13963         if(close){
13964             close.hide();
13965         }
13966         
13967         this.validate();
13968         
13969     },
13970
13971     /**
13972      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13973      * will be displayed in the field.  If the value does not match the data value of an existing item,
13974      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13975      * Otherwise the field will be blank (although the value will still be set).
13976      * @param {String} value The value to match
13977      */
13978     setValue : function(v)
13979     {
13980         if(Roo.isIOS && this.useNativeIOS){
13981             this.setIOSValue(v);
13982             return;
13983         }
13984         
13985         if(this.multiple){
13986             this.syncValue();
13987             return;
13988         }
13989         
13990         var text = v;
13991         if(this.valueField){
13992             var r = this.findRecord(this.valueField, v);
13993             if(r){
13994                 text = r.data[this.displayField];
13995             }else if(this.valueNotFoundText !== undefined){
13996                 text = this.valueNotFoundText;
13997             }
13998         }
13999         this.lastSelectionText = text;
14000         if(this.hiddenField){
14001             this.hiddenField.dom.value = v;
14002         }
14003         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14004         this.value = v;
14005         
14006         var close = this.closeTriggerEl();
14007         
14008         if(close){
14009             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14010         }
14011         
14012         this.validate();
14013     },
14014     /**
14015      * @property {Object} the last set data for the element
14016      */
14017     
14018     lastData : false,
14019     /**
14020      * Sets the value of the field based on a object which is related to the record format for the store.
14021      * @param {Object} value the value to set as. or false on reset?
14022      */
14023     setFromData : function(o){
14024         
14025         if(this.multiple){
14026             this.addItem(o);
14027             return;
14028         }
14029             
14030         var dv = ''; // display value
14031         var vv = ''; // value value..
14032         this.lastData = o;
14033         if (this.displayField) {
14034             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14035         } else {
14036             // this is an error condition!!!
14037             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14038         }
14039         
14040         if(this.valueField){
14041             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14042         }
14043         
14044         var close = this.closeTriggerEl();
14045         
14046         if(close){
14047             if(dv.length || vv * 1 > 0){
14048                 close.show() ;
14049                 this.blockFocus=true;
14050             } else {
14051                 close.hide();
14052             }             
14053         }
14054         
14055         if(this.hiddenField){
14056             this.hiddenField.dom.value = vv;
14057             
14058             this.lastSelectionText = dv;
14059             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14060             this.value = vv;
14061             return;
14062         }
14063         // no hidden field.. - we store the value in 'value', but still display
14064         // display field!!!!
14065         this.lastSelectionText = dv;
14066         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14067         this.value = vv;
14068         
14069         
14070         
14071     },
14072     // private
14073     reset : function(){
14074         // overridden so that last data is reset..
14075         
14076         if(this.multiple){
14077             this.clearItem();
14078             return;
14079         }
14080         
14081         this.setValue(this.originalValue);
14082         //this.clearInvalid();
14083         this.lastData = false;
14084         if (this.view) {
14085             this.view.clearSelections();
14086         }
14087         
14088         this.validate();
14089     },
14090     // private
14091     findRecord : function(prop, value){
14092         var record;
14093         if(this.store.getCount() > 0){
14094             this.store.each(function(r){
14095                 if(r.data[prop] == value){
14096                     record = r;
14097                     return false;
14098                 }
14099                 return true;
14100             });
14101         }
14102         return record;
14103     },
14104     
14105     getName: function()
14106     {
14107         // returns hidden if it's set..
14108         if (!this.rendered) {return ''};
14109         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14110         
14111     },
14112     // private
14113     onViewMove : function(e, t){
14114         this.inKeyMode = false;
14115     },
14116
14117     // private
14118     onViewOver : function(e, t){
14119         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14120             return;
14121         }
14122         var item = this.view.findItemFromChild(t);
14123         
14124         if(item){
14125             var index = this.view.indexOf(item);
14126             this.select(index, false);
14127         }
14128     },
14129
14130     // private
14131     onViewClick : function(view, doFocus, el, e)
14132     {
14133         var index = this.view.getSelectedIndexes()[0];
14134         
14135         var r = this.store.getAt(index);
14136         
14137         if(this.tickable){
14138             
14139             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14140                 return;
14141             }
14142             
14143             var rm = false;
14144             var _this = this;
14145             
14146             Roo.each(this.tickItems, function(v,k){
14147                 
14148                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14149                     Roo.log(v);
14150                     _this.tickItems.splice(k, 1);
14151                     
14152                     if(typeof(e) == 'undefined' && view == false){
14153                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14154                     }
14155                     
14156                     rm = true;
14157                     return;
14158                 }
14159             });
14160             
14161             if(rm){
14162                 return;
14163             }
14164             
14165             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14166                 this.tickItems.push(r.data);
14167             }
14168             
14169             if(typeof(e) == 'undefined' && view == false){
14170                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14171             }
14172                     
14173             return;
14174         }
14175         
14176         if(r){
14177             this.onSelect(r, index);
14178         }
14179         if(doFocus !== false && !this.blockFocus){
14180             this.inputEl().focus();
14181         }
14182     },
14183
14184     // private
14185     restrictHeight : function(){
14186         //this.innerList.dom.style.height = '';
14187         //var inner = this.innerList.dom;
14188         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14189         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14190         //this.list.beginUpdate();
14191         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14192         this.list.alignTo(this.inputEl(), this.listAlign);
14193         this.list.alignTo(this.inputEl(), this.listAlign);
14194         //this.list.endUpdate();
14195     },
14196
14197     // private
14198     onEmptyResults : function(){
14199         
14200         if(this.tickable && this.editable){
14201             this.hasFocus = false;
14202             this.restrictHeight();
14203             return;
14204         }
14205         
14206         this.collapse();
14207     },
14208
14209     /**
14210      * Returns true if the dropdown list is expanded, else false.
14211      */
14212     isExpanded : function(){
14213         return this.list.isVisible();
14214     },
14215
14216     /**
14217      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14218      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14219      * @param {String} value The data value of the item to select
14220      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14221      * selected item if it is not currently in view (defaults to true)
14222      * @return {Boolean} True if the value matched an item in the list, else false
14223      */
14224     selectByValue : function(v, scrollIntoView){
14225         if(v !== undefined && v !== null){
14226             var r = this.findRecord(this.valueField || this.displayField, v);
14227             if(r){
14228                 this.select(this.store.indexOf(r), scrollIntoView);
14229                 return true;
14230             }
14231         }
14232         return false;
14233     },
14234
14235     /**
14236      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14237      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14238      * @param {Number} index The zero-based index of the list item to select
14239      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14240      * selected item if it is not currently in view (defaults to true)
14241      */
14242     select : function(index, scrollIntoView){
14243         this.selectedIndex = index;
14244         this.view.select(index);
14245         if(scrollIntoView !== false){
14246             var el = this.view.getNode(index);
14247             /*
14248              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14249              */
14250             if(el){
14251                 this.list.scrollChildIntoView(el, false);
14252             }
14253         }
14254     },
14255
14256     // private
14257     selectNext : function(){
14258         var ct = this.store.getCount();
14259         if(ct > 0){
14260             if(this.selectedIndex == -1){
14261                 this.select(0);
14262             }else if(this.selectedIndex < ct-1){
14263                 this.select(this.selectedIndex+1);
14264             }
14265         }
14266     },
14267
14268     // private
14269     selectPrev : function(){
14270         var ct = this.store.getCount();
14271         if(ct > 0){
14272             if(this.selectedIndex == -1){
14273                 this.select(0);
14274             }else if(this.selectedIndex != 0){
14275                 this.select(this.selectedIndex-1);
14276             }
14277         }
14278     },
14279
14280     // private
14281     onKeyUp : function(e){
14282         if(this.editable !== false && !e.isSpecialKey()){
14283             this.lastKey = e.getKey();
14284             this.dqTask.delay(this.queryDelay);
14285         }
14286     },
14287
14288     // private
14289     validateBlur : function(){
14290         return !this.list || !this.list.isVisible();   
14291     },
14292
14293     // private
14294     initQuery : function(){
14295         
14296         var v = this.getRawValue();
14297         
14298         if(this.tickable && this.editable){
14299             v = this.tickableInputEl().getValue();
14300         }
14301         
14302         this.doQuery(v);
14303     },
14304
14305     // private
14306     doForce : function(){
14307         if(this.inputEl().dom.value.length > 0){
14308             this.inputEl().dom.value =
14309                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14310              
14311         }
14312     },
14313
14314     /**
14315      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14316      * query allowing the query action to be canceled if needed.
14317      * @param {String} query The SQL query to execute
14318      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14319      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14320      * saved in the current store (defaults to false)
14321      */
14322     doQuery : function(q, forceAll){
14323         
14324         if(q === undefined || q === null){
14325             q = '';
14326         }
14327         var qe = {
14328             query: q,
14329             forceAll: forceAll,
14330             combo: this,
14331             cancel:false
14332         };
14333         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14334             return false;
14335         }
14336         q = qe.query;
14337         
14338         forceAll = qe.forceAll;
14339         if(forceAll === true || (q.length >= this.minChars)){
14340             
14341             this.hasQuery = true;
14342             
14343             if(this.lastQuery != q || this.alwaysQuery){
14344                 this.lastQuery = q;
14345                 if(this.mode == 'local'){
14346                     this.selectedIndex = -1;
14347                     if(forceAll){
14348                         this.store.clearFilter();
14349                     }else{
14350                         
14351                         if(this.specialFilter){
14352                             this.fireEvent('specialfilter', this);
14353                             this.onLoad();
14354                             return;
14355                         }
14356                         
14357                         this.store.filter(this.displayField, q);
14358                     }
14359                     
14360                     this.store.fireEvent("datachanged", this.store);
14361                     
14362                     this.onLoad();
14363                     
14364                     
14365                 }else{
14366                     
14367                     this.store.baseParams[this.queryParam] = q;
14368                     
14369                     var options = {params : this.getParams(q)};
14370                     
14371                     if(this.loadNext){
14372                         options.add = true;
14373                         options.params.start = this.page * this.pageSize;
14374                     }
14375                     
14376                     this.store.load(options);
14377                     
14378                     /*
14379                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14380                      *  we should expand the list on onLoad
14381                      *  so command out it
14382                      */
14383 //                    this.expand();
14384                 }
14385             }else{
14386                 this.selectedIndex = -1;
14387                 this.onLoad();   
14388             }
14389         }
14390         
14391         this.loadNext = false;
14392     },
14393     
14394     // private
14395     getParams : function(q){
14396         var p = {};
14397         //p[this.queryParam] = q;
14398         
14399         if(this.pageSize){
14400             p.start = 0;
14401             p.limit = this.pageSize;
14402         }
14403         return p;
14404     },
14405
14406     /**
14407      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14408      */
14409     collapse : function(){
14410         if(!this.isExpanded()){
14411             return;
14412         }
14413         
14414         this.list.hide();
14415         
14416         this.hasFocus = false;
14417         
14418         if(this.tickable){
14419             this.okBtn.hide();
14420             this.cancelBtn.hide();
14421             this.trigger.show();
14422             
14423             if(this.editable){
14424                 this.tickableInputEl().dom.value = '';
14425                 this.tickableInputEl().blur();
14426             }
14427             
14428         }
14429         
14430         Roo.get(document).un('mousedown', this.collapseIf, this);
14431         Roo.get(document).un('mousewheel', this.collapseIf, this);
14432         if (!this.editable) {
14433             Roo.get(document).un('keydown', this.listKeyPress, this);
14434         }
14435         this.fireEvent('collapse', this);
14436         
14437         this.validate();
14438     },
14439
14440     // private
14441     collapseIf : function(e){
14442         var in_combo  = e.within(this.el);
14443         var in_list =  e.within(this.list);
14444         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14445         
14446         if (in_combo || in_list || is_list) {
14447             //e.stopPropagation();
14448             return;
14449         }
14450         
14451         if(this.tickable){
14452             this.onTickableFooterButtonClick(e, false, false);
14453         }
14454
14455         this.collapse();
14456         
14457     },
14458
14459     /**
14460      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14461      */
14462     expand : function(){
14463        
14464         if(this.isExpanded() || !this.hasFocus){
14465             return;
14466         }
14467         
14468         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14469         this.list.setWidth(lw);
14470         
14471         Roo.log('expand');
14472         
14473         this.list.show();
14474         
14475         this.restrictHeight();
14476         
14477         if(this.tickable){
14478             
14479             this.tickItems = Roo.apply([], this.item);
14480             
14481             this.okBtn.show();
14482             this.cancelBtn.show();
14483             this.trigger.hide();
14484             
14485             if(this.editable){
14486                 this.tickableInputEl().focus();
14487             }
14488             
14489         }
14490         
14491         Roo.get(document).on('mousedown', this.collapseIf, this);
14492         Roo.get(document).on('mousewheel', this.collapseIf, this);
14493         if (!this.editable) {
14494             Roo.get(document).on('keydown', this.listKeyPress, this);
14495         }
14496         
14497         this.fireEvent('expand', this);
14498     },
14499
14500     // private
14501     // Implements the default empty TriggerField.onTriggerClick function
14502     onTriggerClick : function(e)
14503     {
14504         Roo.log('trigger click');
14505         
14506         if(this.disabled || !this.triggerList){
14507             return;
14508         }
14509         
14510         this.page = 0;
14511         this.loadNext = false;
14512         
14513         if(this.isExpanded()){
14514             this.collapse();
14515             if (!this.blockFocus) {
14516                 this.inputEl().focus();
14517             }
14518             
14519         }else {
14520             this.hasFocus = true;
14521             if(this.triggerAction == 'all') {
14522                 this.doQuery(this.allQuery, true);
14523             } else {
14524                 this.doQuery(this.getRawValue());
14525             }
14526             if (!this.blockFocus) {
14527                 this.inputEl().focus();
14528             }
14529         }
14530     },
14531     
14532     onTickableTriggerClick : function(e)
14533     {
14534         if(this.disabled){
14535             return;
14536         }
14537         
14538         this.page = 0;
14539         this.loadNext = false;
14540         this.hasFocus = true;
14541         
14542         if(this.triggerAction == 'all') {
14543             this.doQuery(this.allQuery, true);
14544         } else {
14545             this.doQuery(this.getRawValue());
14546         }
14547     },
14548     
14549     onSearchFieldClick : function(e)
14550     {
14551         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14552             this.onTickableFooterButtonClick(e, false, false);
14553             return;
14554         }
14555         
14556         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14557             return;
14558         }
14559         
14560         this.page = 0;
14561         this.loadNext = false;
14562         this.hasFocus = true;
14563         
14564         if(this.triggerAction == 'all') {
14565             this.doQuery(this.allQuery, true);
14566         } else {
14567             this.doQuery(this.getRawValue());
14568         }
14569     },
14570     
14571     listKeyPress : function(e)
14572     {
14573         //Roo.log('listkeypress');
14574         // scroll to first matching element based on key pres..
14575         if (e.isSpecialKey()) {
14576             return false;
14577         }
14578         var k = String.fromCharCode(e.getKey()).toUpperCase();
14579         //Roo.log(k);
14580         var match  = false;
14581         var csel = this.view.getSelectedNodes();
14582         var cselitem = false;
14583         if (csel.length) {
14584             var ix = this.view.indexOf(csel[0]);
14585             cselitem  = this.store.getAt(ix);
14586             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14587                 cselitem = false;
14588             }
14589             
14590         }
14591         
14592         this.store.each(function(v) { 
14593             if (cselitem) {
14594                 // start at existing selection.
14595                 if (cselitem.id == v.id) {
14596                     cselitem = false;
14597                 }
14598                 return true;
14599             }
14600                 
14601             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14602                 match = this.store.indexOf(v);
14603                 return false;
14604             }
14605             return true;
14606         }, this);
14607         
14608         if (match === false) {
14609             return true; // no more action?
14610         }
14611         // scroll to?
14612         this.view.select(match);
14613         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14614         sn.scrollIntoView(sn.dom.parentNode, false);
14615     },
14616     
14617     onViewScroll : function(e, t){
14618         
14619         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){
14620             return;
14621         }
14622         
14623         this.hasQuery = true;
14624         
14625         this.loading = this.list.select('.loading', true).first();
14626         
14627         if(this.loading === null){
14628             this.list.createChild({
14629                 tag: 'div',
14630                 cls: 'loading roo-select2-more-results roo-select2-active',
14631                 html: 'Loading more results...'
14632             });
14633             
14634             this.loading = this.list.select('.loading', true).first();
14635             
14636             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14637             
14638             this.loading.hide();
14639         }
14640         
14641         this.loading.show();
14642         
14643         var _combo = this;
14644         
14645         this.page++;
14646         this.loadNext = true;
14647         
14648         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14649         
14650         return;
14651     },
14652     
14653     addItem : function(o)
14654     {   
14655         var dv = ''; // display value
14656         
14657         if (this.displayField) {
14658             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14659         } else {
14660             // this is an error condition!!!
14661             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14662         }
14663         
14664         if(!dv.length){
14665             return;
14666         }
14667         
14668         var choice = this.choices.createChild({
14669             tag: 'li',
14670             cls: 'roo-select2-search-choice',
14671             cn: [
14672                 {
14673                     tag: 'div',
14674                     html: dv
14675                 },
14676                 {
14677                     tag: 'a',
14678                     href: '#',
14679                     cls: 'roo-select2-search-choice-close fa fa-times',
14680                     tabindex: '-1'
14681                 }
14682             ]
14683             
14684         }, this.searchField);
14685         
14686         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14687         
14688         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14689         
14690         this.item.push(o);
14691         
14692         this.lastData = o;
14693         
14694         this.syncValue();
14695         
14696         this.inputEl().dom.value = '';
14697         
14698         this.validate();
14699     },
14700     
14701     onRemoveItem : function(e, _self, o)
14702     {
14703         e.preventDefault();
14704         
14705         this.lastItem = Roo.apply([], this.item);
14706         
14707         var index = this.item.indexOf(o.data) * 1;
14708         
14709         if( index < 0){
14710             Roo.log('not this item?!');
14711             return;
14712         }
14713         
14714         this.item.splice(index, 1);
14715         o.item.remove();
14716         
14717         this.syncValue();
14718         
14719         this.fireEvent('remove', this, e);
14720         
14721         this.validate();
14722         
14723     },
14724     
14725     syncValue : function()
14726     {
14727         if(!this.item.length){
14728             this.clearValue();
14729             return;
14730         }
14731             
14732         var value = [];
14733         var _this = this;
14734         Roo.each(this.item, function(i){
14735             if(_this.valueField){
14736                 value.push(i[_this.valueField]);
14737                 return;
14738             }
14739
14740             value.push(i);
14741         });
14742
14743         this.value = value.join(',');
14744
14745         if(this.hiddenField){
14746             this.hiddenField.dom.value = this.value;
14747         }
14748         
14749         this.store.fireEvent("datachanged", this.store);
14750         
14751         this.validate();
14752     },
14753     
14754     clearItem : function()
14755     {
14756         if(!this.multiple){
14757             return;
14758         }
14759         
14760         this.item = [];
14761         
14762         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14763            c.remove();
14764         });
14765         
14766         this.syncValue();
14767         
14768         this.validate();
14769         
14770         if(this.tickable && !Roo.isTouch){
14771             this.view.refresh();
14772         }
14773     },
14774     
14775     inputEl: function ()
14776     {
14777         if(Roo.isIOS && this.useNativeIOS){
14778             return this.el.select('select.roo-ios-select', true).first();
14779         }
14780         
14781         if(Roo.isTouch && this.mobileTouchView){
14782             return this.el.select('input.form-control',true).first();
14783         }
14784         
14785         if(this.tickable){
14786             return this.searchField;
14787         }
14788         
14789         return this.el.select('input.form-control',true).first();
14790     },
14791     
14792     onTickableFooterButtonClick : function(e, btn, el)
14793     {
14794         e.preventDefault();
14795         
14796         this.lastItem = Roo.apply([], this.item);
14797         
14798         if(btn && btn.name == 'cancel'){
14799             this.tickItems = Roo.apply([], this.item);
14800             this.collapse();
14801             return;
14802         }
14803         
14804         this.clearItem();
14805         
14806         var _this = this;
14807         
14808         Roo.each(this.tickItems, function(o){
14809             _this.addItem(o);
14810         });
14811         
14812         this.collapse();
14813         
14814     },
14815     
14816     validate : function()
14817     {
14818         if(this.getVisibilityEl().hasClass('hidden')){
14819             return true;
14820         }
14821         
14822         var v = this.getRawValue();
14823         
14824         if(this.multiple){
14825             v = this.getValue();
14826         }
14827         
14828         if(this.disabled || this.allowBlank || v.length){
14829             this.markValid();
14830             return true;
14831         }
14832         
14833         this.markInvalid();
14834         return false;
14835     },
14836     
14837     tickableInputEl : function()
14838     {
14839         if(!this.tickable || !this.editable){
14840             return this.inputEl();
14841         }
14842         
14843         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14844     },
14845     
14846     
14847     getAutoCreateTouchView : function()
14848     {
14849         var id = Roo.id();
14850         
14851         var cfg = {
14852             cls: 'form-group' //input-group
14853         };
14854         
14855         var input =  {
14856             tag: 'input',
14857             id : id,
14858             type : this.inputType,
14859             cls : 'form-control x-combo-noedit',
14860             autocomplete: 'new-password',
14861             placeholder : this.placeholder || '',
14862             readonly : true
14863         };
14864         
14865         if (this.name) {
14866             input.name = this.name;
14867         }
14868         
14869         if (this.size) {
14870             input.cls += ' input-' + this.size;
14871         }
14872         
14873         if (this.disabled) {
14874             input.disabled = true;
14875         }
14876         
14877         var inputblock = {
14878             cls : '',
14879             cn : [
14880                 input
14881             ]
14882         };
14883         
14884         if(this.before){
14885             inputblock.cls += ' input-group';
14886             
14887             inputblock.cn.unshift({
14888                 tag :'span',
14889                 cls : 'input-group-addon',
14890                 html : this.before
14891             });
14892         }
14893         
14894         if(this.removable && !this.multiple){
14895             inputblock.cls += ' roo-removable';
14896             
14897             inputblock.cn.push({
14898                 tag: 'button',
14899                 html : 'x',
14900                 cls : 'roo-combo-removable-btn close'
14901             });
14902         }
14903
14904         if(this.hasFeedback && !this.allowBlank){
14905             
14906             inputblock.cls += ' has-feedback';
14907             
14908             inputblock.cn.push({
14909                 tag: 'span',
14910                 cls: 'glyphicon form-control-feedback'
14911             });
14912             
14913         }
14914         
14915         if (this.after) {
14916             
14917             inputblock.cls += (this.before) ? '' : ' input-group';
14918             
14919             inputblock.cn.push({
14920                 tag :'span',
14921                 cls : 'input-group-addon',
14922                 html : this.after
14923             });
14924         }
14925
14926         var box = {
14927             tag: 'div',
14928             cn: [
14929                 {
14930                     tag: 'input',
14931                     type : 'hidden',
14932                     cls: 'form-hidden-field'
14933                 },
14934                 inputblock
14935             ]
14936             
14937         };
14938         
14939         if(this.multiple){
14940             box = {
14941                 tag: 'div',
14942                 cn: [
14943                     {
14944                         tag: 'input',
14945                         type : 'hidden',
14946                         cls: 'form-hidden-field'
14947                     },
14948                     {
14949                         tag: 'ul',
14950                         cls: 'roo-select2-choices',
14951                         cn:[
14952                             {
14953                                 tag: 'li',
14954                                 cls: 'roo-select2-search-field',
14955                                 cn: [
14956
14957                                     inputblock
14958                                 ]
14959                             }
14960                         ]
14961                     }
14962                 ]
14963             }
14964         };
14965         
14966         var combobox = {
14967             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14968             cn: [
14969                 box
14970             ]
14971         };
14972         
14973         if(!this.multiple && this.showToggleBtn){
14974             
14975             var caret = {
14976                         tag: 'span',
14977                         cls: 'caret'
14978             };
14979             
14980             if (this.caret != false) {
14981                 caret = {
14982                      tag: 'i',
14983                      cls: 'fa fa-' + this.caret
14984                 };
14985                 
14986             }
14987             
14988             combobox.cn.push({
14989                 tag :'span',
14990                 cls : 'input-group-addon btn dropdown-toggle',
14991                 cn : [
14992                     caret,
14993                     {
14994                         tag: 'span',
14995                         cls: 'combobox-clear',
14996                         cn  : [
14997                             {
14998                                 tag : 'i',
14999                                 cls: 'icon-remove'
15000                             }
15001                         ]
15002                     }
15003                 ]
15004
15005             })
15006         }
15007         
15008         if(this.multiple){
15009             combobox.cls += ' roo-select2-container-multi';
15010         }
15011         
15012         var align = this.labelAlign || this.parentLabelAlign();
15013         
15014         if (align ==='left' && this.fieldLabel.length) {
15015
15016             cfg.cn = [
15017                 {
15018                    tag : 'i',
15019                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15020                    tooltip : 'This field is required'
15021                 },
15022                 {
15023                     tag: 'label',
15024                     cls : 'control-label',
15025                     html : this.fieldLabel
15026
15027                 },
15028                 {
15029                     cls : '', 
15030                     cn: [
15031                         combobox
15032                     ]
15033                 }
15034             ];
15035             
15036             var labelCfg = cfg.cn[1];
15037             var contentCfg = cfg.cn[2];
15038             
15039
15040             if(this.indicatorpos == 'right'){
15041                 cfg.cn = [
15042                     {
15043                         tag: 'label',
15044                         'for' :  id,
15045                         cls : 'control-label',
15046                         cn : [
15047                             {
15048                                 tag : 'span',
15049                                 html : this.fieldLabel
15050                             },
15051                             {
15052                                 tag : 'i',
15053                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15054                                 tooltip : 'This field is required'
15055                             }
15056                         ]
15057                     },
15058                     {
15059                         cls : "",
15060                         cn: [
15061                             combobox
15062                         ]
15063                     }
15064
15065                 ];
15066                 
15067                 labelCfg = cfg.cn[0];
15068                 contentCfg = cfg.cn[1];
15069             }
15070             
15071            
15072             
15073             if(this.labelWidth > 12){
15074                 labelCfg.style = "width: " + this.labelWidth + 'px';
15075             }
15076             
15077             if(this.labelWidth < 13 && this.labelmd == 0){
15078                 this.labelmd = this.labelWidth;
15079             }
15080             
15081             if(this.labellg > 0){
15082                 labelCfg.cls += ' col-lg-' + this.labellg;
15083                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15084             }
15085             
15086             if(this.labelmd > 0){
15087                 labelCfg.cls += ' col-md-' + this.labelmd;
15088                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15089             }
15090             
15091             if(this.labelsm > 0){
15092                 labelCfg.cls += ' col-sm-' + this.labelsm;
15093                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15094             }
15095             
15096             if(this.labelxs > 0){
15097                 labelCfg.cls += ' col-xs-' + this.labelxs;
15098                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15099             }
15100                 
15101                 
15102         } else if ( this.fieldLabel.length) {
15103             cfg.cn = [
15104                 {
15105                    tag : 'i',
15106                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15107                    tooltip : 'This field is required'
15108                 },
15109                 {
15110                     tag: 'label',
15111                     cls : 'control-label',
15112                     html : this.fieldLabel
15113
15114                 },
15115                 {
15116                     cls : '', 
15117                     cn: [
15118                         combobox
15119                     ]
15120                 }
15121             ];
15122             
15123             if(this.indicatorpos == 'right'){
15124                 cfg.cn = [
15125                     {
15126                         tag: 'label',
15127                         cls : 'control-label',
15128                         html : this.fieldLabel,
15129                         cn : [
15130                             {
15131                                tag : 'i',
15132                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15133                                tooltip : 'This field is required'
15134                             }
15135                         ]
15136                     },
15137                     {
15138                         cls : '', 
15139                         cn: [
15140                             combobox
15141                         ]
15142                     }
15143                 ];
15144             }
15145         } else {
15146             cfg.cn = combobox;    
15147         }
15148         
15149         
15150         var settings = this;
15151         
15152         ['xs','sm','md','lg'].map(function(size){
15153             if (settings[size]) {
15154                 cfg.cls += ' col-' + size + '-' + settings[size];
15155             }
15156         });
15157         
15158         return cfg;
15159     },
15160     
15161     initTouchView : function()
15162     {
15163         this.renderTouchView();
15164         
15165         this.touchViewEl.on('scroll', function(){
15166             this.el.dom.scrollTop = 0;
15167         }, this);
15168         
15169         this.originalValue = this.getValue();
15170         
15171         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15172         
15173         this.inputEl().on("click", this.showTouchView, this);
15174         if (this.triggerEl) {
15175             this.triggerEl.on("click", this.showTouchView, this);
15176         }
15177         
15178         
15179         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15180         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15181         
15182         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15183         
15184         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15185         this.store.on('load', this.onTouchViewLoad, this);
15186         this.store.on('loadexception', this.onTouchViewLoadException, this);
15187         
15188         if(this.hiddenName){
15189             
15190             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15191             
15192             this.hiddenField.dom.value =
15193                 this.hiddenValue !== undefined ? this.hiddenValue :
15194                 this.value !== undefined ? this.value : '';
15195         
15196             this.el.dom.removeAttribute('name');
15197             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15198         }
15199         
15200         if(this.multiple){
15201             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15202             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15203         }
15204         
15205         if(this.removable && !this.multiple){
15206             var close = this.closeTriggerEl();
15207             if(close){
15208                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15209                 close.on('click', this.removeBtnClick, this, close);
15210             }
15211         }
15212         /*
15213          * fix the bug in Safari iOS8
15214          */
15215         this.inputEl().on("focus", function(e){
15216             document.activeElement.blur();
15217         }, this);
15218         
15219         return;
15220         
15221         
15222     },
15223     
15224     renderTouchView : function()
15225     {
15226         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15227         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15228         
15229         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15230         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15231         
15232         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15233         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15234         this.touchViewBodyEl.setStyle('overflow', 'auto');
15235         
15236         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15237         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15238         
15239         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15240         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15241         
15242     },
15243     
15244     showTouchView : function()
15245     {
15246         if(this.disabled){
15247             return;
15248         }
15249         
15250         this.touchViewHeaderEl.hide();
15251
15252         if(this.modalTitle.length){
15253             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15254             this.touchViewHeaderEl.show();
15255         }
15256
15257         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15258         this.touchViewEl.show();
15259
15260         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15261         
15262         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15263         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15264
15265         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15266
15267         if(this.modalTitle.length){
15268             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15269         }
15270         
15271         this.touchViewBodyEl.setHeight(bodyHeight);
15272
15273         if(this.animate){
15274             var _this = this;
15275             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15276         }else{
15277             this.touchViewEl.addClass('in');
15278         }
15279
15280         this.doTouchViewQuery();
15281         
15282     },
15283     
15284     hideTouchView : function()
15285     {
15286         this.touchViewEl.removeClass('in');
15287
15288         if(this.animate){
15289             var _this = this;
15290             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15291         }else{
15292             this.touchViewEl.setStyle('display', 'none');
15293         }
15294         
15295     },
15296     
15297     setTouchViewValue : function()
15298     {
15299         if(this.multiple){
15300             this.clearItem();
15301         
15302             var _this = this;
15303
15304             Roo.each(this.tickItems, function(o){
15305                 this.addItem(o);
15306             }, this);
15307         }
15308         
15309         this.hideTouchView();
15310     },
15311     
15312     doTouchViewQuery : function()
15313     {
15314         var qe = {
15315             query: '',
15316             forceAll: true,
15317             combo: this,
15318             cancel:false
15319         };
15320         
15321         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15322             return false;
15323         }
15324         
15325         if(!this.alwaysQuery || this.mode == 'local'){
15326             this.onTouchViewLoad();
15327             return;
15328         }
15329         
15330         this.store.load();
15331     },
15332     
15333     onTouchViewBeforeLoad : function(combo,opts)
15334     {
15335         return;
15336     },
15337
15338     // private
15339     onTouchViewLoad : function()
15340     {
15341         if(this.store.getCount() < 1){
15342             this.onTouchViewEmptyResults();
15343             return;
15344         }
15345         
15346         this.clearTouchView();
15347         
15348         var rawValue = this.getRawValue();
15349         
15350         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15351         
15352         this.tickItems = [];
15353         
15354         this.store.data.each(function(d, rowIndex){
15355             var row = this.touchViewListGroup.createChild(template);
15356             
15357             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15358                 row.addClass(d.data.cls);
15359             }
15360             
15361             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15362                 var cfg = {
15363                     data : d.data,
15364                     html : d.data[this.displayField]
15365                 };
15366                 
15367                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15368                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15369                 }
15370             }
15371             row.removeClass('selected');
15372             if(!this.multiple && this.valueField &&
15373                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15374             {
15375                 // radio buttons..
15376                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15377                 row.addClass('selected');
15378             }
15379             
15380             if(this.multiple && this.valueField &&
15381                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15382             {
15383                 
15384                 // checkboxes...
15385                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15386                 this.tickItems.push(d.data);
15387             }
15388             
15389             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15390             
15391         }, this);
15392         
15393         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15394         
15395         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15396
15397         if(this.modalTitle.length){
15398             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15399         }
15400
15401         var listHeight = this.touchViewListGroup.getHeight();
15402         
15403         var _this = this;
15404         
15405         if(firstChecked && listHeight > bodyHeight){
15406             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15407         }
15408         
15409     },
15410     
15411     onTouchViewLoadException : function()
15412     {
15413         this.hideTouchView();
15414     },
15415     
15416     onTouchViewEmptyResults : function()
15417     {
15418         this.clearTouchView();
15419         
15420         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15421         
15422         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15423         
15424     },
15425     
15426     clearTouchView : function()
15427     {
15428         this.touchViewListGroup.dom.innerHTML = '';
15429     },
15430     
15431     onTouchViewClick : function(e, el, o)
15432     {
15433         e.preventDefault();
15434         
15435         var row = o.row;
15436         var rowIndex = o.rowIndex;
15437         
15438         var r = this.store.getAt(rowIndex);
15439         
15440         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15441             
15442             if(!this.multiple){
15443                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15444                     c.dom.removeAttribute('checked');
15445                 }, this);
15446
15447                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15448
15449                 this.setFromData(r.data);
15450
15451                 var close = this.closeTriggerEl();
15452
15453                 if(close){
15454                     close.show();
15455                 }
15456
15457                 this.hideTouchView();
15458
15459                 this.fireEvent('select', this, r, rowIndex);
15460
15461                 return;
15462             }
15463
15464             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15465                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15466                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15467                 return;
15468             }
15469
15470             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15471             this.addItem(r.data);
15472             this.tickItems.push(r.data);
15473         }
15474     },
15475     
15476     getAutoCreateNativeIOS : function()
15477     {
15478         var cfg = {
15479             cls: 'form-group' //input-group,
15480         };
15481         
15482         var combobox =  {
15483             tag: 'select',
15484             cls : 'roo-ios-select'
15485         };
15486         
15487         if (this.name) {
15488             combobox.name = this.name;
15489         }
15490         
15491         if (this.disabled) {
15492             combobox.disabled = true;
15493         }
15494         
15495         var settings = this;
15496         
15497         ['xs','sm','md','lg'].map(function(size){
15498             if (settings[size]) {
15499                 cfg.cls += ' col-' + size + '-' + settings[size];
15500             }
15501         });
15502         
15503         cfg.cn = combobox;
15504         
15505         return cfg;
15506         
15507     },
15508     
15509     initIOSView : function()
15510     {
15511         this.store.on('load', this.onIOSViewLoad, this);
15512         
15513         return;
15514     },
15515     
15516     onIOSViewLoad : function()
15517     {
15518         if(this.store.getCount() < 1){
15519             return;
15520         }
15521         
15522         this.clearIOSView();
15523         
15524         if(this.allowBlank) {
15525             
15526             var default_text = '-- SELECT --';
15527             
15528             if(this.placeholder.length){
15529                 default_text = this.placeholder;
15530             }
15531             
15532             if(this.emptyTitle.length){
15533                 default_text += ' - ' + this.emptyTitle + ' -';
15534             }
15535             
15536             var opt = this.inputEl().createChild({
15537                 tag: 'option',
15538                 value : 0,
15539                 html : default_text
15540             });
15541             
15542             var o = {};
15543             o[this.valueField] = 0;
15544             o[this.displayField] = default_text;
15545             
15546             this.ios_options.push({
15547                 data : o,
15548                 el : opt
15549             });
15550             
15551         }
15552         
15553         this.store.data.each(function(d, rowIndex){
15554             
15555             var html = '';
15556             
15557             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15558                 html = d.data[this.displayField];
15559             }
15560             
15561             var value = '';
15562             
15563             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15564                 value = d.data[this.valueField];
15565             }
15566             
15567             var option = {
15568                 tag: 'option',
15569                 value : value,
15570                 html : html
15571             };
15572             
15573             if(this.value == d.data[this.valueField]){
15574                 option['selected'] = true;
15575             }
15576             
15577             var opt = this.inputEl().createChild(option);
15578             
15579             this.ios_options.push({
15580                 data : d.data,
15581                 el : opt
15582             });
15583             
15584         }, this);
15585         
15586         this.inputEl().on('change', function(){
15587            this.fireEvent('select', this);
15588         }, this);
15589         
15590     },
15591     
15592     clearIOSView: function()
15593     {
15594         this.inputEl().dom.innerHTML = '';
15595         
15596         this.ios_options = [];
15597     },
15598     
15599     setIOSValue: function(v)
15600     {
15601         this.value = v;
15602         
15603         if(!this.ios_options){
15604             return;
15605         }
15606         
15607         Roo.each(this.ios_options, function(opts){
15608            
15609            opts.el.dom.removeAttribute('selected');
15610            
15611            if(opts.data[this.valueField] != v){
15612                return;
15613            }
15614            
15615            opts.el.dom.setAttribute('selected', true);
15616            
15617         }, this);
15618     }
15619
15620     /** 
15621     * @cfg {Boolean} grow 
15622     * @hide 
15623     */
15624     /** 
15625     * @cfg {Number} growMin 
15626     * @hide 
15627     */
15628     /** 
15629     * @cfg {Number} growMax 
15630     * @hide 
15631     */
15632     /**
15633      * @hide
15634      * @method autoSize
15635      */
15636 });
15637
15638 Roo.apply(Roo.bootstrap.ComboBox,  {
15639     
15640     header : {
15641         tag: 'div',
15642         cls: 'modal-header',
15643         cn: [
15644             {
15645                 tag: 'h4',
15646                 cls: 'modal-title'
15647             }
15648         ]
15649     },
15650     
15651     body : {
15652         tag: 'div',
15653         cls: 'modal-body',
15654         cn: [
15655             {
15656                 tag: 'ul',
15657                 cls: 'list-group'
15658             }
15659         ]
15660     },
15661     
15662     listItemRadio : {
15663         tag: 'li',
15664         cls: 'list-group-item',
15665         cn: [
15666             {
15667                 tag: 'span',
15668                 cls: 'roo-combobox-list-group-item-value'
15669             },
15670             {
15671                 tag: 'div',
15672                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15673                 cn: [
15674                     {
15675                         tag: 'input',
15676                         type: 'radio'
15677                     },
15678                     {
15679                         tag: 'label'
15680                     }
15681                 ]
15682             }
15683         ]
15684     },
15685     
15686     listItemCheckbox : {
15687         tag: 'li',
15688         cls: 'list-group-item',
15689         cn: [
15690             {
15691                 tag: 'span',
15692                 cls: 'roo-combobox-list-group-item-value'
15693             },
15694             {
15695                 tag: 'div',
15696                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15697                 cn: [
15698                     {
15699                         tag: 'input',
15700                         type: 'checkbox'
15701                     },
15702                     {
15703                         tag: 'label'
15704                     }
15705                 ]
15706             }
15707         ]
15708     },
15709     
15710     emptyResult : {
15711         tag: 'div',
15712         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15713     },
15714     
15715     footer : {
15716         tag: 'div',
15717         cls: 'modal-footer',
15718         cn: [
15719             {
15720                 tag: 'div',
15721                 cls: 'row',
15722                 cn: [
15723                     {
15724                         tag: 'div',
15725                         cls: 'col-xs-6 text-left',
15726                         cn: {
15727                             tag: 'button',
15728                             cls: 'btn btn-danger roo-touch-view-cancel',
15729                             html: 'Cancel'
15730                         }
15731                     },
15732                     {
15733                         tag: 'div',
15734                         cls: 'col-xs-6 text-right',
15735                         cn: {
15736                             tag: 'button',
15737                             cls: 'btn btn-success roo-touch-view-ok',
15738                             html: 'OK'
15739                         }
15740                     }
15741                 ]
15742             }
15743         ]
15744         
15745     }
15746 });
15747
15748 Roo.apply(Roo.bootstrap.ComboBox,  {
15749     
15750     touchViewTemplate : {
15751         tag: 'div',
15752         cls: 'modal fade roo-combobox-touch-view',
15753         cn: [
15754             {
15755                 tag: 'div',
15756                 cls: 'modal-dialog',
15757                 style : 'position:fixed', // we have to fix position....
15758                 cn: [
15759                     {
15760                         tag: 'div',
15761                         cls: 'modal-content',
15762                         cn: [
15763                             Roo.bootstrap.ComboBox.header,
15764                             Roo.bootstrap.ComboBox.body,
15765                             Roo.bootstrap.ComboBox.footer
15766                         ]
15767                     }
15768                 ]
15769             }
15770         ]
15771     }
15772 });/*
15773  * Based on:
15774  * Ext JS Library 1.1.1
15775  * Copyright(c) 2006-2007, Ext JS, LLC.
15776  *
15777  * Originally Released Under LGPL - original licence link has changed is not relivant.
15778  *
15779  * Fork - LGPL
15780  * <script type="text/javascript">
15781  */
15782
15783 /**
15784  * @class Roo.View
15785  * @extends Roo.util.Observable
15786  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15787  * This class also supports single and multi selection modes. <br>
15788  * Create a data model bound view:
15789  <pre><code>
15790  var store = new Roo.data.Store(...);
15791
15792  var view = new Roo.View({
15793     el : "my-element",
15794     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15795  
15796     singleSelect: true,
15797     selectedClass: "ydataview-selected",
15798     store: store
15799  });
15800
15801  // listen for node click?
15802  view.on("click", function(vw, index, node, e){
15803  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15804  });
15805
15806  // load XML data
15807  dataModel.load("foobar.xml");
15808  </code></pre>
15809  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15810  * <br><br>
15811  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15812  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15813  * 
15814  * Note: old style constructor is still suported (container, template, config)
15815  * 
15816  * @constructor
15817  * Create a new View
15818  * @param {Object} config The config object
15819  * 
15820  */
15821 Roo.View = function(config, depreciated_tpl, depreciated_config){
15822     
15823     this.parent = false;
15824     
15825     if (typeof(depreciated_tpl) == 'undefined') {
15826         // new way.. - universal constructor.
15827         Roo.apply(this, config);
15828         this.el  = Roo.get(this.el);
15829     } else {
15830         // old format..
15831         this.el  = Roo.get(config);
15832         this.tpl = depreciated_tpl;
15833         Roo.apply(this, depreciated_config);
15834     }
15835     this.wrapEl  = this.el.wrap().wrap();
15836     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15837     
15838     
15839     if(typeof(this.tpl) == "string"){
15840         this.tpl = new Roo.Template(this.tpl);
15841     } else {
15842         // support xtype ctors..
15843         this.tpl = new Roo.factory(this.tpl, Roo);
15844     }
15845     
15846     
15847     this.tpl.compile();
15848     
15849     /** @private */
15850     this.addEvents({
15851         /**
15852          * @event beforeclick
15853          * Fires before a click is processed. Returns false to cancel the default action.
15854          * @param {Roo.View} this
15855          * @param {Number} index The index of the target node
15856          * @param {HTMLElement} node The target node
15857          * @param {Roo.EventObject} e The raw event object
15858          */
15859             "beforeclick" : true,
15860         /**
15861          * @event click
15862          * Fires when a template node is clicked.
15863          * @param {Roo.View} this
15864          * @param {Number} index The index of the target node
15865          * @param {HTMLElement} node The target node
15866          * @param {Roo.EventObject} e The raw event object
15867          */
15868             "click" : true,
15869         /**
15870          * @event dblclick
15871          * Fires when a template node is double clicked.
15872          * @param {Roo.View} this
15873          * @param {Number} index The index of the target node
15874          * @param {HTMLElement} node The target node
15875          * @param {Roo.EventObject} e The raw event object
15876          */
15877             "dblclick" : true,
15878         /**
15879          * @event contextmenu
15880          * Fires when a template node is right clicked.
15881          * @param {Roo.View} this
15882          * @param {Number} index The index of the target node
15883          * @param {HTMLElement} node The target node
15884          * @param {Roo.EventObject} e The raw event object
15885          */
15886             "contextmenu" : true,
15887         /**
15888          * @event selectionchange
15889          * Fires when the selected nodes change.
15890          * @param {Roo.View} this
15891          * @param {Array} selections Array of the selected nodes
15892          */
15893             "selectionchange" : true,
15894     
15895         /**
15896          * @event beforeselect
15897          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15898          * @param {Roo.View} this
15899          * @param {HTMLElement} node The node to be selected
15900          * @param {Array} selections Array of currently selected nodes
15901          */
15902             "beforeselect" : true,
15903         /**
15904          * @event preparedata
15905          * Fires on every row to render, to allow you to change the data.
15906          * @param {Roo.View} this
15907          * @param {Object} data to be rendered (change this)
15908          */
15909           "preparedata" : true
15910           
15911           
15912         });
15913
15914
15915
15916     this.el.on({
15917         "click": this.onClick,
15918         "dblclick": this.onDblClick,
15919         "contextmenu": this.onContextMenu,
15920         scope:this
15921     });
15922
15923     this.selections = [];
15924     this.nodes = [];
15925     this.cmp = new Roo.CompositeElementLite([]);
15926     if(this.store){
15927         this.store = Roo.factory(this.store, Roo.data);
15928         this.setStore(this.store, true);
15929     }
15930     
15931     if ( this.footer && this.footer.xtype) {
15932            
15933          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15934         
15935         this.footer.dataSource = this.store;
15936         this.footer.container = fctr;
15937         this.footer = Roo.factory(this.footer, Roo);
15938         fctr.insertFirst(this.el);
15939         
15940         // this is a bit insane - as the paging toolbar seems to detach the el..
15941 //        dom.parentNode.parentNode.parentNode
15942          // they get detached?
15943     }
15944     
15945     
15946     Roo.View.superclass.constructor.call(this);
15947     
15948     
15949 };
15950
15951 Roo.extend(Roo.View, Roo.util.Observable, {
15952     
15953      /**
15954      * @cfg {Roo.data.Store} store Data store to load data from.
15955      */
15956     store : false,
15957     
15958     /**
15959      * @cfg {String|Roo.Element} el The container element.
15960      */
15961     el : '',
15962     
15963     /**
15964      * @cfg {String|Roo.Template} tpl The template used by this View 
15965      */
15966     tpl : false,
15967     /**
15968      * @cfg {String} dataName the named area of the template to use as the data area
15969      *                          Works with domtemplates roo-name="name"
15970      */
15971     dataName: false,
15972     /**
15973      * @cfg {String} selectedClass The css class to add to selected nodes
15974      */
15975     selectedClass : "x-view-selected",
15976      /**
15977      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15978      */
15979     emptyText : "",
15980     
15981     /**
15982      * @cfg {String} text to display on mask (default Loading)
15983      */
15984     mask : false,
15985     /**
15986      * @cfg {Boolean} multiSelect Allow multiple selection
15987      */
15988     multiSelect : false,
15989     /**
15990      * @cfg {Boolean} singleSelect Allow single selection
15991      */
15992     singleSelect:  false,
15993     
15994     /**
15995      * @cfg {Boolean} toggleSelect - selecting 
15996      */
15997     toggleSelect : false,
15998     
15999     /**
16000      * @cfg {Boolean} tickable - selecting 
16001      */
16002     tickable : false,
16003     
16004     /**
16005      * Returns the element this view is bound to.
16006      * @return {Roo.Element}
16007      */
16008     getEl : function(){
16009         return this.wrapEl;
16010     },
16011     
16012     
16013
16014     /**
16015      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16016      */
16017     refresh : function(){
16018         //Roo.log('refresh');
16019         var t = this.tpl;
16020         
16021         // if we are using something like 'domtemplate', then
16022         // the what gets used is:
16023         // t.applySubtemplate(NAME, data, wrapping data..)
16024         // the outer template then get' applied with
16025         //     the store 'extra data'
16026         // and the body get's added to the
16027         //      roo-name="data" node?
16028         //      <span class='roo-tpl-{name}'></span> ?????
16029         
16030         
16031         
16032         this.clearSelections();
16033         this.el.update("");
16034         var html = [];
16035         var records = this.store.getRange();
16036         if(records.length < 1) {
16037             
16038             // is this valid??  = should it render a template??
16039             
16040             this.el.update(this.emptyText);
16041             return;
16042         }
16043         var el = this.el;
16044         if (this.dataName) {
16045             this.el.update(t.apply(this.store.meta)); //????
16046             el = this.el.child('.roo-tpl-' + this.dataName);
16047         }
16048         
16049         for(var i = 0, len = records.length; i < len; i++){
16050             var data = this.prepareData(records[i].data, i, records[i]);
16051             this.fireEvent("preparedata", this, data, i, records[i]);
16052             
16053             var d = Roo.apply({}, data);
16054             
16055             if(this.tickable){
16056                 Roo.apply(d, {'roo-id' : Roo.id()});
16057                 
16058                 var _this = this;
16059             
16060                 Roo.each(this.parent.item, function(item){
16061                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16062                         return;
16063                     }
16064                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16065                 });
16066             }
16067             
16068             html[html.length] = Roo.util.Format.trim(
16069                 this.dataName ?
16070                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16071                     t.apply(d)
16072             );
16073         }
16074         
16075         
16076         
16077         el.update(html.join(""));
16078         this.nodes = el.dom.childNodes;
16079         this.updateIndexes(0);
16080     },
16081     
16082
16083     /**
16084      * Function to override to reformat the data that is sent to
16085      * the template for each node.
16086      * DEPRICATED - use the preparedata event handler.
16087      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16088      * a JSON object for an UpdateManager bound view).
16089      */
16090     prepareData : function(data, index, record)
16091     {
16092         this.fireEvent("preparedata", this, data, index, record);
16093         return data;
16094     },
16095
16096     onUpdate : function(ds, record){
16097         // Roo.log('on update');   
16098         this.clearSelections();
16099         var index = this.store.indexOf(record);
16100         var n = this.nodes[index];
16101         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16102         n.parentNode.removeChild(n);
16103         this.updateIndexes(index, index);
16104     },
16105
16106     
16107     
16108 // --------- FIXME     
16109     onAdd : function(ds, records, index)
16110     {
16111         //Roo.log(['on Add', ds, records, index] );        
16112         this.clearSelections();
16113         if(this.nodes.length == 0){
16114             this.refresh();
16115             return;
16116         }
16117         var n = this.nodes[index];
16118         for(var i = 0, len = records.length; i < len; i++){
16119             var d = this.prepareData(records[i].data, i, records[i]);
16120             if(n){
16121                 this.tpl.insertBefore(n, d);
16122             }else{
16123                 
16124                 this.tpl.append(this.el, d);
16125             }
16126         }
16127         this.updateIndexes(index);
16128     },
16129
16130     onRemove : function(ds, record, index){
16131        // Roo.log('onRemove');
16132         this.clearSelections();
16133         var el = this.dataName  ?
16134             this.el.child('.roo-tpl-' + this.dataName) :
16135             this.el; 
16136         
16137         el.dom.removeChild(this.nodes[index]);
16138         this.updateIndexes(index);
16139     },
16140
16141     /**
16142      * Refresh an individual node.
16143      * @param {Number} index
16144      */
16145     refreshNode : function(index){
16146         this.onUpdate(this.store, this.store.getAt(index));
16147     },
16148
16149     updateIndexes : function(startIndex, endIndex){
16150         var ns = this.nodes;
16151         startIndex = startIndex || 0;
16152         endIndex = endIndex || ns.length - 1;
16153         for(var i = startIndex; i <= endIndex; i++){
16154             ns[i].nodeIndex = i;
16155         }
16156     },
16157
16158     /**
16159      * Changes the data store this view uses and refresh the view.
16160      * @param {Store} store
16161      */
16162     setStore : function(store, initial){
16163         if(!initial && this.store){
16164             this.store.un("datachanged", this.refresh);
16165             this.store.un("add", this.onAdd);
16166             this.store.un("remove", this.onRemove);
16167             this.store.un("update", this.onUpdate);
16168             this.store.un("clear", this.refresh);
16169             this.store.un("beforeload", this.onBeforeLoad);
16170             this.store.un("load", this.onLoad);
16171             this.store.un("loadexception", this.onLoad);
16172         }
16173         if(store){
16174           
16175             store.on("datachanged", this.refresh, this);
16176             store.on("add", this.onAdd, this);
16177             store.on("remove", this.onRemove, this);
16178             store.on("update", this.onUpdate, this);
16179             store.on("clear", this.refresh, this);
16180             store.on("beforeload", this.onBeforeLoad, this);
16181             store.on("load", this.onLoad, this);
16182             store.on("loadexception", this.onLoad, this);
16183         }
16184         
16185         if(store){
16186             this.refresh();
16187         }
16188     },
16189     /**
16190      * onbeforeLoad - masks the loading area.
16191      *
16192      */
16193     onBeforeLoad : function(store,opts)
16194     {
16195          //Roo.log('onBeforeLoad');   
16196         if (!opts.add) {
16197             this.el.update("");
16198         }
16199         this.el.mask(this.mask ? this.mask : "Loading" ); 
16200     },
16201     onLoad : function ()
16202     {
16203         this.el.unmask();
16204     },
16205     
16206
16207     /**
16208      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16209      * @param {HTMLElement} node
16210      * @return {HTMLElement} The template node
16211      */
16212     findItemFromChild : function(node){
16213         var el = this.dataName  ?
16214             this.el.child('.roo-tpl-' + this.dataName,true) :
16215             this.el.dom; 
16216         
16217         if(!node || node.parentNode == el){
16218                     return node;
16219             }
16220             var p = node.parentNode;
16221             while(p && p != el){
16222             if(p.parentNode == el){
16223                 return p;
16224             }
16225             p = p.parentNode;
16226         }
16227             return null;
16228     },
16229
16230     /** @ignore */
16231     onClick : function(e){
16232         var item = this.findItemFromChild(e.getTarget());
16233         if(item){
16234             var index = this.indexOf(item);
16235             if(this.onItemClick(item, index, e) !== false){
16236                 this.fireEvent("click", this, index, item, e);
16237             }
16238         }else{
16239             this.clearSelections();
16240         }
16241     },
16242
16243     /** @ignore */
16244     onContextMenu : function(e){
16245         var item = this.findItemFromChild(e.getTarget());
16246         if(item){
16247             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16248         }
16249     },
16250
16251     /** @ignore */
16252     onDblClick : function(e){
16253         var item = this.findItemFromChild(e.getTarget());
16254         if(item){
16255             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16256         }
16257     },
16258
16259     onItemClick : function(item, index, e)
16260     {
16261         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16262             return false;
16263         }
16264         if (this.toggleSelect) {
16265             var m = this.isSelected(item) ? 'unselect' : 'select';
16266             //Roo.log(m);
16267             var _t = this;
16268             _t[m](item, true, false);
16269             return true;
16270         }
16271         if(this.multiSelect || this.singleSelect){
16272             if(this.multiSelect && e.shiftKey && this.lastSelection){
16273                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16274             }else{
16275                 this.select(item, this.multiSelect && e.ctrlKey);
16276                 this.lastSelection = item;
16277             }
16278             
16279             if(!this.tickable){
16280                 e.preventDefault();
16281             }
16282             
16283         }
16284         return true;
16285     },
16286
16287     /**
16288      * Get the number of selected nodes.
16289      * @return {Number}
16290      */
16291     getSelectionCount : function(){
16292         return this.selections.length;
16293     },
16294
16295     /**
16296      * Get the currently selected nodes.
16297      * @return {Array} An array of HTMLElements
16298      */
16299     getSelectedNodes : function(){
16300         return this.selections;
16301     },
16302
16303     /**
16304      * Get the indexes of the selected nodes.
16305      * @return {Array}
16306      */
16307     getSelectedIndexes : function(){
16308         var indexes = [], s = this.selections;
16309         for(var i = 0, len = s.length; i < len; i++){
16310             indexes.push(s[i].nodeIndex);
16311         }
16312         return indexes;
16313     },
16314
16315     /**
16316      * Clear all selections
16317      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16318      */
16319     clearSelections : function(suppressEvent){
16320         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16321             this.cmp.elements = this.selections;
16322             this.cmp.removeClass(this.selectedClass);
16323             this.selections = [];
16324             if(!suppressEvent){
16325                 this.fireEvent("selectionchange", this, this.selections);
16326             }
16327         }
16328     },
16329
16330     /**
16331      * Returns true if the passed node is selected
16332      * @param {HTMLElement/Number} node The node or node index
16333      * @return {Boolean}
16334      */
16335     isSelected : function(node){
16336         var s = this.selections;
16337         if(s.length < 1){
16338             return false;
16339         }
16340         node = this.getNode(node);
16341         return s.indexOf(node) !== -1;
16342     },
16343
16344     /**
16345      * Selects nodes.
16346      * @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
16347      * @param {Boolean} keepExisting (optional) true to keep existing selections
16348      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16349      */
16350     select : function(nodeInfo, keepExisting, suppressEvent){
16351         if(nodeInfo instanceof Array){
16352             if(!keepExisting){
16353                 this.clearSelections(true);
16354             }
16355             for(var i = 0, len = nodeInfo.length; i < len; i++){
16356                 this.select(nodeInfo[i], true, true);
16357             }
16358             return;
16359         } 
16360         var node = this.getNode(nodeInfo);
16361         if(!node || this.isSelected(node)){
16362             return; // already selected.
16363         }
16364         if(!keepExisting){
16365             this.clearSelections(true);
16366         }
16367         
16368         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16369             Roo.fly(node).addClass(this.selectedClass);
16370             this.selections.push(node);
16371             if(!suppressEvent){
16372                 this.fireEvent("selectionchange", this, this.selections);
16373             }
16374         }
16375         
16376         
16377     },
16378       /**
16379      * Unselects nodes.
16380      * @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
16381      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16382      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16383      */
16384     unselect : function(nodeInfo, keepExisting, suppressEvent)
16385     {
16386         if(nodeInfo instanceof Array){
16387             Roo.each(this.selections, function(s) {
16388                 this.unselect(s, nodeInfo);
16389             }, this);
16390             return;
16391         }
16392         var node = this.getNode(nodeInfo);
16393         if(!node || !this.isSelected(node)){
16394             //Roo.log("not selected");
16395             return; // not selected.
16396         }
16397         // fireevent???
16398         var ns = [];
16399         Roo.each(this.selections, function(s) {
16400             if (s == node ) {
16401                 Roo.fly(node).removeClass(this.selectedClass);
16402
16403                 return;
16404             }
16405             ns.push(s);
16406         },this);
16407         
16408         this.selections= ns;
16409         this.fireEvent("selectionchange", this, this.selections);
16410     },
16411
16412     /**
16413      * Gets a template node.
16414      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16415      * @return {HTMLElement} The node or null if it wasn't found
16416      */
16417     getNode : function(nodeInfo){
16418         if(typeof nodeInfo == "string"){
16419             return document.getElementById(nodeInfo);
16420         }else if(typeof nodeInfo == "number"){
16421             return this.nodes[nodeInfo];
16422         }
16423         return nodeInfo;
16424     },
16425
16426     /**
16427      * Gets a range template nodes.
16428      * @param {Number} startIndex
16429      * @param {Number} endIndex
16430      * @return {Array} An array of nodes
16431      */
16432     getNodes : function(start, end){
16433         var ns = this.nodes;
16434         start = start || 0;
16435         end = typeof end == "undefined" ? ns.length - 1 : end;
16436         var nodes = [];
16437         if(start <= end){
16438             for(var i = start; i <= end; i++){
16439                 nodes.push(ns[i]);
16440             }
16441         } else{
16442             for(var i = start; i >= end; i--){
16443                 nodes.push(ns[i]);
16444             }
16445         }
16446         return nodes;
16447     },
16448
16449     /**
16450      * Finds the index of the passed node
16451      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16452      * @return {Number} The index of the node or -1
16453      */
16454     indexOf : function(node){
16455         node = this.getNode(node);
16456         if(typeof node.nodeIndex == "number"){
16457             return node.nodeIndex;
16458         }
16459         var ns = this.nodes;
16460         for(var i = 0, len = ns.length; i < len; i++){
16461             if(ns[i] == node){
16462                 return i;
16463             }
16464         }
16465         return -1;
16466     }
16467 });
16468 /*
16469  * - LGPL
16470  *
16471  * based on jquery fullcalendar
16472  * 
16473  */
16474
16475 Roo.bootstrap = Roo.bootstrap || {};
16476 /**
16477  * @class Roo.bootstrap.Calendar
16478  * @extends Roo.bootstrap.Component
16479  * Bootstrap Calendar class
16480  * @cfg {Boolean} loadMask (true|false) default false
16481  * @cfg {Object} header generate the user specific header of the calendar, default false
16482
16483  * @constructor
16484  * Create a new Container
16485  * @param {Object} config The config object
16486  */
16487
16488
16489
16490 Roo.bootstrap.Calendar = function(config){
16491     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16492      this.addEvents({
16493         /**
16494              * @event select
16495              * Fires when a date is selected
16496              * @param {DatePicker} this
16497              * @param {Date} date The selected date
16498              */
16499         'select': true,
16500         /**
16501              * @event monthchange
16502              * Fires when the displayed month changes 
16503              * @param {DatePicker} this
16504              * @param {Date} date The selected month
16505              */
16506         'monthchange': true,
16507         /**
16508              * @event evententer
16509              * Fires when mouse over an event
16510              * @param {Calendar} this
16511              * @param {event} Event
16512              */
16513         'evententer': true,
16514         /**
16515              * @event eventleave
16516              * Fires when the mouse leaves an
16517              * @param {Calendar} this
16518              * @param {event}
16519              */
16520         'eventleave': true,
16521         /**
16522              * @event eventclick
16523              * Fires when the mouse click an
16524              * @param {Calendar} this
16525              * @param {event}
16526              */
16527         'eventclick': true
16528         
16529     });
16530
16531 };
16532
16533 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16534     
16535      /**
16536      * @cfg {Number} startDay
16537      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16538      */
16539     startDay : 0,
16540     
16541     loadMask : false,
16542     
16543     header : false,
16544       
16545     getAutoCreate : function(){
16546         
16547         
16548         var fc_button = function(name, corner, style, content ) {
16549             return Roo.apply({},{
16550                 tag : 'span',
16551                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16552                          (corner.length ?
16553                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16554                             ''
16555                         ),
16556                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16557                 unselectable: 'on'
16558             });
16559         };
16560         
16561         var header = {};
16562         
16563         if(!this.header){
16564             header = {
16565                 tag : 'table',
16566                 cls : 'fc-header',
16567                 style : 'width:100%',
16568                 cn : [
16569                     {
16570                         tag: 'tr',
16571                         cn : [
16572                             {
16573                                 tag : 'td',
16574                                 cls : 'fc-header-left',
16575                                 cn : [
16576                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16577                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16578                                     { tag: 'span', cls: 'fc-header-space' },
16579                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16580
16581
16582                                 ]
16583                             },
16584
16585                             {
16586                                 tag : 'td',
16587                                 cls : 'fc-header-center',
16588                                 cn : [
16589                                     {
16590                                         tag: 'span',
16591                                         cls: 'fc-header-title',
16592                                         cn : {
16593                                             tag: 'H2',
16594                                             html : 'month / year'
16595                                         }
16596                                     }
16597
16598                                 ]
16599                             },
16600                             {
16601                                 tag : 'td',
16602                                 cls : 'fc-header-right',
16603                                 cn : [
16604                               /*      fc_button('month', 'left', '', 'month' ),
16605                                     fc_button('week', '', '', 'week' ),
16606                                     fc_button('day', 'right', '', 'day' )
16607                                 */    
16608
16609                                 ]
16610                             }
16611
16612                         ]
16613                     }
16614                 ]
16615             };
16616         }
16617         
16618         header = this.header;
16619         
16620        
16621         var cal_heads = function() {
16622             var ret = [];
16623             // fixme - handle this.
16624             
16625             for (var i =0; i < Date.dayNames.length; i++) {
16626                 var d = Date.dayNames[i];
16627                 ret.push({
16628                     tag: 'th',
16629                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16630                     html : d.substring(0,3)
16631                 });
16632                 
16633             }
16634             ret[0].cls += ' fc-first';
16635             ret[6].cls += ' fc-last';
16636             return ret;
16637         };
16638         var cal_cell = function(n) {
16639             return  {
16640                 tag: 'td',
16641                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16642                 cn : [
16643                     {
16644                         cn : [
16645                             {
16646                                 cls: 'fc-day-number',
16647                                 html: 'D'
16648                             },
16649                             {
16650                                 cls: 'fc-day-content',
16651                              
16652                                 cn : [
16653                                      {
16654                                         style: 'position: relative;' // height: 17px;
16655                                     }
16656                                 ]
16657                             }
16658                             
16659                             
16660                         ]
16661                     }
16662                 ]
16663                 
16664             }
16665         };
16666         var cal_rows = function() {
16667             
16668             var ret = [];
16669             for (var r = 0; r < 6; r++) {
16670                 var row= {
16671                     tag : 'tr',
16672                     cls : 'fc-week',
16673                     cn : []
16674                 };
16675                 
16676                 for (var i =0; i < Date.dayNames.length; i++) {
16677                     var d = Date.dayNames[i];
16678                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16679
16680                 }
16681                 row.cn[0].cls+=' fc-first';
16682                 row.cn[0].cn[0].style = 'min-height:90px';
16683                 row.cn[6].cls+=' fc-last';
16684                 ret.push(row);
16685                 
16686             }
16687             ret[0].cls += ' fc-first';
16688             ret[4].cls += ' fc-prev-last';
16689             ret[5].cls += ' fc-last';
16690             return ret;
16691             
16692         };
16693         
16694         var cal_table = {
16695             tag: 'table',
16696             cls: 'fc-border-separate',
16697             style : 'width:100%',
16698             cellspacing  : 0,
16699             cn : [
16700                 { 
16701                     tag: 'thead',
16702                     cn : [
16703                         { 
16704                             tag: 'tr',
16705                             cls : 'fc-first fc-last',
16706                             cn : cal_heads()
16707                         }
16708                     ]
16709                 },
16710                 { 
16711                     tag: 'tbody',
16712                     cn : cal_rows()
16713                 }
16714                   
16715             ]
16716         };
16717          
16718          var cfg = {
16719             cls : 'fc fc-ltr',
16720             cn : [
16721                 header,
16722                 {
16723                     cls : 'fc-content',
16724                     style : "position: relative;",
16725                     cn : [
16726                         {
16727                             cls : 'fc-view fc-view-month fc-grid',
16728                             style : 'position: relative',
16729                             unselectable : 'on',
16730                             cn : [
16731                                 {
16732                                     cls : 'fc-event-container',
16733                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16734                                 },
16735                                 cal_table
16736                             ]
16737                         }
16738                     ]
16739     
16740                 }
16741            ] 
16742             
16743         };
16744         
16745          
16746         
16747         return cfg;
16748     },
16749     
16750     
16751     initEvents : function()
16752     {
16753         if(!this.store){
16754             throw "can not find store for calendar";
16755         }
16756         
16757         var mark = {
16758             tag: "div",
16759             cls:"x-dlg-mask",
16760             style: "text-align:center",
16761             cn: [
16762                 {
16763                     tag: "div",
16764                     style: "background-color:white;width:50%;margin:250 auto",
16765                     cn: [
16766                         {
16767                             tag: "img",
16768                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16769                         },
16770                         {
16771                             tag: "span",
16772                             html: "Loading"
16773                         }
16774                         
16775                     ]
16776                 }
16777             ]
16778         };
16779         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16780         
16781         var size = this.el.select('.fc-content', true).first().getSize();
16782         this.maskEl.setSize(size.width, size.height);
16783         this.maskEl.enableDisplayMode("block");
16784         if(!this.loadMask){
16785             this.maskEl.hide();
16786         }
16787         
16788         this.store = Roo.factory(this.store, Roo.data);
16789         this.store.on('load', this.onLoad, this);
16790         this.store.on('beforeload', this.onBeforeLoad, this);
16791         
16792         this.resize();
16793         
16794         this.cells = this.el.select('.fc-day',true);
16795         //Roo.log(this.cells);
16796         this.textNodes = this.el.query('.fc-day-number');
16797         this.cells.addClassOnOver('fc-state-hover');
16798         
16799         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16800         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16801         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16802         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16803         
16804         this.on('monthchange', this.onMonthChange, this);
16805         
16806         this.update(new Date().clearTime());
16807     },
16808     
16809     resize : function() {
16810         var sz  = this.el.getSize();
16811         
16812         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16813         this.el.select('.fc-day-content div',true).setHeight(34);
16814     },
16815     
16816     
16817     // private
16818     showPrevMonth : function(e){
16819         this.update(this.activeDate.add("mo", -1));
16820     },
16821     showToday : function(e){
16822         this.update(new Date().clearTime());
16823     },
16824     // private
16825     showNextMonth : function(e){
16826         this.update(this.activeDate.add("mo", 1));
16827     },
16828
16829     // private
16830     showPrevYear : function(){
16831         this.update(this.activeDate.add("y", -1));
16832     },
16833
16834     // private
16835     showNextYear : function(){
16836         this.update(this.activeDate.add("y", 1));
16837     },
16838
16839     
16840    // private
16841     update : function(date)
16842     {
16843         var vd = this.activeDate;
16844         this.activeDate = date;
16845 //        if(vd && this.el){
16846 //            var t = date.getTime();
16847 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16848 //                Roo.log('using add remove');
16849 //                
16850 //                this.fireEvent('monthchange', this, date);
16851 //                
16852 //                this.cells.removeClass("fc-state-highlight");
16853 //                this.cells.each(function(c){
16854 //                   if(c.dateValue == t){
16855 //                       c.addClass("fc-state-highlight");
16856 //                       setTimeout(function(){
16857 //                            try{c.dom.firstChild.focus();}catch(e){}
16858 //                       }, 50);
16859 //                       return false;
16860 //                   }
16861 //                   return true;
16862 //                });
16863 //                return;
16864 //            }
16865 //        }
16866         
16867         var days = date.getDaysInMonth();
16868         
16869         var firstOfMonth = date.getFirstDateOfMonth();
16870         var startingPos = firstOfMonth.getDay()-this.startDay;
16871         
16872         if(startingPos < this.startDay){
16873             startingPos += 7;
16874         }
16875         
16876         var pm = date.add(Date.MONTH, -1);
16877         var prevStart = pm.getDaysInMonth()-startingPos;
16878 //        
16879         this.cells = this.el.select('.fc-day',true);
16880         this.textNodes = this.el.query('.fc-day-number');
16881         this.cells.addClassOnOver('fc-state-hover');
16882         
16883         var cells = this.cells.elements;
16884         var textEls = this.textNodes;
16885         
16886         Roo.each(cells, function(cell){
16887             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16888         });
16889         
16890         days += startingPos;
16891
16892         // convert everything to numbers so it's fast
16893         var day = 86400000;
16894         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16895         //Roo.log(d);
16896         //Roo.log(pm);
16897         //Roo.log(prevStart);
16898         
16899         var today = new Date().clearTime().getTime();
16900         var sel = date.clearTime().getTime();
16901         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16902         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16903         var ddMatch = this.disabledDatesRE;
16904         var ddText = this.disabledDatesText;
16905         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16906         var ddaysText = this.disabledDaysText;
16907         var format = this.format;
16908         
16909         var setCellClass = function(cal, cell){
16910             cell.row = 0;
16911             cell.events = [];
16912             cell.more = [];
16913             //Roo.log('set Cell Class');
16914             cell.title = "";
16915             var t = d.getTime();
16916             
16917             //Roo.log(d);
16918             
16919             cell.dateValue = t;
16920             if(t == today){
16921                 cell.className += " fc-today";
16922                 cell.className += " fc-state-highlight";
16923                 cell.title = cal.todayText;
16924             }
16925             if(t == sel){
16926                 // disable highlight in other month..
16927                 //cell.className += " fc-state-highlight";
16928                 
16929             }
16930             // disabling
16931             if(t < min) {
16932                 cell.className = " fc-state-disabled";
16933                 cell.title = cal.minText;
16934                 return;
16935             }
16936             if(t > max) {
16937                 cell.className = " fc-state-disabled";
16938                 cell.title = cal.maxText;
16939                 return;
16940             }
16941             if(ddays){
16942                 if(ddays.indexOf(d.getDay()) != -1){
16943                     cell.title = ddaysText;
16944                     cell.className = " fc-state-disabled";
16945                 }
16946             }
16947             if(ddMatch && format){
16948                 var fvalue = d.dateFormat(format);
16949                 if(ddMatch.test(fvalue)){
16950                     cell.title = ddText.replace("%0", fvalue);
16951                     cell.className = " fc-state-disabled";
16952                 }
16953             }
16954             
16955             if (!cell.initialClassName) {
16956                 cell.initialClassName = cell.dom.className;
16957             }
16958             
16959             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16960         };
16961
16962         var i = 0;
16963         
16964         for(; i < startingPos; i++) {
16965             textEls[i].innerHTML = (++prevStart);
16966             d.setDate(d.getDate()+1);
16967             
16968             cells[i].className = "fc-past fc-other-month";
16969             setCellClass(this, cells[i]);
16970         }
16971         
16972         var intDay = 0;
16973         
16974         for(; i < days; i++){
16975             intDay = i - startingPos + 1;
16976             textEls[i].innerHTML = (intDay);
16977             d.setDate(d.getDate()+1);
16978             
16979             cells[i].className = ''; // "x-date-active";
16980             setCellClass(this, cells[i]);
16981         }
16982         var extraDays = 0;
16983         
16984         for(; i < 42; i++) {
16985             textEls[i].innerHTML = (++extraDays);
16986             d.setDate(d.getDate()+1);
16987             
16988             cells[i].className = "fc-future fc-other-month";
16989             setCellClass(this, cells[i]);
16990         }
16991         
16992         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16993         
16994         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16995         
16996         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16997         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16998         
16999         if(totalRows != 6){
17000             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17001             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17002         }
17003         
17004         this.fireEvent('monthchange', this, date);
17005         
17006         
17007         /*
17008         if(!this.internalRender){
17009             var main = this.el.dom.firstChild;
17010             var w = main.offsetWidth;
17011             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17012             Roo.fly(main).setWidth(w);
17013             this.internalRender = true;
17014             // opera does not respect the auto grow header center column
17015             // then, after it gets a width opera refuses to recalculate
17016             // without a second pass
17017             if(Roo.isOpera && !this.secondPass){
17018                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17019                 this.secondPass = true;
17020                 this.update.defer(10, this, [date]);
17021             }
17022         }
17023         */
17024         
17025     },
17026     
17027     findCell : function(dt) {
17028         dt = dt.clearTime().getTime();
17029         var ret = false;
17030         this.cells.each(function(c){
17031             //Roo.log("check " +c.dateValue + '?=' + dt);
17032             if(c.dateValue == dt){
17033                 ret = c;
17034                 return false;
17035             }
17036             return true;
17037         });
17038         
17039         return ret;
17040     },
17041     
17042     findCells : function(ev) {
17043         var s = ev.start.clone().clearTime().getTime();
17044        // Roo.log(s);
17045         var e= ev.end.clone().clearTime().getTime();
17046        // Roo.log(e);
17047         var ret = [];
17048         this.cells.each(function(c){
17049              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17050             
17051             if(c.dateValue > e){
17052                 return ;
17053             }
17054             if(c.dateValue < s){
17055                 return ;
17056             }
17057             ret.push(c);
17058         });
17059         
17060         return ret;    
17061     },
17062     
17063 //    findBestRow: function(cells)
17064 //    {
17065 //        var ret = 0;
17066 //        
17067 //        for (var i =0 ; i < cells.length;i++) {
17068 //            ret  = Math.max(cells[i].rows || 0,ret);
17069 //        }
17070 //        return ret;
17071 //        
17072 //    },
17073     
17074     
17075     addItem : function(ev)
17076     {
17077         // look for vertical location slot in
17078         var cells = this.findCells(ev);
17079         
17080 //        ev.row = this.findBestRow(cells);
17081         
17082         // work out the location.
17083         
17084         var crow = false;
17085         var rows = [];
17086         for(var i =0; i < cells.length; i++) {
17087             
17088             cells[i].row = cells[0].row;
17089             
17090             if(i == 0){
17091                 cells[i].row = cells[i].row + 1;
17092             }
17093             
17094             if (!crow) {
17095                 crow = {
17096                     start : cells[i],
17097                     end :  cells[i]
17098                 };
17099                 continue;
17100             }
17101             if (crow.start.getY() == cells[i].getY()) {
17102                 // on same row.
17103                 crow.end = cells[i];
17104                 continue;
17105             }
17106             // different row.
17107             rows.push(crow);
17108             crow = {
17109                 start: cells[i],
17110                 end : cells[i]
17111             };
17112             
17113         }
17114         
17115         rows.push(crow);
17116         ev.els = [];
17117         ev.rows = rows;
17118         ev.cells = cells;
17119         
17120         cells[0].events.push(ev);
17121         
17122         this.calevents.push(ev);
17123     },
17124     
17125     clearEvents: function() {
17126         
17127         if(!this.calevents){
17128             return;
17129         }
17130         
17131         Roo.each(this.cells.elements, function(c){
17132             c.row = 0;
17133             c.events = [];
17134             c.more = [];
17135         });
17136         
17137         Roo.each(this.calevents, function(e) {
17138             Roo.each(e.els, function(el) {
17139                 el.un('mouseenter' ,this.onEventEnter, this);
17140                 el.un('mouseleave' ,this.onEventLeave, this);
17141                 el.remove();
17142             },this);
17143         },this);
17144         
17145         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17146             e.remove();
17147         });
17148         
17149     },
17150     
17151     renderEvents: function()
17152     {   
17153         var _this = this;
17154         
17155         this.cells.each(function(c) {
17156             
17157             if(c.row < 5){
17158                 return;
17159             }
17160             
17161             var ev = c.events;
17162             
17163             var r = 4;
17164             if(c.row != c.events.length){
17165                 r = 4 - (4 - (c.row - c.events.length));
17166             }
17167             
17168             c.events = ev.slice(0, r);
17169             c.more = ev.slice(r);
17170             
17171             if(c.more.length && c.more.length == 1){
17172                 c.events.push(c.more.pop());
17173             }
17174             
17175             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17176             
17177         });
17178             
17179         this.cells.each(function(c) {
17180             
17181             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17182             
17183             
17184             for (var e = 0; e < c.events.length; e++){
17185                 var ev = c.events[e];
17186                 var rows = ev.rows;
17187                 
17188                 for(var i = 0; i < rows.length; i++) {
17189                 
17190                     // how many rows should it span..
17191
17192                     var  cfg = {
17193                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17194                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17195
17196                         unselectable : "on",
17197                         cn : [
17198                             {
17199                                 cls: 'fc-event-inner',
17200                                 cn : [
17201     //                                {
17202     //                                  tag:'span',
17203     //                                  cls: 'fc-event-time',
17204     //                                  html : cells.length > 1 ? '' : ev.time
17205     //                                },
17206                                     {
17207                                       tag:'span',
17208                                       cls: 'fc-event-title',
17209                                       html : String.format('{0}', ev.title)
17210                                     }
17211
17212
17213                                 ]
17214                             },
17215                             {
17216                                 cls: 'ui-resizable-handle ui-resizable-e',
17217                                 html : '&nbsp;&nbsp;&nbsp'
17218                             }
17219
17220                         ]
17221                     };
17222
17223                     if (i == 0) {
17224                         cfg.cls += ' fc-event-start';
17225                     }
17226                     if ((i+1) == rows.length) {
17227                         cfg.cls += ' fc-event-end';
17228                     }
17229
17230                     var ctr = _this.el.select('.fc-event-container',true).first();
17231                     var cg = ctr.createChild(cfg);
17232
17233                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17234                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17235
17236                     var r = (c.more.length) ? 1 : 0;
17237                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17238                     cg.setWidth(ebox.right - sbox.x -2);
17239
17240                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17241                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17242                     cg.on('click', _this.onEventClick, _this, ev);
17243
17244                     ev.els.push(cg);
17245                     
17246                 }
17247                 
17248             }
17249             
17250             
17251             if(c.more.length){
17252                 var  cfg = {
17253                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17254                     style : 'position: absolute',
17255                     unselectable : "on",
17256                     cn : [
17257                         {
17258                             cls: 'fc-event-inner',
17259                             cn : [
17260                                 {
17261                                   tag:'span',
17262                                   cls: 'fc-event-title',
17263                                   html : 'More'
17264                                 }
17265
17266
17267                             ]
17268                         },
17269                         {
17270                             cls: 'ui-resizable-handle ui-resizable-e',
17271                             html : '&nbsp;&nbsp;&nbsp'
17272                         }
17273
17274                     ]
17275                 };
17276
17277                 var ctr = _this.el.select('.fc-event-container',true).first();
17278                 var cg = ctr.createChild(cfg);
17279
17280                 var sbox = c.select('.fc-day-content',true).first().getBox();
17281                 var ebox = c.select('.fc-day-content',true).first().getBox();
17282                 //Roo.log(cg);
17283                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17284                 cg.setWidth(ebox.right - sbox.x -2);
17285
17286                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17287                 
17288             }
17289             
17290         });
17291         
17292         
17293         
17294     },
17295     
17296     onEventEnter: function (e, el,event,d) {
17297         this.fireEvent('evententer', this, el, event);
17298     },
17299     
17300     onEventLeave: function (e, el,event,d) {
17301         this.fireEvent('eventleave', this, el, event);
17302     },
17303     
17304     onEventClick: function (e, el,event,d) {
17305         this.fireEvent('eventclick', this, el, event);
17306     },
17307     
17308     onMonthChange: function () {
17309         this.store.load();
17310     },
17311     
17312     onMoreEventClick: function(e, el, more)
17313     {
17314         var _this = this;
17315         
17316         this.calpopover.placement = 'right';
17317         this.calpopover.setTitle('More');
17318         
17319         this.calpopover.setContent('');
17320         
17321         var ctr = this.calpopover.el.select('.popover-content', true).first();
17322         
17323         Roo.each(more, function(m){
17324             var cfg = {
17325                 cls : 'fc-event-hori fc-event-draggable',
17326                 html : m.title
17327             };
17328             var cg = ctr.createChild(cfg);
17329             
17330             cg.on('click', _this.onEventClick, _this, m);
17331         });
17332         
17333         this.calpopover.show(el);
17334         
17335         
17336     },
17337     
17338     onLoad: function () 
17339     {   
17340         this.calevents = [];
17341         var cal = this;
17342         
17343         if(this.store.getCount() > 0){
17344             this.store.data.each(function(d){
17345                cal.addItem({
17346                     id : d.data.id,
17347                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17348                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17349                     time : d.data.start_time,
17350                     title : d.data.title,
17351                     description : d.data.description,
17352                     venue : d.data.venue
17353                 });
17354             });
17355         }
17356         
17357         this.renderEvents();
17358         
17359         if(this.calevents.length && this.loadMask){
17360             this.maskEl.hide();
17361         }
17362     },
17363     
17364     onBeforeLoad: function()
17365     {
17366         this.clearEvents();
17367         if(this.loadMask){
17368             this.maskEl.show();
17369         }
17370     }
17371 });
17372
17373  
17374  /*
17375  * - LGPL
17376  *
17377  * element
17378  * 
17379  */
17380
17381 /**
17382  * @class Roo.bootstrap.Popover
17383  * @extends Roo.bootstrap.Component
17384  * Bootstrap Popover class
17385  * @cfg {String} html contents of the popover   (or false to use children..)
17386  * @cfg {String} title of popover (or false to hide)
17387  * @cfg {String} placement how it is placed
17388  * @cfg {String} trigger click || hover (or false to trigger manually)
17389  * @cfg {String} over what (parent or false to trigger manually.)
17390  * @cfg {Number} delay - delay before showing
17391  
17392  * @constructor
17393  * Create a new Popover
17394  * @param {Object} config The config object
17395  */
17396
17397 Roo.bootstrap.Popover = function(config){
17398     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17399     
17400     this.addEvents({
17401         // raw events
17402          /**
17403          * @event show
17404          * After the popover show
17405          * 
17406          * @param {Roo.bootstrap.Popover} this
17407          */
17408         "show" : true,
17409         /**
17410          * @event hide
17411          * After the popover hide
17412          * 
17413          * @param {Roo.bootstrap.Popover} this
17414          */
17415         "hide" : true
17416     });
17417 };
17418
17419 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17420     
17421     title: 'Fill in a title',
17422     html: false,
17423     
17424     placement : 'right',
17425     trigger : 'hover', // hover
17426     
17427     delay : 0,
17428     
17429     over: 'parent',
17430     
17431     can_build_overlaid : false,
17432     
17433     getChildContainer : function()
17434     {
17435         return this.el.select('.popover-content',true).first();
17436     },
17437     
17438     getAutoCreate : function(){
17439          
17440         var cfg = {
17441            cls : 'popover roo-dynamic',
17442            style: 'display:block',
17443            cn : [
17444                 {
17445                     cls : 'arrow'
17446                 },
17447                 {
17448                     cls : 'popover-inner',
17449                     cn : [
17450                         {
17451                             tag: 'h3',
17452                             cls: 'popover-title',
17453                             html : this.title
17454                         },
17455                         {
17456                             cls : 'popover-content',
17457                             html : this.html
17458                         }
17459                     ]
17460                     
17461                 }
17462            ]
17463         };
17464         
17465         return cfg;
17466     },
17467     setTitle: function(str)
17468     {
17469         this.title = str;
17470         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17471     },
17472     setContent: function(str)
17473     {
17474         this.html = str;
17475         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17476     },
17477     // as it get's added to the bottom of the page.
17478     onRender : function(ct, position)
17479     {
17480         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17481         if(!this.el){
17482             var cfg = Roo.apply({},  this.getAutoCreate());
17483             cfg.id = Roo.id();
17484             
17485             if (this.cls) {
17486                 cfg.cls += ' ' + this.cls;
17487             }
17488             if (this.style) {
17489                 cfg.style = this.style;
17490             }
17491             //Roo.log("adding to ");
17492             this.el = Roo.get(document.body).createChild(cfg, position);
17493 //            Roo.log(this.el);
17494         }
17495         this.initEvents();
17496     },
17497     
17498     initEvents : function()
17499     {
17500         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17501         this.el.enableDisplayMode('block');
17502         this.el.hide();
17503         if (this.over === false) {
17504             return; 
17505         }
17506         if (this.triggers === false) {
17507             return;
17508         }
17509         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17510         var triggers = this.trigger ? this.trigger.split(' ') : [];
17511         Roo.each(triggers, function(trigger) {
17512         
17513             if (trigger == 'click') {
17514                 on_el.on('click', this.toggle, this);
17515             } else if (trigger != 'manual') {
17516                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17517                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17518       
17519                 on_el.on(eventIn  ,this.enter, this);
17520                 on_el.on(eventOut, this.leave, this);
17521             }
17522         }, this);
17523         
17524     },
17525     
17526     
17527     // private
17528     timeout : null,
17529     hoverState : null,
17530     
17531     toggle : function () {
17532         this.hoverState == 'in' ? this.leave() : this.enter();
17533     },
17534     
17535     enter : function () {
17536         
17537         clearTimeout(this.timeout);
17538     
17539         this.hoverState = 'in';
17540     
17541         if (!this.delay || !this.delay.show) {
17542             this.show();
17543             return;
17544         }
17545         var _t = this;
17546         this.timeout = setTimeout(function () {
17547             if (_t.hoverState == 'in') {
17548                 _t.show();
17549             }
17550         }, this.delay.show)
17551     },
17552     
17553     leave : function() {
17554         clearTimeout(this.timeout);
17555     
17556         this.hoverState = 'out';
17557     
17558         if (!this.delay || !this.delay.hide) {
17559             this.hide();
17560             return;
17561         }
17562         var _t = this;
17563         this.timeout = setTimeout(function () {
17564             if (_t.hoverState == 'out') {
17565                 _t.hide();
17566             }
17567         }, this.delay.hide)
17568     },
17569     
17570     show : function (on_el)
17571     {
17572         if (!on_el) {
17573             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17574         }
17575         
17576         // set content.
17577         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17578         if (this.html !== false) {
17579             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17580         }
17581         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17582         if (!this.title.length) {
17583             this.el.select('.popover-title',true).hide();
17584         }
17585         
17586         var placement = typeof this.placement == 'function' ?
17587             this.placement.call(this, this.el, on_el) :
17588             this.placement;
17589             
17590         var autoToken = /\s?auto?\s?/i;
17591         var autoPlace = autoToken.test(placement);
17592         if (autoPlace) {
17593             placement = placement.replace(autoToken, '') || 'top';
17594         }
17595         
17596         //this.el.detach()
17597         //this.el.setXY([0,0]);
17598         this.el.show();
17599         this.el.dom.style.display='block';
17600         this.el.addClass(placement);
17601         
17602         //this.el.appendTo(on_el);
17603         
17604         var p = this.getPosition();
17605         var box = this.el.getBox();
17606         
17607         if (autoPlace) {
17608             // fixme..
17609         }
17610         var align = Roo.bootstrap.Popover.alignment[placement];
17611         
17612 //        Roo.log(align);
17613         this.el.alignTo(on_el, align[0],align[1]);
17614         //var arrow = this.el.select('.arrow',true).first();
17615         //arrow.set(align[2], 
17616         
17617         this.el.addClass('in');
17618         
17619         
17620         if (this.el.hasClass('fade')) {
17621             // fade it?
17622         }
17623         
17624         this.hoverState = 'in';
17625         
17626         this.fireEvent('show', this);
17627         
17628     },
17629     hide : function()
17630     {
17631         this.el.setXY([0,0]);
17632         this.el.removeClass('in');
17633         this.el.hide();
17634         this.hoverState = null;
17635         
17636         this.fireEvent('hide', this);
17637     }
17638     
17639 });
17640
17641 Roo.bootstrap.Popover.alignment = {
17642     'left' : ['r-l', [-10,0], 'right'],
17643     'right' : ['l-r', [10,0], 'left'],
17644     'bottom' : ['t-b', [0,10], 'top'],
17645     'top' : [ 'b-t', [0,-10], 'bottom']
17646 };
17647
17648  /*
17649  * - LGPL
17650  *
17651  * Progress
17652  * 
17653  */
17654
17655 /**
17656  * @class Roo.bootstrap.Progress
17657  * @extends Roo.bootstrap.Component
17658  * Bootstrap Progress class
17659  * @cfg {Boolean} striped striped of the progress bar
17660  * @cfg {Boolean} active animated of the progress bar
17661  * 
17662  * 
17663  * @constructor
17664  * Create a new Progress
17665  * @param {Object} config The config object
17666  */
17667
17668 Roo.bootstrap.Progress = function(config){
17669     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17670 };
17671
17672 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17673     
17674     striped : false,
17675     active: false,
17676     
17677     getAutoCreate : function(){
17678         var cfg = {
17679             tag: 'div',
17680             cls: 'progress'
17681         };
17682         
17683         
17684         if(this.striped){
17685             cfg.cls += ' progress-striped';
17686         }
17687       
17688         if(this.active){
17689             cfg.cls += ' active';
17690         }
17691         
17692         
17693         return cfg;
17694     }
17695    
17696 });
17697
17698  
17699
17700  /*
17701  * - LGPL
17702  *
17703  * ProgressBar
17704  * 
17705  */
17706
17707 /**
17708  * @class Roo.bootstrap.ProgressBar
17709  * @extends Roo.bootstrap.Component
17710  * Bootstrap ProgressBar class
17711  * @cfg {Number} aria_valuenow aria-value now
17712  * @cfg {Number} aria_valuemin aria-value min
17713  * @cfg {Number} aria_valuemax aria-value max
17714  * @cfg {String} label label for the progress bar
17715  * @cfg {String} panel (success | info | warning | danger )
17716  * @cfg {String} role role of the progress bar
17717  * @cfg {String} sr_only text
17718  * 
17719  * 
17720  * @constructor
17721  * Create a new ProgressBar
17722  * @param {Object} config The config object
17723  */
17724
17725 Roo.bootstrap.ProgressBar = function(config){
17726     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17727 };
17728
17729 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17730     
17731     aria_valuenow : 0,
17732     aria_valuemin : 0,
17733     aria_valuemax : 100,
17734     label : false,
17735     panel : false,
17736     role : false,
17737     sr_only: false,
17738     
17739     getAutoCreate : function()
17740     {
17741         
17742         var cfg = {
17743             tag: 'div',
17744             cls: 'progress-bar',
17745             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17746         };
17747         
17748         if(this.sr_only){
17749             cfg.cn = {
17750                 tag: 'span',
17751                 cls: 'sr-only',
17752                 html: this.sr_only
17753             }
17754         }
17755         
17756         if(this.role){
17757             cfg.role = this.role;
17758         }
17759         
17760         if(this.aria_valuenow){
17761             cfg['aria-valuenow'] = this.aria_valuenow;
17762         }
17763         
17764         if(this.aria_valuemin){
17765             cfg['aria-valuemin'] = this.aria_valuemin;
17766         }
17767         
17768         if(this.aria_valuemax){
17769             cfg['aria-valuemax'] = this.aria_valuemax;
17770         }
17771         
17772         if(this.label && !this.sr_only){
17773             cfg.html = this.label;
17774         }
17775         
17776         if(this.panel){
17777             cfg.cls += ' progress-bar-' + this.panel;
17778         }
17779         
17780         return cfg;
17781     },
17782     
17783     update : function(aria_valuenow)
17784     {
17785         this.aria_valuenow = aria_valuenow;
17786         
17787         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17788     }
17789    
17790 });
17791
17792  
17793
17794  /*
17795  * - LGPL
17796  *
17797  * column
17798  * 
17799  */
17800
17801 /**
17802  * @class Roo.bootstrap.TabGroup
17803  * @extends Roo.bootstrap.Column
17804  * Bootstrap Column class
17805  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17806  * @cfg {Boolean} carousel true to make the group behave like a carousel
17807  * @cfg {Boolean} bullets show bullets for the panels
17808  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17809  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17810  * @cfg {Boolean} showarrow (true|false) show arrow default true
17811  * 
17812  * @constructor
17813  * Create a new TabGroup
17814  * @param {Object} config The config object
17815  */
17816
17817 Roo.bootstrap.TabGroup = function(config){
17818     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17819     if (!this.navId) {
17820         this.navId = Roo.id();
17821     }
17822     this.tabs = [];
17823     Roo.bootstrap.TabGroup.register(this);
17824     
17825 };
17826
17827 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17828     
17829     carousel : false,
17830     transition : false,
17831     bullets : 0,
17832     timer : 0,
17833     autoslide : false,
17834     slideFn : false,
17835     slideOnTouch : false,
17836     showarrow : true,
17837     
17838     getAutoCreate : function()
17839     {
17840         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17841         
17842         cfg.cls += ' tab-content';
17843         
17844         if (this.carousel) {
17845             cfg.cls += ' carousel slide';
17846             
17847             cfg.cn = [{
17848                cls : 'carousel-inner',
17849                cn : []
17850             }];
17851         
17852             if(this.bullets  && !Roo.isTouch){
17853                 
17854                 var bullets = {
17855                     cls : 'carousel-bullets',
17856                     cn : []
17857                 };
17858                
17859                 if(this.bullets_cls){
17860                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17861                 }
17862                 
17863                 bullets.cn.push({
17864                     cls : 'clear'
17865                 });
17866                 
17867                 cfg.cn[0].cn.push(bullets);
17868             }
17869             
17870             if(this.showarrow){
17871                 cfg.cn[0].cn.push({
17872                     tag : 'div',
17873                     class : 'carousel-arrow',
17874                     cn : [
17875                         {
17876                             tag : 'div',
17877                             class : 'carousel-prev',
17878                             cn : [
17879                                 {
17880                                     tag : 'i',
17881                                     class : 'fa fa-chevron-left'
17882                                 }
17883                             ]
17884                         },
17885                         {
17886                             tag : 'div',
17887                             class : 'carousel-next',
17888                             cn : [
17889                                 {
17890                                     tag : 'i',
17891                                     class : 'fa fa-chevron-right'
17892                                 }
17893                             ]
17894                         }
17895                     ]
17896                 });
17897             }
17898             
17899         }
17900         
17901         return cfg;
17902     },
17903     
17904     initEvents:  function()
17905     {
17906 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17907 //            this.el.on("touchstart", this.onTouchStart, this);
17908 //        }
17909         
17910         if(this.autoslide){
17911             var _this = this;
17912             
17913             this.slideFn = window.setInterval(function() {
17914                 _this.showPanelNext();
17915             }, this.timer);
17916         }
17917         
17918         if(this.showarrow){
17919             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17920             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17921         }
17922         
17923         
17924     },
17925     
17926 //    onTouchStart : function(e, el, o)
17927 //    {
17928 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17929 //            return;
17930 //        }
17931 //        
17932 //        this.showPanelNext();
17933 //    },
17934     
17935     
17936     getChildContainer : function()
17937     {
17938         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17939     },
17940     
17941     /**
17942     * register a Navigation item
17943     * @param {Roo.bootstrap.NavItem} the navitem to add
17944     */
17945     register : function(item)
17946     {
17947         this.tabs.push( item);
17948         item.navId = this.navId; // not really needed..
17949         this.addBullet();
17950     
17951     },
17952     
17953     getActivePanel : function()
17954     {
17955         var r = false;
17956         Roo.each(this.tabs, function(t) {
17957             if (t.active) {
17958                 r = t;
17959                 return false;
17960             }
17961             return null;
17962         });
17963         return r;
17964         
17965     },
17966     getPanelByName : function(n)
17967     {
17968         var r = false;
17969         Roo.each(this.tabs, function(t) {
17970             if (t.tabId == n) {
17971                 r = t;
17972                 return false;
17973             }
17974             return null;
17975         });
17976         return r;
17977     },
17978     indexOfPanel : function(p)
17979     {
17980         var r = false;
17981         Roo.each(this.tabs, function(t,i) {
17982             if (t.tabId == p.tabId) {
17983                 r = i;
17984                 return false;
17985             }
17986             return null;
17987         });
17988         return r;
17989     },
17990     /**
17991      * show a specific panel
17992      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17993      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17994      */
17995     showPanel : function (pan)
17996     {
17997         if(this.transition || typeof(pan) == 'undefined'){
17998             Roo.log("waiting for the transitionend");
17999             return;
18000         }
18001         
18002         if (typeof(pan) == 'number') {
18003             pan = this.tabs[pan];
18004         }
18005         
18006         if (typeof(pan) == 'string') {
18007             pan = this.getPanelByName(pan);
18008         }
18009         
18010         var cur = this.getActivePanel();
18011         
18012         if(!pan || !cur){
18013             Roo.log('pan or acitve pan is undefined');
18014             return false;
18015         }
18016         
18017         if (pan.tabId == this.getActivePanel().tabId) {
18018             return true;
18019         }
18020         
18021         if (false === cur.fireEvent('beforedeactivate')) {
18022             return false;
18023         }
18024         
18025         if(this.bullets > 0 && !Roo.isTouch){
18026             this.setActiveBullet(this.indexOfPanel(pan));
18027         }
18028         
18029         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18030             
18031             this.transition = true;
18032             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18033             var lr = dir == 'next' ? 'left' : 'right';
18034             pan.el.addClass(dir); // or prev
18035             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18036             cur.el.addClass(lr); // or right
18037             pan.el.addClass(lr);
18038             
18039             var _this = this;
18040             cur.el.on('transitionend', function() {
18041                 Roo.log("trans end?");
18042                 
18043                 pan.el.removeClass([lr,dir]);
18044                 pan.setActive(true);
18045                 
18046                 cur.el.removeClass([lr]);
18047                 cur.setActive(false);
18048                 
18049                 _this.transition = false;
18050                 
18051             }, this, { single:  true } );
18052             
18053             return true;
18054         }
18055         
18056         cur.setActive(false);
18057         pan.setActive(true);
18058         
18059         return true;
18060         
18061     },
18062     showPanelNext : function()
18063     {
18064         var i = this.indexOfPanel(this.getActivePanel());
18065         
18066         if (i >= this.tabs.length - 1 && !this.autoslide) {
18067             return;
18068         }
18069         
18070         if (i >= this.tabs.length - 1 && this.autoslide) {
18071             i = -1;
18072         }
18073         
18074         this.showPanel(this.tabs[i+1]);
18075     },
18076     
18077     showPanelPrev : function()
18078     {
18079         var i = this.indexOfPanel(this.getActivePanel());
18080         
18081         if (i  < 1 && !this.autoslide) {
18082             return;
18083         }
18084         
18085         if (i < 1 && this.autoslide) {
18086             i = this.tabs.length;
18087         }
18088         
18089         this.showPanel(this.tabs[i-1]);
18090     },
18091     
18092     
18093     addBullet: function()
18094     {
18095         if(!this.bullets || Roo.isTouch){
18096             return;
18097         }
18098         var ctr = this.el.select('.carousel-bullets',true).first();
18099         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18100         var bullet = ctr.createChild({
18101             cls : 'bullet bullet-' + i
18102         },ctr.dom.lastChild);
18103         
18104         
18105         var _this = this;
18106         
18107         bullet.on('click', (function(e, el, o, ii, t){
18108
18109             e.preventDefault();
18110
18111             this.showPanel(ii);
18112
18113             if(this.autoslide && this.slideFn){
18114                 clearInterval(this.slideFn);
18115                 this.slideFn = window.setInterval(function() {
18116                     _this.showPanelNext();
18117                 }, this.timer);
18118             }
18119
18120         }).createDelegate(this, [i, bullet], true));
18121                 
18122         
18123     },
18124      
18125     setActiveBullet : function(i)
18126     {
18127         if(Roo.isTouch){
18128             return;
18129         }
18130         
18131         Roo.each(this.el.select('.bullet', true).elements, function(el){
18132             el.removeClass('selected');
18133         });
18134
18135         var bullet = this.el.select('.bullet-' + i, true).first();
18136         
18137         if(!bullet){
18138             return;
18139         }
18140         
18141         bullet.addClass('selected');
18142     }
18143     
18144     
18145   
18146 });
18147
18148  
18149
18150  
18151  
18152 Roo.apply(Roo.bootstrap.TabGroup, {
18153     
18154     groups: {},
18155      /**
18156     * register a Navigation Group
18157     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18158     */
18159     register : function(navgrp)
18160     {
18161         this.groups[navgrp.navId] = navgrp;
18162         
18163     },
18164     /**
18165     * fetch a Navigation Group based on the navigation ID
18166     * if one does not exist , it will get created.
18167     * @param {string} the navgroup to add
18168     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18169     */
18170     get: function(navId) {
18171         if (typeof(this.groups[navId]) == 'undefined') {
18172             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18173         }
18174         return this.groups[navId] ;
18175     }
18176     
18177     
18178     
18179 });
18180
18181  /*
18182  * - LGPL
18183  *
18184  * TabPanel
18185  * 
18186  */
18187
18188 /**
18189  * @class Roo.bootstrap.TabPanel
18190  * @extends Roo.bootstrap.Component
18191  * Bootstrap TabPanel class
18192  * @cfg {Boolean} active panel active
18193  * @cfg {String} html panel content
18194  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18195  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18196  * @cfg {String} href click to link..
18197  * 
18198  * 
18199  * @constructor
18200  * Create a new TabPanel
18201  * @param {Object} config The config object
18202  */
18203
18204 Roo.bootstrap.TabPanel = function(config){
18205     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18206     this.addEvents({
18207         /**
18208              * @event changed
18209              * Fires when the active status changes
18210              * @param {Roo.bootstrap.TabPanel} this
18211              * @param {Boolean} state the new state
18212             
18213          */
18214         'changed': true,
18215         /**
18216              * @event beforedeactivate
18217              * Fires before a tab is de-activated - can be used to do validation on a form.
18218              * @param {Roo.bootstrap.TabPanel} this
18219              * @return {Boolean} false if there is an error
18220             
18221          */
18222         'beforedeactivate': true
18223      });
18224     
18225     this.tabId = this.tabId || Roo.id();
18226   
18227 };
18228
18229 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18230     
18231     active: false,
18232     html: false,
18233     tabId: false,
18234     navId : false,
18235     href : '',
18236     
18237     getAutoCreate : function(){
18238         var cfg = {
18239             tag: 'div',
18240             // item is needed for carousel - not sure if it has any effect otherwise
18241             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18242             html: this.html || ''
18243         };
18244         
18245         if(this.active){
18246             cfg.cls += ' active';
18247         }
18248         
18249         if(this.tabId){
18250             cfg.tabId = this.tabId;
18251         }
18252         
18253         
18254         return cfg;
18255     },
18256     
18257     initEvents:  function()
18258     {
18259         var p = this.parent();
18260         
18261         this.navId = this.navId || p.navId;
18262         
18263         if (typeof(this.navId) != 'undefined') {
18264             // not really needed.. but just in case.. parent should be a NavGroup.
18265             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18266             
18267             tg.register(this);
18268             
18269             var i = tg.tabs.length - 1;
18270             
18271             if(this.active && tg.bullets > 0 && i < tg.bullets){
18272                 tg.setActiveBullet(i);
18273             }
18274         }
18275         
18276         this.el.on('click', this.onClick, this);
18277         
18278         if(Roo.isTouch){
18279             this.el.on("touchstart", this.onTouchStart, this);
18280             this.el.on("touchmove", this.onTouchMove, this);
18281             this.el.on("touchend", this.onTouchEnd, this);
18282         }
18283         
18284     },
18285     
18286     onRender : function(ct, position)
18287     {
18288         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18289     },
18290     
18291     setActive : function(state)
18292     {
18293         Roo.log("panel - set active " + this.tabId + "=" + state);
18294         
18295         this.active = state;
18296         if (!state) {
18297             this.el.removeClass('active');
18298             
18299         } else  if (!this.el.hasClass('active')) {
18300             this.el.addClass('active');
18301         }
18302         
18303         this.fireEvent('changed', this, state);
18304     },
18305     
18306     onClick : function(e)
18307     {
18308         e.preventDefault();
18309         
18310         if(!this.href.length){
18311             return;
18312         }
18313         
18314         window.location.href = this.href;
18315     },
18316     
18317     startX : 0,
18318     startY : 0,
18319     endX : 0,
18320     endY : 0,
18321     swiping : false,
18322     
18323     onTouchStart : function(e)
18324     {
18325         this.swiping = false;
18326         
18327         this.startX = e.browserEvent.touches[0].clientX;
18328         this.startY = e.browserEvent.touches[0].clientY;
18329     },
18330     
18331     onTouchMove : function(e)
18332     {
18333         this.swiping = true;
18334         
18335         this.endX = e.browserEvent.touches[0].clientX;
18336         this.endY = e.browserEvent.touches[0].clientY;
18337     },
18338     
18339     onTouchEnd : function(e)
18340     {
18341         if(!this.swiping){
18342             this.onClick(e);
18343             return;
18344         }
18345         
18346         var tabGroup = this.parent();
18347         
18348         if(this.endX > this.startX){ // swiping right
18349             tabGroup.showPanelPrev();
18350             return;
18351         }
18352         
18353         if(this.startX > this.endX){ // swiping left
18354             tabGroup.showPanelNext();
18355             return;
18356         }
18357     }
18358     
18359     
18360 });
18361  
18362
18363  
18364
18365  /*
18366  * - LGPL
18367  *
18368  * DateField
18369  * 
18370  */
18371
18372 /**
18373  * @class Roo.bootstrap.DateField
18374  * @extends Roo.bootstrap.Input
18375  * Bootstrap DateField class
18376  * @cfg {Number} weekStart default 0
18377  * @cfg {String} viewMode default empty, (months|years)
18378  * @cfg {String} minViewMode default empty, (months|years)
18379  * @cfg {Number} startDate default -Infinity
18380  * @cfg {Number} endDate default Infinity
18381  * @cfg {Boolean} todayHighlight default false
18382  * @cfg {Boolean} todayBtn default false
18383  * @cfg {Boolean} calendarWeeks default false
18384  * @cfg {Object} daysOfWeekDisabled default empty
18385  * @cfg {Boolean} singleMode default false (true | false)
18386  * 
18387  * @cfg {Boolean} keyboardNavigation default true
18388  * @cfg {String} language default en
18389  * 
18390  * @constructor
18391  * Create a new DateField
18392  * @param {Object} config The config object
18393  */
18394
18395 Roo.bootstrap.DateField = function(config){
18396     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18397      this.addEvents({
18398             /**
18399              * @event show
18400              * Fires when this field show.
18401              * @param {Roo.bootstrap.DateField} this
18402              * @param {Mixed} date The date value
18403              */
18404             show : true,
18405             /**
18406              * @event show
18407              * Fires when this field hide.
18408              * @param {Roo.bootstrap.DateField} this
18409              * @param {Mixed} date The date value
18410              */
18411             hide : true,
18412             /**
18413              * @event select
18414              * Fires when select a date.
18415              * @param {Roo.bootstrap.DateField} this
18416              * @param {Mixed} date The date value
18417              */
18418             select : true,
18419             /**
18420              * @event beforeselect
18421              * Fires when before select a date.
18422              * @param {Roo.bootstrap.DateField} this
18423              * @param {Mixed} date The date value
18424              */
18425             beforeselect : true
18426         });
18427 };
18428
18429 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18430     
18431     /**
18432      * @cfg {String} format
18433      * The default date format string which can be overriden for localization support.  The format must be
18434      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18435      */
18436     format : "m/d/y",
18437     /**
18438      * @cfg {String} altFormats
18439      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18440      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18441      */
18442     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18443     
18444     weekStart : 0,
18445     
18446     viewMode : '',
18447     
18448     minViewMode : '',
18449     
18450     todayHighlight : false,
18451     
18452     todayBtn: false,
18453     
18454     language: 'en',
18455     
18456     keyboardNavigation: true,
18457     
18458     calendarWeeks: false,
18459     
18460     startDate: -Infinity,
18461     
18462     endDate: Infinity,
18463     
18464     daysOfWeekDisabled: [],
18465     
18466     _events: [],
18467     
18468     singleMode : false,
18469     
18470     UTCDate: function()
18471     {
18472         return new Date(Date.UTC.apply(Date, arguments));
18473     },
18474     
18475     UTCToday: function()
18476     {
18477         var today = new Date();
18478         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18479     },
18480     
18481     getDate: function() {
18482             var d = this.getUTCDate();
18483             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18484     },
18485     
18486     getUTCDate: function() {
18487             return this.date;
18488     },
18489     
18490     setDate: function(d) {
18491             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18492     },
18493     
18494     setUTCDate: function(d) {
18495             this.date = d;
18496             this.setValue(this.formatDate(this.date));
18497     },
18498         
18499     onRender: function(ct, position)
18500     {
18501         
18502         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18503         
18504         this.language = this.language || 'en';
18505         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18506         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18507         
18508         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18509         this.format = this.format || 'm/d/y';
18510         this.isInline = false;
18511         this.isInput = true;
18512         this.component = this.el.select('.add-on', true).first() || false;
18513         this.component = (this.component && this.component.length === 0) ? false : this.component;
18514         this.hasInput = this.component && this.inputEl().length;
18515         
18516         if (typeof(this.minViewMode === 'string')) {
18517             switch (this.minViewMode) {
18518                 case 'months':
18519                     this.minViewMode = 1;
18520                     break;
18521                 case 'years':
18522                     this.minViewMode = 2;
18523                     break;
18524                 default:
18525                     this.minViewMode = 0;
18526                     break;
18527             }
18528         }
18529         
18530         if (typeof(this.viewMode === 'string')) {
18531             switch (this.viewMode) {
18532                 case 'months':
18533                     this.viewMode = 1;
18534                     break;
18535                 case 'years':
18536                     this.viewMode = 2;
18537                     break;
18538                 default:
18539                     this.viewMode = 0;
18540                     break;
18541             }
18542         }
18543                 
18544         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18545         
18546 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18547         
18548         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18549         
18550         this.picker().on('mousedown', this.onMousedown, this);
18551         this.picker().on('click', this.onClick, this);
18552         
18553         this.picker().addClass('datepicker-dropdown');
18554         
18555         this.startViewMode = this.viewMode;
18556         
18557         if(this.singleMode){
18558             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18559                 v.setVisibilityMode(Roo.Element.DISPLAY);
18560                 v.hide();
18561             });
18562             
18563             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18564                 v.setStyle('width', '189px');
18565             });
18566         }
18567         
18568         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18569             if(!this.calendarWeeks){
18570                 v.remove();
18571                 return;
18572             }
18573             
18574             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18575             v.attr('colspan', function(i, val){
18576                 return parseInt(val) + 1;
18577             });
18578         });
18579                         
18580         
18581         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18582         
18583         this.setStartDate(this.startDate);
18584         this.setEndDate(this.endDate);
18585         
18586         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18587         
18588         this.fillDow();
18589         this.fillMonths();
18590         this.update();
18591         this.showMode();
18592         
18593         if(this.isInline) {
18594             this.showPopup();
18595         }
18596     },
18597     
18598     picker : function()
18599     {
18600         return this.pickerEl;
18601 //        return this.el.select('.datepicker', true).first();
18602     },
18603     
18604     fillDow: function()
18605     {
18606         var dowCnt = this.weekStart;
18607         
18608         var dow = {
18609             tag: 'tr',
18610             cn: [
18611                 
18612             ]
18613         };
18614         
18615         if(this.calendarWeeks){
18616             dow.cn.push({
18617                 tag: 'th',
18618                 cls: 'cw',
18619                 html: '&nbsp;'
18620             })
18621         }
18622         
18623         while (dowCnt < this.weekStart + 7) {
18624             dow.cn.push({
18625                 tag: 'th',
18626                 cls: 'dow',
18627                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18628             });
18629         }
18630         
18631         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18632     },
18633     
18634     fillMonths: function()
18635     {    
18636         var i = 0;
18637         var months = this.picker().select('>.datepicker-months td', true).first();
18638         
18639         months.dom.innerHTML = '';
18640         
18641         while (i < 12) {
18642             var month = {
18643                 tag: 'span',
18644                 cls: 'month',
18645                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18646             };
18647             
18648             months.createChild(month);
18649         }
18650         
18651     },
18652     
18653     update: function()
18654     {
18655         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;
18656         
18657         if (this.date < this.startDate) {
18658             this.viewDate = new Date(this.startDate);
18659         } else if (this.date > this.endDate) {
18660             this.viewDate = new Date(this.endDate);
18661         } else {
18662             this.viewDate = new Date(this.date);
18663         }
18664         
18665         this.fill();
18666     },
18667     
18668     fill: function() 
18669     {
18670         var d = new Date(this.viewDate),
18671                 year = d.getUTCFullYear(),
18672                 month = d.getUTCMonth(),
18673                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18674                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18675                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18676                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18677                 currentDate = this.date && this.date.valueOf(),
18678                 today = this.UTCToday();
18679         
18680         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18681         
18682 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18683         
18684 //        this.picker.select('>tfoot th.today').
18685 //                                              .text(dates[this.language].today)
18686 //                                              .toggle(this.todayBtn !== false);
18687     
18688         this.updateNavArrows();
18689         this.fillMonths();
18690                                                 
18691         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18692         
18693         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18694          
18695         prevMonth.setUTCDate(day);
18696         
18697         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18698         
18699         var nextMonth = new Date(prevMonth);
18700         
18701         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18702         
18703         nextMonth = nextMonth.valueOf();
18704         
18705         var fillMonths = false;
18706         
18707         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18708         
18709         while(prevMonth.valueOf() <= nextMonth) {
18710             var clsName = '';
18711             
18712             if (prevMonth.getUTCDay() === this.weekStart) {
18713                 if(fillMonths){
18714                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18715                 }
18716                     
18717                 fillMonths = {
18718                     tag: 'tr',
18719                     cn: []
18720                 };
18721                 
18722                 if(this.calendarWeeks){
18723                     // ISO 8601: First week contains first thursday.
18724                     // ISO also states week starts on Monday, but we can be more abstract here.
18725                     var
18726                     // Start of current week: based on weekstart/current date
18727                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18728                     // Thursday of this week
18729                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18730                     // First Thursday of year, year from thursday
18731                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18732                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18733                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18734                     
18735                     fillMonths.cn.push({
18736                         tag: 'td',
18737                         cls: 'cw',
18738                         html: calWeek
18739                     });
18740                 }
18741             }
18742             
18743             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18744                 clsName += ' old';
18745             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18746                 clsName += ' new';
18747             }
18748             if (this.todayHighlight &&
18749                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18750                 prevMonth.getUTCMonth() == today.getMonth() &&
18751                 prevMonth.getUTCDate() == today.getDate()) {
18752                 clsName += ' today';
18753             }
18754             
18755             if (currentDate && prevMonth.valueOf() === currentDate) {
18756                 clsName += ' active';
18757             }
18758             
18759             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18760                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18761                     clsName += ' disabled';
18762             }
18763             
18764             fillMonths.cn.push({
18765                 tag: 'td',
18766                 cls: 'day ' + clsName,
18767                 html: prevMonth.getDate()
18768             });
18769             
18770             prevMonth.setDate(prevMonth.getDate()+1);
18771         }
18772           
18773         var currentYear = this.date && this.date.getUTCFullYear();
18774         var currentMonth = this.date && this.date.getUTCMonth();
18775         
18776         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18777         
18778         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18779             v.removeClass('active');
18780             
18781             if(currentYear === year && k === currentMonth){
18782                 v.addClass('active');
18783             }
18784             
18785             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18786                 v.addClass('disabled');
18787             }
18788             
18789         });
18790         
18791         
18792         year = parseInt(year/10, 10) * 10;
18793         
18794         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18795         
18796         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18797         
18798         year -= 1;
18799         for (var i = -1; i < 11; i++) {
18800             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18801                 tag: 'span',
18802                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18803                 html: year
18804             });
18805             
18806             year += 1;
18807         }
18808     },
18809     
18810     showMode: function(dir) 
18811     {
18812         if (dir) {
18813             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18814         }
18815         
18816         Roo.each(this.picker().select('>div',true).elements, function(v){
18817             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18818             v.hide();
18819         });
18820         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18821     },
18822     
18823     place: function()
18824     {
18825         if(this.isInline) {
18826             return;
18827         }
18828         
18829         this.picker().removeClass(['bottom', 'top']);
18830         
18831         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18832             /*
18833              * place to the top of element!
18834              *
18835              */
18836             
18837             this.picker().addClass('top');
18838             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18839             
18840             return;
18841         }
18842         
18843         this.picker().addClass('bottom');
18844         
18845         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18846     },
18847     
18848     parseDate : function(value)
18849     {
18850         if(!value || value instanceof Date){
18851             return value;
18852         }
18853         var v = Date.parseDate(value, this.format);
18854         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18855             v = Date.parseDate(value, 'Y-m-d');
18856         }
18857         if(!v && this.altFormats){
18858             if(!this.altFormatsArray){
18859                 this.altFormatsArray = this.altFormats.split("|");
18860             }
18861             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18862                 v = Date.parseDate(value, this.altFormatsArray[i]);
18863             }
18864         }
18865         return v;
18866     },
18867     
18868     formatDate : function(date, fmt)
18869     {   
18870         return (!date || !(date instanceof Date)) ?
18871         date : date.dateFormat(fmt || this.format);
18872     },
18873     
18874     onFocus : function()
18875     {
18876         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18877         this.showPopup();
18878     },
18879     
18880     onBlur : function()
18881     {
18882         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18883         
18884         var d = this.inputEl().getValue();
18885         
18886         this.setValue(d);
18887                 
18888         this.hidePopup();
18889     },
18890     
18891     showPopup : function()
18892     {
18893         this.picker().show();
18894         this.update();
18895         this.place();
18896         
18897         this.fireEvent('showpopup', this, this.date);
18898     },
18899     
18900     hidePopup : function()
18901     {
18902         if(this.isInline) {
18903             return;
18904         }
18905         this.picker().hide();
18906         this.viewMode = this.startViewMode;
18907         this.showMode();
18908         
18909         this.fireEvent('hidepopup', this, this.date);
18910         
18911     },
18912     
18913     onMousedown: function(e)
18914     {
18915         e.stopPropagation();
18916         e.preventDefault();
18917     },
18918     
18919     keyup: function(e)
18920     {
18921         Roo.bootstrap.DateField.superclass.keyup.call(this);
18922         this.update();
18923     },
18924
18925     setValue: function(v)
18926     {
18927         if(this.fireEvent('beforeselect', this, v) !== false){
18928             var d = new Date(this.parseDate(v) ).clearTime();
18929         
18930             if(isNaN(d.getTime())){
18931                 this.date = this.viewDate = '';
18932                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18933                 return;
18934             }
18935
18936             v = this.formatDate(d);
18937
18938             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18939
18940             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18941
18942             this.update();
18943
18944             this.fireEvent('select', this, this.date);
18945         }
18946     },
18947     
18948     getValue: function()
18949     {
18950         return this.formatDate(this.date);
18951     },
18952     
18953     fireKey: function(e)
18954     {
18955         if (!this.picker().isVisible()){
18956             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18957                 this.showPopup();
18958             }
18959             return;
18960         }
18961         
18962         var dateChanged = false,
18963         dir, day, month,
18964         newDate, newViewDate;
18965         
18966         switch(e.keyCode){
18967             case 27: // escape
18968                 this.hidePopup();
18969                 e.preventDefault();
18970                 break;
18971             case 37: // left
18972             case 39: // right
18973                 if (!this.keyboardNavigation) {
18974                     break;
18975                 }
18976                 dir = e.keyCode == 37 ? -1 : 1;
18977                 
18978                 if (e.ctrlKey){
18979                     newDate = this.moveYear(this.date, dir);
18980                     newViewDate = this.moveYear(this.viewDate, dir);
18981                 } else if (e.shiftKey){
18982                     newDate = this.moveMonth(this.date, dir);
18983                     newViewDate = this.moveMonth(this.viewDate, dir);
18984                 } else {
18985                     newDate = new Date(this.date);
18986                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18987                     newViewDate = new Date(this.viewDate);
18988                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18989                 }
18990                 if (this.dateWithinRange(newDate)){
18991                     this.date = newDate;
18992                     this.viewDate = newViewDate;
18993                     this.setValue(this.formatDate(this.date));
18994 //                    this.update();
18995                     e.preventDefault();
18996                     dateChanged = true;
18997                 }
18998                 break;
18999             case 38: // up
19000             case 40: // down
19001                 if (!this.keyboardNavigation) {
19002                     break;
19003                 }
19004                 dir = e.keyCode == 38 ? -1 : 1;
19005                 if (e.ctrlKey){
19006                     newDate = this.moveYear(this.date, dir);
19007                     newViewDate = this.moveYear(this.viewDate, dir);
19008                 } else if (e.shiftKey){
19009                     newDate = this.moveMonth(this.date, dir);
19010                     newViewDate = this.moveMonth(this.viewDate, dir);
19011                 } else {
19012                     newDate = new Date(this.date);
19013                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19014                     newViewDate = new Date(this.viewDate);
19015                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19016                 }
19017                 if (this.dateWithinRange(newDate)){
19018                     this.date = newDate;
19019                     this.viewDate = newViewDate;
19020                     this.setValue(this.formatDate(this.date));
19021 //                    this.update();
19022                     e.preventDefault();
19023                     dateChanged = true;
19024                 }
19025                 break;
19026             case 13: // enter
19027                 this.setValue(this.formatDate(this.date));
19028                 this.hidePopup();
19029                 e.preventDefault();
19030                 break;
19031             case 9: // tab
19032                 this.setValue(this.formatDate(this.date));
19033                 this.hidePopup();
19034                 break;
19035             case 16: // shift
19036             case 17: // ctrl
19037             case 18: // alt
19038                 break;
19039             default :
19040                 this.hide();
19041                 
19042         }
19043     },
19044     
19045     
19046     onClick: function(e) 
19047     {
19048         e.stopPropagation();
19049         e.preventDefault();
19050         
19051         var target = e.getTarget();
19052         
19053         if(target.nodeName.toLowerCase() === 'i'){
19054             target = Roo.get(target).dom.parentNode;
19055         }
19056         
19057         var nodeName = target.nodeName;
19058         var className = target.className;
19059         var html = target.innerHTML;
19060         //Roo.log(nodeName);
19061         
19062         switch(nodeName.toLowerCase()) {
19063             case 'th':
19064                 switch(className) {
19065                     case 'switch':
19066                         this.showMode(1);
19067                         break;
19068                     case 'prev':
19069                     case 'next':
19070                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19071                         switch(this.viewMode){
19072                                 case 0:
19073                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19074                                         break;
19075                                 case 1:
19076                                 case 2:
19077                                         this.viewDate = this.moveYear(this.viewDate, dir);
19078                                         break;
19079                         }
19080                         this.fill();
19081                         break;
19082                     case 'today':
19083                         var date = new Date();
19084                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19085 //                        this.fill()
19086                         this.setValue(this.formatDate(this.date));
19087                         
19088                         this.hidePopup();
19089                         break;
19090                 }
19091                 break;
19092             case 'span':
19093                 if (className.indexOf('disabled') < 0) {
19094                     this.viewDate.setUTCDate(1);
19095                     if (className.indexOf('month') > -1) {
19096                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19097                     } else {
19098                         var year = parseInt(html, 10) || 0;
19099                         this.viewDate.setUTCFullYear(year);
19100                         
19101                     }
19102                     
19103                     if(this.singleMode){
19104                         this.setValue(this.formatDate(this.viewDate));
19105                         this.hidePopup();
19106                         return;
19107                     }
19108                     
19109                     this.showMode(-1);
19110                     this.fill();
19111                 }
19112                 break;
19113                 
19114             case 'td':
19115                 //Roo.log(className);
19116                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19117                     var day = parseInt(html, 10) || 1;
19118                     var year = this.viewDate.getUTCFullYear(),
19119                         month = this.viewDate.getUTCMonth();
19120
19121                     if (className.indexOf('old') > -1) {
19122                         if(month === 0 ){
19123                             month = 11;
19124                             year -= 1;
19125                         }else{
19126                             month -= 1;
19127                         }
19128                     } else if (className.indexOf('new') > -1) {
19129                         if (month == 11) {
19130                             month = 0;
19131                             year += 1;
19132                         } else {
19133                             month += 1;
19134                         }
19135                     }
19136                     //Roo.log([year,month,day]);
19137                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19138                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19139 //                    this.fill();
19140                     //Roo.log(this.formatDate(this.date));
19141                     this.setValue(this.formatDate(this.date));
19142                     this.hidePopup();
19143                 }
19144                 break;
19145         }
19146     },
19147     
19148     setStartDate: function(startDate)
19149     {
19150         this.startDate = startDate || -Infinity;
19151         if (this.startDate !== -Infinity) {
19152             this.startDate = this.parseDate(this.startDate);
19153         }
19154         this.update();
19155         this.updateNavArrows();
19156     },
19157
19158     setEndDate: function(endDate)
19159     {
19160         this.endDate = endDate || Infinity;
19161         if (this.endDate !== Infinity) {
19162             this.endDate = this.parseDate(this.endDate);
19163         }
19164         this.update();
19165         this.updateNavArrows();
19166     },
19167     
19168     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19169     {
19170         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19171         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19172             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19173         }
19174         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19175             return parseInt(d, 10);
19176         });
19177         this.update();
19178         this.updateNavArrows();
19179     },
19180     
19181     updateNavArrows: function() 
19182     {
19183         if(this.singleMode){
19184             return;
19185         }
19186         
19187         var d = new Date(this.viewDate),
19188         year = d.getUTCFullYear(),
19189         month = d.getUTCMonth();
19190         
19191         Roo.each(this.picker().select('.prev', true).elements, function(v){
19192             v.show();
19193             switch (this.viewMode) {
19194                 case 0:
19195
19196                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19197                         v.hide();
19198                     }
19199                     break;
19200                 case 1:
19201                 case 2:
19202                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19203                         v.hide();
19204                     }
19205                     break;
19206             }
19207         });
19208         
19209         Roo.each(this.picker().select('.next', true).elements, function(v){
19210             v.show();
19211             switch (this.viewMode) {
19212                 case 0:
19213
19214                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19215                         v.hide();
19216                     }
19217                     break;
19218                 case 1:
19219                 case 2:
19220                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19221                         v.hide();
19222                     }
19223                     break;
19224             }
19225         })
19226     },
19227     
19228     moveMonth: function(date, dir)
19229     {
19230         if (!dir) {
19231             return date;
19232         }
19233         var new_date = new Date(date.valueOf()),
19234         day = new_date.getUTCDate(),
19235         month = new_date.getUTCMonth(),
19236         mag = Math.abs(dir),
19237         new_month, test;
19238         dir = dir > 0 ? 1 : -1;
19239         if (mag == 1){
19240             test = dir == -1
19241             // If going back one month, make sure month is not current month
19242             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19243             ? function(){
19244                 return new_date.getUTCMonth() == month;
19245             }
19246             // If going forward one month, make sure month is as expected
19247             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19248             : function(){
19249                 return new_date.getUTCMonth() != new_month;
19250             };
19251             new_month = month + dir;
19252             new_date.setUTCMonth(new_month);
19253             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19254             if (new_month < 0 || new_month > 11) {
19255                 new_month = (new_month + 12) % 12;
19256             }
19257         } else {
19258             // For magnitudes >1, move one month at a time...
19259             for (var i=0; i<mag; i++) {
19260                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19261                 new_date = this.moveMonth(new_date, dir);
19262             }
19263             // ...then reset the day, keeping it in the new month
19264             new_month = new_date.getUTCMonth();
19265             new_date.setUTCDate(day);
19266             test = function(){
19267                 return new_month != new_date.getUTCMonth();
19268             };
19269         }
19270         // Common date-resetting loop -- if date is beyond end of month, make it
19271         // end of month
19272         while (test()){
19273             new_date.setUTCDate(--day);
19274             new_date.setUTCMonth(new_month);
19275         }
19276         return new_date;
19277     },
19278
19279     moveYear: function(date, dir)
19280     {
19281         return this.moveMonth(date, dir*12);
19282     },
19283
19284     dateWithinRange: function(date)
19285     {
19286         return date >= this.startDate && date <= this.endDate;
19287     },
19288
19289     
19290     remove: function() 
19291     {
19292         this.picker().remove();
19293     },
19294     
19295     validateValue : function(value)
19296     {
19297         if(this.getVisibilityEl().hasClass('hidden')){
19298             return true;
19299         }
19300         
19301         if(value.length < 1)  {
19302             if(this.allowBlank){
19303                 return true;
19304             }
19305             return false;
19306         }
19307         
19308         if(value.length < this.minLength){
19309             return false;
19310         }
19311         if(value.length > this.maxLength){
19312             return false;
19313         }
19314         if(this.vtype){
19315             var vt = Roo.form.VTypes;
19316             if(!vt[this.vtype](value, this)){
19317                 return false;
19318             }
19319         }
19320         if(typeof this.validator == "function"){
19321             var msg = this.validator(value);
19322             if(msg !== true){
19323                 return false;
19324             }
19325         }
19326         
19327         if(this.regex && !this.regex.test(value)){
19328             return false;
19329         }
19330         
19331         if(typeof(this.parseDate(value)) == 'undefined'){
19332             return false;
19333         }
19334         
19335         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19336             return false;
19337         }      
19338         
19339         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19340             return false;
19341         } 
19342         
19343         
19344         return true;
19345     },
19346     
19347     reset : function()
19348     {
19349         this.date = this.viewDate = '';
19350         
19351         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19352     }
19353    
19354 });
19355
19356 Roo.apply(Roo.bootstrap.DateField,  {
19357     
19358     head : {
19359         tag: 'thead',
19360         cn: [
19361         {
19362             tag: 'tr',
19363             cn: [
19364             {
19365                 tag: 'th',
19366                 cls: 'prev',
19367                 html: '<i class="fa fa-arrow-left"/>'
19368             },
19369             {
19370                 tag: 'th',
19371                 cls: 'switch',
19372                 colspan: '5'
19373             },
19374             {
19375                 tag: 'th',
19376                 cls: 'next',
19377                 html: '<i class="fa fa-arrow-right"/>'
19378             }
19379
19380             ]
19381         }
19382         ]
19383     },
19384     
19385     content : {
19386         tag: 'tbody',
19387         cn: [
19388         {
19389             tag: 'tr',
19390             cn: [
19391             {
19392                 tag: 'td',
19393                 colspan: '7'
19394             }
19395             ]
19396         }
19397         ]
19398     },
19399     
19400     footer : {
19401         tag: 'tfoot',
19402         cn: [
19403         {
19404             tag: 'tr',
19405             cn: [
19406             {
19407                 tag: 'th',
19408                 colspan: '7',
19409                 cls: 'today'
19410             }
19411                     
19412             ]
19413         }
19414         ]
19415     },
19416     
19417     dates:{
19418         en: {
19419             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19420             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19421             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19422             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19423             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19424             today: "Today"
19425         }
19426     },
19427     
19428     modes: [
19429     {
19430         clsName: 'days',
19431         navFnc: 'Month',
19432         navStep: 1
19433     },
19434     {
19435         clsName: 'months',
19436         navFnc: 'FullYear',
19437         navStep: 1
19438     },
19439     {
19440         clsName: 'years',
19441         navFnc: 'FullYear',
19442         navStep: 10
19443     }]
19444 });
19445
19446 Roo.apply(Roo.bootstrap.DateField,  {
19447   
19448     template : {
19449         tag: 'div',
19450         cls: 'datepicker dropdown-menu roo-dynamic',
19451         cn: [
19452         {
19453             tag: 'div',
19454             cls: 'datepicker-days',
19455             cn: [
19456             {
19457                 tag: 'table',
19458                 cls: 'table-condensed',
19459                 cn:[
19460                 Roo.bootstrap.DateField.head,
19461                 {
19462                     tag: 'tbody'
19463                 },
19464                 Roo.bootstrap.DateField.footer
19465                 ]
19466             }
19467             ]
19468         },
19469         {
19470             tag: 'div',
19471             cls: 'datepicker-months',
19472             cn: [
19473             {
19474                 tag: 'table',
19475                 cls: 'table-condensed',
19476                 cn:[
19477                 Roo.bootstrap.DateField.head,
19478                 Roo.bootstrap.DateField.content,
19479                 Roo.bootstrap.DateField.footer
19480                 ]
19481             }
19482             ]
19483         },
19484         {
19485             tag: 'div',
19486             cls: 'datepicker-years',
19487             cn: [
19488             {
19489                 tag: 'table',
19490                 cls: 'table-condensed',
19491                 cn:[
19492                 Roo.bootstrap.DateField.head,
19493                 Roo.bootstrap.DateField.content,
19494                 Roo.bootstrap.DateField.footer
19495                 ]
19496             }
19497             ]
19498         }
19499         ]
19500     }
19501 });
19502
19503  
19504
19505  /*
19506  * - LGPL
19507  *
19508  * TimeField
19509  * 
19510  */
19511
19512 /**
19513  * @class Roo.bootstrap.TimeField
19514  * @extends Roo.bootstrap.Input
19515  * Bootstrap DateField class
19516  * 
19517  * 
19518  * @constructor
19519  * Create a new TimeField
19520  * @param {Object} config The config object
19521  */
19522
19523 Roo.bootstrap.TimeField = function(config){
19524     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19525     this.addEvents({
19526             /**
19527              * @event show
19528              * Fires when this field show.
19529              * @param {Roo.bootstrap.DateField} thisthis
19530              * @param {Mixed} date The date value
19531              */
19532             show : true,
19533             /**
19534              * @event show
19535              * Fires when this field hide.
19536              * @param {Roo.bootstrap.DateField} this
19537              * @param {Mixed} date The date value
19538              */
19539             hide : true,
19540             /**
19541              * @event select
19542              * Fires when select a date.
19543              * @param {Roo.bootstrap.DateField} this
19544              * @param {Mixed} date The date value
19545              */
19546             select : true
19547         });
19548 };
19549
19550 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19551     
19552     /**
19553      * @cfg {String} format
19554      * The default time format string which can be overriden for localization support.  The format must be
19555      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19556      */
19557     format : "H:i",
19558        
19559     onRender: function(ct, position)
19560     {
19561         
19562         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19563                 
19564         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19565         
19566         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19567         
19568         this.pop = this.picker().select('>.datepicker-time',true).first();
19569         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19570         
19571         this.picker().on('mousedown', this.onMousedown, this);
19572         this.picker().on('click', this.onClick, this);
19573         
19574         this.picker().addClass('datepicker-dropdown');
19575     
19576         this.fillTime();
19577         this.update();
19578             
19579         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19580         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19581         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19582         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19583         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19584         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19585
19586     },
19587     
19588     fireKey: function(e){
19589         if (!this.picker().isVisible()){
19590             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19591                 this.show();
19592             }
19593             return;
19594         }
19595
19596         e.preventDefault();
19597         
19598         switch(e.keyCode){
19599             case 27: // escape
19600                 this.hide();
19601                 break;
19602             case 37: // left
19603             case 39: // right
19604                 this.onTogglePeriod();
19605                 break;
19606             case 38: // up
19607                 this.onIncrementMinutes();
19608                 break;
19609             case 40: // down
19610                 this.onDecrementMinutes();
19611                 break;
19612             case 13: // enter
19613             case 9: // tab
19614                 this.setTime();
19615                 break;
19616         }
19617     },
19618     
19619     onClick: function(e) {
19620         e.stopPropagation();
19621         e.preventDefault();
19622     },
19623     
19624     picker : function()
19625     {
19626         return this.el.select('.datepicker', true).first();
19627     },
19628     
19629     fillTime: function()
19630     {    
19631         var time = this.pop.select('tbody', true).first();
19632         
19633         time.dom.innerHTML = '';
19634         
19635         time.createChild({
19636             tag: 'tr',
19637             cn: [
19638                 {
19639                     tag: 'td',
19640                     cn: [
19641                         {
19642                             tag: 'a',
19643                             href: '#',
19644                             cls: 'btn',
19645                             cn: [
19646                                 {
19647                                     tag: 'span',
19648                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19649                                 }
19650                             ]
19651                         } 
19652                     ]
19653                 },
19654                 {
19655                     tag: 'td',
19656                     cls: 'separator'
19657                 },
19658                 {
19659                     tag: 'td',
19660                     cn: [
19661                         {
19662                             tag: 'a',
19663                             href: '#',
19664                             cls: 'btn',
19665                             cn: [
19666                                 {
19667                                     tag: 'span',
19668                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19669                                 }
19670                             ]
19671                         }
19672                     ]
19673                 },
19674                 {
19675                     tag: 'td',
19676                     cls: 'separator'
19677                 }
19678             ]
19679         });
19680         
19681         time.createChild({
19682             tag: 'tr',
19683             cn: [
19684                 {
19685                     tag: 'td',
19686                     cn: [
19687                         {
19688                             tag: 'span',
19689                             cls: 'timepicker-hour',
19690                             html: '00'
19691                         }  
19692                     ]
19693                 },
19694                 {
19695                     tag: 'td',
19696                     cls: 'separator',
19697                     html: ':'
19698                 },
19699                 {
19700                     tag: 'td',
19701                     cn: [
19702                         {
19703                             tag: 'span',
19704                             cls: 'timepicker-minute',
19705                             html: '00'
19706                         }  
19707                     ]
19708                 },
19709                 {
19710                     tag: 'td',
19711                     cls: 'separator'
19712                 },
19713                 {
19714                     tag: 'td',
19715                     cn: [
19716                         {
19717                             tag: 'button',
19718                             type: 'button',
19719                             cls: 'btn btn-primary period',
19720                             html: 'AM'
19721                             
19722                         }
19723                     ]
19724                 }
19725             ]
19726         });
19727         
19728         time.createChild({
19729             tag: 'tr',
19730             cn: [
19731                 {
19732                     tag: 'td',
19733                     cn: [
19734                         {
19735                             tag: 'a',
19736                             href: '#',
19737                             cls: 'btn',
19738                             cn: [
19739                                 {
19740                                     tag: 'span',
19741                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19742                                 }
19743                             ]
19744                         }
19745                     ]
19746                 },
19747                 {
19748                     tag: 'td',
19749                     cls: 'separator'
19750                 },
19751                 {
19752                     tag: 'td',
19753                     cn: [
19754                         {
19755                             tag: 'a',
19756                             href: '#',
19757                             cls: 'btn',
19758                             cn: [
19759                                 {
19760                                     tag: 'span',
19761                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19762                                 }
19763                             ]
19764                         }
19765                     ]
19766                 },
19767                 {
19768                     tag: 'td',
19769                     cls: 'separator'
19770                 }
19771             ]
19772         });
19773         
19774     },
19775     
19776     update: function()
19777     {
19778         
19779         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19780         
19781         this.fill();
19782     },
19783     
19784     fill: function() 
19785     {
19786         var hours = this.time.getHours();
19787         var minutes = this.time.getMinutes();
19788         var period = 'AM';
19789         
19790         if(hours > 11){
19791             period = 'PM';
19792         }
19793         
19794         if(hours == 0){
19795             hours = 12;
19796         }
19797         
19798         
19799         if(hours > 12){
19800             hours = hours - 12;
19801         }
19802         
19803         if(hours < 10){
19804             hours = '0' + hours;
19805         }
19806         
19807         if(minutes < 10){
19808             minutes = '0' + minutes;
19809         }
19810         
19811         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19812         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19813         this.pop.select('button', true).first().dom.innerHTML = period;
19814         
19815     },
19816     
19817     place: function()
19818     {   
19819         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19820         
19821         var cls = ['bottom'];
19822         
19823         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19824             cls.pop();
19825             cls.push('top');
19826         }
19827         
19828         cls.push('right');
19829         
19830         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19831             cls.pop();
19832             cls.push('left');
19833         }
19834         
19835         this.picker().addClass(cls.join('-'));
19836         
19837         var _this = this;
19838         
19839         Roo.each(cls, function(c){
19840             if(c == 'bottom'){
19841                 _this.picker().setTop(_this.inputEl().getHeight());
19842                 return;
19843             }
19844             if(c == 'top'){
19845                 _this.picker().setTop(0 - _this.picker().getHeight());
19846                 return;
19847             }
19848             
19849             if(c == 'left'){
19850                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19851                 return;
19852             }
19853             if(c == 'right'){
19854                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19855                 return;
19856             }
19857         });
19858         
19859     },
19860   
19861     onFocus : function()
19862     {
19863         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19864         this.show();
19865     },
19866     
19867     onBlur : function()
19868     {
19869         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19870         this.hide();
19871     },
19872     
19873     show : function()
19874     {
19875         this.picker().show();
19876         this.pop.show();
19877         this.update();
19878         this.place();
19879         
19880         this.fireEvent('show', this, this.date);
19881     },
19882     
19883     hide : function()
19884     {
19885         this.picker().hide();
19886         this.pop.hide();
19887         
19888         this.fireEvent('hide', this, this.date);
19889     },
19890     
19891     setTime : function()
19892     {
19893         this.hide();
19894         this.setValue(this.time.format(this.format));
19895         
19896         this.fireEvent('select', this, this.date);
19897         
19898         
19899     },
19900     
19901     onMousedown: function(e){
19902         e.stopPropagation();
19903         e.preventDefault();
19904     },
19905     
19906     onIncrementHours: function()
19907     {
19908         Roo.log('onIncrementHours');
19909         this.time = this.time.add(Date.HOUR, 1);
19910         this.update();
19911         
19912     },
19913     
19914     onDecrementHours: function()
19915     {
19916         Roo.log('onDecrementHours');
19917         this.time = this.time.add(Date.HOUR, -1);
19918         this.update();
19919     },
19920     
19921     onIncrementMinutes: function()
19922     {
19923         Roo.log('onIncrementMinutes');
19924         this.time = this.time.add(Date.MINUTE, 1);
19925         this.update();
19926     },
19927     
19928     onDecrementMinutes: function()
19929     {
19930         Roo.log('onDecrementMinutes');
19931         this.time = this.time.add(Date.MINUTE, -1);
19932         this.update();
19933     },
19934     
19935     onTogglePeriod: function()
19936     {
19937         Roo.log('onTogglePeriod');
19938         this.time = this.time.add(Date.HOUR, 12);
19939         this.update();
19940     }
19941     
19942    
19943 });
19944
19945 Roo.apply(Roo.bootstrap.TimeField,  {
19946     
19947     content : {
19948         tag: 'tbody',
19949         cn: [
19950             {
19951                 tag: 'tr',
19952                 cn: [
19953                 {
19954                     tag: 'td',
19955                     colspan: '7'
19956                 }
19957                 ]
19958             }
19959         ]
19960     },
19961     
19962     footer : {
19963         tag: 'tfoot',
19964         cn: [
19965             {
19966                 tag: 'tr',
19967                 cn: [
19968                 {
19969                     tag: 'th',
19970                     colspan: '7',
19971                     cls: '',
19972                     cn: [
19973                         {
19974                             tag: 'button',
19975                             cls: 'btn btn-info ok',
19976                             html: 'OK'
19977                         }
19978                     ]
19979                 }
19980
19981                 ]
19982             }
19983         ]
19984     }
19985 });
19986
19987 Roo.apply(Roo.bootstrap.TimeField,  {
19988   
19989     template : {
19990         tag: 'div',
19991         cls: 'datepicker dropdown-menu',
19992         cn: [
19993             {
19994                 tag: 'div',
19995                 cls: 'datepicker-time',
19996                 cn: [
19997                 {
19998                     tag: 'table',
19999                     cls: 'table-condensed',
20000                     cn:[
20001                     Roo.bootstrap.TimeField.content,
20002                     Roo.bootstrap.TimeField.footer
20003                     ]
20004                 }
20005                 ]
20006             }
20007         ]
20008     }
20009 });
20010
20011  
20012
20013  /*
20014  * - LGPL
20015  *
20016  * MonthField
20017  * 
20018  */
20019
20020 /**
20021  * @class Roo.bootstrap.MonthField
20022  * @extends Roo.bootstrap.Input
20023  * Bootstrap MonthField class
20024  * 
20025  * @cfg {String} language default en
20026  * 
20027  * @constructor
20028  * Create a new MonthField
20029  * @param {Object} config The config object
20030  */
20031
20032 Roo.bootstrap.MonthField = function(config){
20033     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20034     
20035     this.addEvents({
20036         /**
20037          * @event show
20038          * Fires when this field show.
20039          * @param {Roo.bootstrap.MonthField} this
20040          * @param {Mixed} date The date value
20041          */
20042         show : true,
20043         /**
20044          * @event show
20045          * Fires when this field hide.
20046          * @param {Roo.bootstrap.MonthField} this
20047          * @param {Mixed} date The date value
20048          */
20049         hide : true,
20050         /**
20051          * @event select
20052          * Fires when select a date.
20053          * @param {Roo.bootstrap.MonthField} this
20054          * @param {String} oldvalue The old value
20055          * @param {String} newvalue The new value
20056          */
20057         select : true
20058     });
20059 };
20060
20061 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20062     
20063     onRender: function(ct, position)
20064     {
20065         
20066         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20067         
20068         this.language = this.language || 'en';
20069         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20070         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20071         
20072         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20073         this.isInline = false;
20074         this.isInput = true;
20075         this.component = this.el.select('.add-on', true).first() || false;
20076         this.component = (this.component && this.component.length === 0) ? false : this.component;
20077         this.hasInput = this.component && this.inputEL().length;
20078         
20079         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20080         
20081         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20082         
20083         this.picker().on('mousedown', this.onMousedown, this);
20084         this.picker().on('click', this.onClick, this);
20085         
20086         this.picker().addClass('datepicker-dropdown');
20087         
20088         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20089             v.setStyle('width', '189px');
20090         });
20091         
20092         this.fillMonths();
20093         
20094         this.update();
20095         
20096         if(this.isInline) {
20097             this.show();
20098         }
20099         
20100     },
20101     
20102     setValue: function(v, suppressEvent)
20103     {   
20104         var o = this.getValue();
20105         
20106         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20107         
20108         this.update();
20109
20110         if(suppressEvent !== true){
20111             this.fireEvent('select', this, o, v);
20112         }
20113         
20114     },
20115     
20116     getValue: function()
20117     {
20118         return this.value;
20119     },
20120     
20121     onClick: function(e) 
20122     {
20123         e.stopPropagation();
20124         e.preventDefault();
20125         
20126         var target = e.getTarget();
20127         
20128         if(target.nodeName.toLowerCase() === 'i'){
20129             target = Roo.get(target).dom.parentNode;
20130         }
20131         
20132         var nodeName = target.nodeName;
20133         var className = target.className;
20134         var html = target.innerHTML;
20135         
20136         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20137             return;
20138         }
20139         
20140         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20141         
20142         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20143         
20144         this.hide();
20145                         
20146     },
20147     
20148     picker : function()
20149     {
20150         return this.pickerEl;
20151     },
20152     
20153     fillMonths: function()
20154     {    
20155         var i = 0;
20156         var months = this.picker().select('>.datepicker-months td', true).first();
20157         
20158         months.dom.innerHTML = '';
20159         
20160         while (i < 12) {
20161             var month = {
20162                 tag: 'span',
20163                 cls: 'month',
20164                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20165             };
20166             
20167             months.createChild(month);
20168         }
20169         
20170     },
20171     
20172     update: function()
20173     {
20174         var _this = this;
20175         
20176         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20177             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20178         }
20179         
20180         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20181             e.removeClass('active');
20182             
20183             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20184                 e.addClass('active');
20185             }
20186         })
20187     },
20188     
20189     place: function()
20190     {
20191         if(this.isInline) {
20192             return;
20193         }
20194         
20195         this.picker().removeClass(['bottom', 'top']);
20196         
20197         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20198             /*
20199              * place to the top of element!
20200              *
20201              */
20202             
20203             this.picker().addClass('top');
20204             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20205             
20206             return;
20207         }
20208         
20209         this.picker().addClass('bottom');
20210         
20211         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20212     },
20213     
20214     onFocus : function()
20215     {
20216         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20217         this.show();
20218     },
20219     
20220     onBlur : function()
20221     {
20222         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20223         
20224         var d = this.inputEl().getValue();
20225         
20226         this.setValue(d);
20227                 
20228         this.hide();
20229     },
20230     
20231     show : function()
20232     {
20233         this.picker().show();
20234         this.picker().select('>.datepicker-months', true).first().show();
20235         this.update();
20236         this.place();
20237         
20238         this.fireEvent('show', this, this.date);
20239     },
20240     
20241     hide : function()
20242     {
20243         if(this.isInline) {
20244             return;
20245         }
20246         this.picker().hide();
20247         this.fireEvent('hide', this, this.date);
20248         
20249     },
20250     
20251     onMousedown: function(e)
20252     {
20253         e.stopPropagation();
20254         e.preventDefault();
20255     },
20256     
20257     keyup: function(e)
20258     {
20259         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20260         this.update();
20261     },
20262
20263     fireKey: function(e)
20264     {
20265         if (!this.picker().isVisible()){
20266             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20267                 this.show();
20268             }
20269             return;
20270         }
20271         
20272         var dir;
20273         
20274         switch(e.keyCode){
20275             case 27: // escape
20276                 this.hide();
20277                 e.preventDefault();
20278                 break;
20279             case 37: // left
20280             case 39: // right
20281                 dir = e.keyCode == 37 ? -1 : 1;
20282                 
20283                 this.vIndex = this.vIndex + dir;
20284                 
20285                 if(this.vIndex < 0){
20286                     this.vIndex = 0;
20287                 }
20288                 
20289                 if(this.vIndex > 11){
20290                     this.vIndex = 11;
20291                 }
20292                 
20293                 if(isNaN(this.vIndex)){
20294                     this.vIndex = 0;
20295                 }
20296                 
20297                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20298                 
20299                 break;
20300             case 38: // up
20301             case 40: // down
20302                 
20303                 dir = e.keyCode == 38 ? -1 : 1;
20304                 
20305                 this.vIndex = this.vIndex + dir * 4;
20306                 
20307                 if(this.vIndex < 0){
20308                     this.vIndex = 0;
20309                 }
20310                 
20311                 if(this.vIndex > 11){
20312                     this.vIndex = 11;
20313                 }
20314                 
20315                 if(isNaN(this.vIndex)){
20316                     this.vIndex = 0;
20317                 }
20318                 
20319                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20320                 break;
20321                 
20322             case 13: // enter
20323                 
20324                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20325                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20326                 }
20327                 
20328                 this.hide();
20329                 e.preventDefault();
20330                 break;
20331             case 9: // tab
20332                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20333                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20334                 }
20335                 this.hide();
20336                 break;
20337             case 16: // shift
20338             case 17: // ctrl
20339             case 18: // alt
20340                 break;
20341             default :
20342                 this.hide();
20343                 
20344         }
20345     },
20346     
20347     remove: function() 
20348     {
20349         this.picker().remove();
20350     }
20351    
20352 });
20353
20354 Roo.apply(Roo.bootstrap.MonthField,  {
20355     
20356     content : {
20357         tag: 'tbody',
20358         cn: [
20359         {
20360             tag: 'tr',
20361             cn: [
20362             {
20363                 tag: 'td',
20364                 colspan: '7'
20365             }
20366             ]
20367         }
20368         ]
20369     },
20370     
20371     dates:{
20372         en: {
20373             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20374             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20375         }
20376     }
20377 });
20378
20379 Roo.apply(Roo.bootstrap.MonthField,  {
20380   
20381     template : {
20382         tag: 'div',
20383         cls: 'datepicker dropdown-menu roo-dynamic',
20384         cn: [
20385             {
20386                 tag: 'div',
20387                 cls: 'datepicker-months',
20388                 cn: [
20389                 {
20390                     tag: 'table',
20391                     cls: 'table-condensed',
20392                     cn:[
20393                         Roo.bootstrap.DateField.content
20394                     ]
20395                 }
20396                 ]
20397             }
20398         ]
20399     }
20400 });
20401
20402  
20403
20404  
20405  /*
20406  * - LGPL
20407  *
20408  * CheckBox
20409  * 
20410  */
20411
20412 /**
20413  * @class Roo.bootstrap.CheckBox
20414  * @extends Roo.bootstrap.Input
20415  * Bootstrap CheckBox class
20416  * 
20417  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20418  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20419  * @cfg {String} boxLabel The text that appears beside the checkbox
20420  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20421  * @cfg {Boolean} checked initnal the element
20422  * @cfg {Boolean} inline inline the element (default false)
20423  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20424  * @cfg {String} tooltip label tooltip
20425  * 
20426  * @constructor
20427  * Create a new CheckBox
20428  * @param {Object} config The config object
20429  */
20430
20431 Roo.bootstrap.CheckBox = function(config){
20432     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20433    
20434     this.addEvents({
20435         /**
20436         * @event check
20437         * Fires when the element is checked or unchecked.
20438         * @param {Roo.bootstrap.CheckBox} this This input
20439         * @param {Boolean} checked The new checked value
20440         */
20441        check : true,
20442        /**
20443         * @event click
20444         * Fires when the element is click.
20445         * @param {Roo.bootstrap.CheckBox} this This input
20446         */
20447        click : true
20448     });
20449     
20450 };
20451
20452 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20453   
20454     inputType: 'checkbox',
20455     inputValue: 1,
20456     valueOff: 0,
20457     boxLabel: false,
20458     checked: false,
20459     weight : false,
20460     inline: false,
20461     tooltip : '',
20462     
20463     getAutoCreate : function()
20464     {
20465         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20466         
20467         var id = Roo.id();
20468         
20469         var cfg = {};
20470         
20471         cfg.cls = 'form-group ' + this.inputType; //input-group
20472         
20473         if(this.inline){
20474             cfg.cls += ' ' + this.inputType + '-inline';
20475         }
20476         
20477         var input =  {
20478             tag: 'input',
20479             id : id,
20480             type : this.inputType,
20481             value : this.inputValue,
20482             cls : 'roo-' + this.inputType, //'form-box',
20483             placeholder : this.placeholder || ''
20484             
20485         };
20486         
20487         if(this.inputType != 'radio'){
20488             var hidden =  {
20489                 tag: 'input',
20490                 type : 'hidden',
20491                 cls : 'roo-hidden-value',
20492                 value : this.checked ? this.inputValue : this.valueOff
20493             };
20494         }
20495         
20496             
20497         if (this.weight) { // Validity check?
20498             cfg.cls += " " + this.inputType + "-" + this.weight;
20499         }
20500         
20501         if (this.disabled) {
20502             input.disabled=true;
20503         }
20504         
20505         if(this.checked){
20506             input.checked = this.checked;
20507         }
20508         
20509         if (this.name) {
20510             
20511             input.name = this.name;
20512             
20513             if(this.inputType != 'radio'){
20514                 hidden.name = this.name;
20515                 input.name = '_hidden_' + this.name;
20516             }
20517         }
20518         
20519         if (this.size) {
20520             input.cls += ' input-' + this.size;
20521         }
20522         
20523         var settings=this;
20524         
20525         ['xs','sm','md','lg'].map(function(size){
20526             if (settings[size]) {
20527                 cfg.cls += ' col-' + size + '-' + settings[size];
20528             }
20529         });
20530         
20531         var inputblock = input;
20532          
20533         if (this.before || this.after) {
20534             
20535             inputblock = {
20536                 cls : 'input-group',
20537                 cn :  [] 
20538             };
20539             
20540             if (this.before) {
20541                 inputblock.cn.push({
20542                     tag :'span',
20543                     cls : 'input-group-addon',
20544                     html : this.before
20545                 });
20546             }
20547             
20548             inputblock.cn.push(input);
20549             
20550             if(this.inputType != 'radio'){
20551                 inputblock.cn.push(hidden);
20552             }
20553             
20554             if (this.after) {
20555                 inputblock.cn.push({
20556                     tag :'span',
20557                     cls : 'input-group-addon',
20558                     html : this.after
20559                 });
20560             }
20561             
20562         }
20563         
20564         if (align ==='left' && this.fieldLabel.length) {
20565 //                Roo.log("left and has label");
20566             cfg.cn = [
20567                 {
20568                     tag: 'label',
20569                     'for' :  id,
20570                     cls : 'control-label',
20571                     html : this.fieldLabel
20572                 },
20573                 {
20574                     cls : "", 
20575                     cn: [
20576                         inputblock
20577                     ]
20578                 }
20579             ];
20580             
20581             if(this.labelWidth > 12){
20582                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20583             }
20584             
20585             if(this.labelWidth < 13 && this.labelmd == 0){
20586                 this.labelmd = this.labelWidth;
20587             }
20588             
20589             if(this.labellg > 0){
20590                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20591                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20592             }
20593             
20594             if(this.labelmd > 0){
20595                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20596                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20597             }
20598             
20599             if(this.labelsm > 0){
20600                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20601                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20602             }
20603             
20604             if(this.labelxs > 0){
20605                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20606                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20607             }
20608             
20609         } else if ( this.fieldLabel.length) {
20610 //                Roo.log(" label");
20611                 cfg.cn = [
20612                    
20613                     {
20614                         tag: this.boxLabel ? 'span' : 'label',
20615                         'for': id,
20616                         cls: 'control-label box-input-label',
20617                         //cls : 'input-group-addon',
20618                         html : this.fieldLabel
20619                     },
20620                     
20621                     inputblock
20622                     
20623                 ];
20624
20625         } else {
20626             
20627 //                Roo.log(" no label && no align");
20628                 cfg.cn = [  inputblock ] ;
20629                 
20630                 
20631         }
20632         
20633         if(this.boxLabel){
20634              var boxLabelCfg = {
20635                 tag: 'label',
20636                 //'for': id, // box label is handled by onclick - so no for...
20637                 cls: 'box-label',
20638                 html: this.boxLabel
20639             };
20640             
20641             if(this.tooltip){
20642                 boxLabelCfg.tooltip = this.tooltip;
20643             }
20644              
20645             cfg.cn.push(boxLabelCfg);
20646         }
20647         
20648         if(this.inputType != 'radio'){
20649             cfg.cn.push(hidden);
20650         }
20651         
20652         return cfg;
20653         
20654     },
20655     
20656     /**
20657      * return the real input element.
20658      */
20659     inputEl: function ()
20660     {
20661         return this.el.select('input.roo-' + this.inputType,true).first();
20662     },
20663     hiddenEl: function ()
20664     {
20665         return this.el.select('input.roo-hidden-value',true).first();
20666     },
20667     
20668     labelEl: function()
20669     {
20670         return this.el.select('label.control-label',true).first();
20671     },
20672     /* depricated... */
20673     
20674     label: function()
20675     {
20676         return this.labelEl();
20677     },
20678     
20679     boxLabelEl: function()
20680     {
20681         return this.el.select('label.box-label',true).first();
20682     },
20683     
20684     initEvents : function()
20685     {
20686 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20687         
20688         this.inputEl().on('click', this.onClick,  this);
20689         
20690         if (this.boxLabel) { 
20691             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20692         }
20693         
20694         this.startValue = this.getValue();
20695         
20696         if(this.groupId){
20697             Roo.bootstrap.CheckBox.register(this);
20698         }
20699     },
20700     
20701     onClick : function(e)
20702     {   
20703         if(this.fireEvent('click', this, e) !== false){
20704             this.setChecked(!this.checked);
20705         }
20706         
20707     },
20708     
20709     setChecked : function(state,suppressEvent)
20710     {
20711         this.startValue = this.getValue();
20712
20713         if(this.inputType == 'radio'){
20714             
20715             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20716                 e.dom.checked = false;
20717             });
20718             
20719             this.inputEl().dom.checked = true;
20720             
20721             this.inputEl().dom.value = this.inputValue;
20722             
20723             if(suppressEvent !== true){
20724                 this.fireEvent('check', this, true);
20725             }
20726             
20727             this.validate();
20728             
20729             return;
20730         }
20731         
20732         this.checked = state;
20733         
20734         this.inputEl().dom.checked = state;
20735         
20736         
20737         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20738         
20739         if(suppressEvent !== true){
20740             this.fireEvent('check', this, state);
20741         }
20742         
20743         this.validate();
20744     },
20745     
20746     getValue : function()
20747     {
20748         if(this.inputType == 'radio'){
20749             return this.getGroupValue();
20750         }
20751         
20752         return this.hiddenEl().dom.value;
20753         
20754     },
20755     
20756     getGroupValue : function()
20757     {
20758         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20759             return '';
20760         }
20761         
20762         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20763     },
20764     
20765     setValue : function(v,suppressEvent)
20766     {
20767         if(this.inputType == 'radio'){
20768             this.setGroupValue(v, suppressEvent);
20769             return;
20770         }
20771         
20772         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20773         
20774         this.validate();
20775     },
20776     
20777     setGroupValue : function(v, suppressEvent)
20778     {
20779         this.startValue = this.getValue();
20780         
20781         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20782             e.dom.checked = false;
20783             
20784             if(e.dom.value == v){
20785                 e.dom.checked = true;
20786             }
20787         });
20788         
20789         if(suppressEvent !== true){
20790             this.fireEvent('check', this, true);
20791         }
20792
20793         this.validate();
20794         
20795         return;
20796     },
20797     
20798     validate : function()
20799     {
20800         if(this.getVisibilityEl().hasClass('hidden')){
20801             return true;
20802         }
20803         
20804         if(
20805                 this.disabled || 
20806                 (this.inputType == 'radio' && this.validateRadio()) ||
20807                 (this.inputType == 'checkbox' && this.validateCheckbox())
20808         ){
20809             this.markValid();
20810             return true;
20811         }
20812         
20813         this.markInvalid();
20814         return false;
20815     },
20816     
20817     validateRadio : function()
20818     {
20819         if(this.getVisibilityEl().hasClass('hidden')){
20820             return true;
20821         }
20822         
20823         if(this.allowBlank){
20824             return true;
20825         }
20826         
20827         var valid = false;
20828         
20829         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20830             if(!e.dom.checked){
20831                 return;
20832             }
20833             
20834             valid = true;
20835             
20836             return false;
20837         });
20838         
20839         return valid;
20840     },
20841     
20842     validateCheckbox : function()
20843     {
20844         if(!this.groupId){
20845             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20846             //return (this.getValue() == this.inputValue) ? true : false;
20847         }
20848         
20849         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20850         
20851         if(!group){
20852             return false;
20853         }
20854         
20855         var r = false;
20856         
20857         for(var i in group){
20858             if(group[i].el.isVisible(true)){
20859                 r = false;
20860                 break;
20861             }
20862             
20863             r = true;
20864         }
20865         
20866         for(var i in group){
20867             if(r){
20868                 break;
20869             }
20870             
20871             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20872         }
20873         
20874         return r;
20875     },
20876     
20877     /**
20878      * Mark this field as valid
20879      */
20880     markValid : function()
20881     {
20882         var _this = this;
20883         
20884         this.fireEvent('valid', this);
20885         
20886         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20887         
20888         if(this.groupId){
20889             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20890         }
20891         
20892         if(label){
20893             label.markValid();
20894         }
20895
20896         if(this.inputType == 'radio'){
20897             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20898                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20899                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20900             });
20901             
20902             return;
20903         }
20904
20905         if(!this.groupId){
20906             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20907             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20908             return;
20909         }
20910         
20911         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20912         
20913         if(!group){
20914             return;
20915         }
20916         
20917         for(var i in group){
20918             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20919             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20920         }
20921     },
20922     
20923      /**
20924      * Mark this field as invalid
20925      * @param {String} msg The validation message
20926      */
20927     markInvalid : function(msg)
20928     {
20929         if(this.allowBlank){
20930             return;
20931         }
20932         
20933         var _this = this;
20934         
20935         this.fireEvent('invalid', this, msg);
20936         
20937         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20938         
20939         if(this.groupId){
20940             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20941         }
20942         
20943         if(label){
20944             label.markInvalid();
20945         }
20946             
20947         if(this.inputType == 'radio'){
20948             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20949                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20950                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20951             });
20952             
20953             return;
20954         }
20955         
20956         if(!this.groupId){
20957             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20958             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20959             return;
20960         }
20961         
20962         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20963         
20964         if(!group){
20965             return;
20966         }
20967         
20968         for(var i in group){
20969             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20970             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20971         }
20972         
20973     },
20974     
20975     clearInvalid : function()
20976     {
20977         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20978         
20979         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20980         
20981         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20982         
20983         if (label && label.iconEl) {
20984             label.iconEl.removeClass(label.validClass);
20985             label.iconEl.removeClass(label.invalidClass);
20986         }
20987     },
20988     
20989     disable : function()
20990     {
20991         if(this.inputType != 'radio'){
20992             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20993             return;
20994         }
20995         
20996         var _this = this;
20997         
20998         if(this.rendered){
20999             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21000                 _this.getActionEl().addClass(this.disabledClass);
21001                 e.dom.disabled = true;
21002             });
21003         }
21004         
21005         this.disabled = true;
21006         this.fireEvent("disable", this);
21007         return this;
21008     },
21009
21010     enable : function()
21011     {
21012         if(this.inputType != 'radio'){
21013             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21014             return;
21015         }
21016         
21017         var _this = this;
21018         
21019         if(this.rendered){
21020             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21021                 _this.getActionEl().removeClass(this.disabledClass);
21022                 e.dom.disabled = false;
21023             });
21024         }
21025         
21026         this.disabled = false;
21027         this.fireEvent("enable", this);
21028         return this;
21029     },
21030     
21031     setBoxLabel : function(v)
21032     {
21033         this.boxLabel = v;
21034         
21035         if(this.rendered){
21036             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21037         }
21038     }
21039
21040 });
21041
21042 Roo.apply(Roo.bootstrap.CheckBox, {
21043     
21044     groups: {},
21045     
21046      /**
21047     * register a CheckBox Group
21048     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21049     */
21050     register : function(checkbox)
21051     {
21052         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21053             this.groups[checkbox.groupId] = {};
21054         }
21055         
21056         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21057             return;
21058         }
21059         
21060         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21061         
21062     },
21063     /**
21064     * fetch a CheckBox Group based on the group ID
21065     * @param {string} the group ID
21066     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21067     */
21068     get: function(groupId) {
21069         if (typeof(this.groups[groupId]) == 'undefined') {
21070             return false;
21071         }
21072         
21073         return this.groups[groupId] ;
21074     }
21075     
21076     
21077 });
21078 /*
21079  * - LGPL
21080  *
21081  * RadioItem
21082  * 
21083  */
21084
21085 /**
21086  * @class Roo.bootstrap.Radio
21087  * @extends Roo.bootstrap.Component
21088  * Bootstrap Radio class
21089  * @cfg {String} boxLabel - the label associated
21090  * @cfg {String} value - the value of radio
21091  * 
21092  * @constructor
21093  * Create a new Radio
21094  * @param {Object} config The config object
21095  */
21096 Roo.bootstrap.Radio = function(config){
21097     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21098     
21099 };
21100
21101 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21102     
21103     boxLabel : '',
21104     
21105     value : '',
21106     
21107     getAutoCreate : function()
21108     {
21109         var cfg = {
21110             tag : 'div',
21111             cls : 'form-group radio',
21112             cn : [
21113                 {
21114                     tag : 'label',
21115                     cls : 'box-label',
21116                     html : this.boxLabel
21117                 }
21118             ]
21119         };
21120         
21121         return cfg;
21122     },
21123     
21124     initEvents : function() 
21125     {
21126         this.parent().register(this);
21127         
21128         this.el.on('click', this.onClick, this);
21129         
21130     },
21131     
21132     onClick : function(e)
21133     {
21134         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21135             this.setChecked(true);
21136         }
21137     },
21138     
21139     setChecked : function(state, suppressEvent)
21140     {
21141         this.parent().setValue(this.value, suppressEvent);
21142         
21143     },
21144     
21145     setBoxLabel : function(v)
21146     {
21147         this.boxLabel = v;
21148         
21149         if(this.rendered){
21150             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21151         }
21152     }
21153     
21154 });
21155  
21156
21157  /*
21158  * - LGPL
21159  *
21160  * Input
21161  * 
21162  */
21163
21164 /**
21165  * @class Roo.bootstrap.SecurePass
21166  * @extends Roo.bootstrap.Input
21167  * Bootstrap SecurePass class
21168  *
21169  * 
21170  * @constructor
21171  * Create a new SecurePass
21172  * @param {Object} config The config object
21173  */
21174  
21175 Roo.bootstrap.SecurePass = function (config) {
21176     // these go here, so the translation tool can replace them..
21177     this.errors = {
21178         PwdEmpty: "Please type a password, and then retype it to confirm.",
21179         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21180         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21181         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21182         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21183         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21184         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21185         TooWeak: "Your password is Too Weak."
21186     },
21187     this.meterLabel = "Password strength:";
21188     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21189     this.meterClass = [
21190         "roo-password-meter-tooweak", 
21191         "roo-password-meter-weak", 
21192         "roo-password-meter-medium", 
21193         "roo-password-meter-strong", 
21194         "roo-password-meter-grey"
21195     ];
21196     
21197     this.errors = {};
21198     
21199     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21200 }
21201
21202 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21203     /**
21204      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21205      * {
21206      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21207      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21208      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21209      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21210      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21211      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21212      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21213      * })
21214      */
21215     // private
21216     
21217     meterWidth: 300,
21218     errorMsg :'',    
21219     errors: false,
21220     imageRoot: '/',
21221     /**
21222      * @cfg {String/Object} Label for the strength meter (defaults to
21223      * 'Password strength:')
21224      */
21225     // private
21226     meterLabel: '',
21227     /**
21228      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21229      * ['Weak', 'Medium', 'Strong'])
21230      */
21231     // private    
21232     pwdStrengths: false,    
21233     // private
21234     strength: 0,
21235     // private
21236     _lastPwd: null,
21237     // private
21238     kCapitalLetter: 0,
21239     kSmallLetter: 1,
21240     kDigit: 2,
21241     kPunctuation: 3,
21242     
21243     insecure: false,
21244     // private
21245     initEvents: function ()
21246     {
21247         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21248
21249         if (this.el.is('input[type=password]') && Roo.isSafari) {
21250             this.el.on('keydown', this.SafariOnKeyDown, this);
21251         }
21252
21253         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21254     },
21255     // private
21256     onRender: function (ct, position)
21257     {
21258         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21259         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21260         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21261
21262         this.trigger.createChild({
21263                    cn: [
21264                     {
21265                     //id: 'PwdMeter',
21266                     tag: 'div',
21267                     cls: 'roo-password-meter-grey col-xs-12',
21268                     style: {
21269                         //width: 0,
21270                         //width: this.meterWidth + 'px'                                                
21271                         }
21272                     },
21273                     {                            
21274                          cls: 'roo-password-meter-text'                          
21275                     }
21276                 ]            
21277         });
21278
21279          
21280         if (this.hideTrigger) {
21281             this.trigger.setDisplayed(false);
21282         }
21283         this.setSize(this.width || '', this.height || '');
21284     },
21285     // private
21286     onDestroy: function ()
21287     {
21288         if (this.trigger) {
21289             this.trigger.removeAllListeners();
21290             this.trigger.remove();
21291         }
21292         if (this.wrap) {
21293             this.wrap.remove();
21294         }
21295         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21296     },
21297     // private
21298     checkStrength: function ()
21299     {
21300         var pwd = this.inputEl().getValue();
21301         if (pwd == this._lastPwd) {
21302             return;
21303         }
21304
21305         var strength;
21306         if (this.ClientSideStrongPassword(pwd)) {
21307             strength = 3;
21308         } else if (this.ClientSideMediumPassword(pwd)) {
21309             strength = 2;
21310         } else if (this.ClientSideWeakPassword(pwd)) {
21311             strength = 1;
21312         } else {
21313             strength = 0;
21314         }
21315         
21316         Roo.log('strength1: ' + strength);
21317         
21318         //var pm = this.trigger.child('div/div/div').dom;
21319         var pm = this.trigger.child('div/div');
21320         pm.removeClass(this.meterClass);
21321         pm.addClass(this.meterClass[strength]);
21322                 
21323         
21324         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21325                 
21326         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21327         
21328         this._lastPwd = pwd;
21329     },
21330     reset: function ()
21331     {
21332         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21333         
21334         this._lastPwd = '';
21335         
21336         var pm = this.trigger.child('div/div');
21337         pm.removeClass(this.meterClass);
21338         pm.addClass('roo-password-meter-grey');        
21339         
21340         
21341         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21342         
21343         pt.innerHTML = '';
21344         this.inputEl().dom.type='password';
21345     },
21346     // private
21347     validateValue: function (value)
21348     {
21349         
21350         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21351             return false;
21352         }
21353         if (value.length == 0) {
21354             if (this.allowBlank) {
21355                 this.clearInvalid();
21356                 return true;
21357             }
21358
21359             this.markInvalid(this.errors.PwdEmpty);
21360             this.errorMsg = this.errors.PwdEmpty;
21361             return false;
21362         }
21363         
21364         if(this.insecure){
21365             return true;
21366         }
21367         
21368         if ('[\x21-\x7e]*'.match(value)) {
21369             this.markInvalid(this.errors.PwdBadChar);
21370             this.errorMsg = this.errors.PwdBadChar;
21371             return false;
21372         }
21373         if (value.length < 6) {
21374             this.markInvalid(this.errors.PwdShort);
21375             this.errorMsg = this.errors.PwdShort;
21376             return false;
21377         }
21378         if (value.length > 16) {
21379             this.markInvalid(this.errors.PwdLong);
21380             this.errorMsg = this.errors.PwdLong;
21381             return false;
21382         }
21383         var strength;
21384         if (this.ClientSideStrongPassword(value)) {
21385             strength = 3;
21386         } else if (this.ClientSideMediumPassword(value)) {
21387             strength = 2;
21388         } else if (this.ClientSideWeakPassword(value)) {
21389             strength = 1;
21390         } else {
21391             strength = 0;
21392         }
21393
21394         
21395         if (strength < 2) {
21396             //this.markInvalid(this.errors.TooWeak);
21397             this.errorMsg = this.errors.TooWeak;
21398             //return false;
21399         }
21400         
21401         
21402         console.log('strength2: ' + strength);
21403         
21404         //var pm = this.trigger.child('div/div/div').dom;
21405         
21406         var pm = this.trigger.child('div/div');
21407         pm.removeClass(this.meterClass);
21408         pm.addClass(this.meterClass[strength]);
21409                 
21410         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21411                 
21412         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21413         
21414         this.errorMsg = ''; 
21415         return true;
21416     },
21417     // private
21418     CharacterSetChecks: function (type)
21419     {
21420         this.type = type;
21421         this.fResult = false;
21422     },
21423     // private
21424     isctype: function (character, type)
21425     {
21426         switch (type) {  
21427             case this.kCapitalLetter:
21428                 if (character >= 'A' && character <= 'Z') {
21429                     return true;
21430                 }
21431                 break;
21432             
21433             case this.kSmallLetter:
21434                 if (character >= 'a' && character <= 'z') {
21435                     return true;
21436                 }
21437                 break;
21438             
21439             case this.kDigit:
21440                 if (character >= '0' && character <= '9') {
21441                     return true;
21442                 }
21443                 break;
21444             
21445             case this.kPunctuation:
21446                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21447                     return true;
21448                 }
21449                 break;
21450             
21451             default:
21452                 return false;
21453         }
21454
21455     },
21456     // private
21457     IsLongEnough: function (pwd, size)
21458     {
21459         return !(pwd == null || isNaN(size) || pwd.length < size);
21460     },
21461     // private
21462     SpansEnoughCharacterSets: function (word, nb)
21463     {
21464         if (!this.IsLongEnough(word, nb))
21465         {
21466             return false;
21467         }
21468
21469         var characterSetChecks = new Array(
21470             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21471             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21472         );
21473         
21474         for (var index = 0; index < word.length; ++index) {
21475             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21476                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21477                     characterSetChecks[nCharSet].fResult = true;
21478                     break;
21479                 }
21480             }
21481         }
21482
21483         var nCharSets = 0;
21484         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21485             if (characterSetChecks[nCharSet].fResult) {
21486                 ++nCharSets;
21487             }
21488         }
21489
21490         if (nCharSets < nb) {
21491             return false;
21492         }
21493         return true;
21494     },
21495     // private
21496     ClientSideStrongPassword: function (pwd)
21497     {
21498         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21499     },
21500     // private
21501     ClientSideMediumPassword: function (pwd)
21502     {
21503         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21504     },
21505     // private
21506     ClientSideWeakPassword: function (pwd)
21507     {
21508         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21509     }
21510           
21511 })//<script type="text/javascript">
21512
21513 /*
21514  * Based  Ext JS Library 1.1.1
21515  * Copyright(c) 2006-2007, Ext JS, LLC.
21516  * LGPL
21517  *
21518  */
21519  
21520 /**
21521  * @class Roo.HtmlEditorCore
21522  * @extends Roo.Component
21523  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21524  *
21525  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21526  */
21527
21528 Roo.HtmlEditorCore = function(config){
21529     
21530     
21531     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21532     
21533     
21534     this.addEvents({
21535         /**
21536          * @event initialize
21537          * Fires when the editor is fully initialized (including the iframe)
21538          * @param {Roo.HtmlEditorCore} this
21539          */
21540         initialize: true,
21541         /**
21542          * @event activate
21543          * Fires when the editor is first receives the focus. Any insertion must wait
21544          * until after this event.
21545          * @param {Roo.HtmlEditorCore} this
21546          */
21547         activate: true,
21548          /**
21549          * @event beforesync
21550          * Fires before the textarea is updated with content from the editor iframe. Return false
21551          * to cancel the sync.
21552          * @param {Roo.HtmlEditorCore} this
21553          * @param {String} html
21554          */
21555         beforesync: true,
21556          /**
21557          * @event beforepush
21558          * Fires before the iframe editor is updated with content from the textarea. Return false
21559          * to cancel the push.
21560          * @param {Roo.HtmlEditorCore} this
21561          * @param {String} html
21562          */
21563         beforepush: true,
21564          /**
21565          * @event sync
21566          * Fires when the textarea is updated with content from the editor iframe.
21567          * @param {Roo.HtmlEditorCore} this
21568          * @param {String} html
21569          */
21570         sync: true,
21571          /**
21572          * @event push
21573          * Fires when the iframe editor is updated with content from the textarea.
21574          * @param {Roo.HtmlEditorCore} this
21575          * @param {String} html
21576          */
21577         push: true,
21578         
21579         /**
21580          * @event editorevent
21581          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21582          * @param {Roo.HtmlEditorCore} this
21583          */
21584         editorevent: true
21585         
21586     });
21587     
21588     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21589     
21590     // defaults : white / black...
21591     this.applyBlacklists();
21592     
21593     
21594     
21595 };
21596
21597
21598 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21599
21600
21601      /**
21602      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21603      */
21604     
21605     owner : false,
21606     
21607      /**
21608      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21609      *                        Roo.resizable.
21610      */
21611     resizable : false,
21612      /**
21613      * @cfg {Number} height (in pixels)
21614      */   
21615     height: 300,
21616    /**
21617      * @cfg {Number} width (in pixels)
21618      */   
21619     width: 500,
21620     
21621     /**
21622      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21623      * 
21624      */
21625     stylesheets: false,
21626     
21627     // id of frame..
21628     frameId: false,
21629     
21630     // private properties
21631     validationEvent : false,
21632     deferHeight: true,
21633     initialized : false,
21634     activated : false,
21635     sourceEditMode : false,
21636     onFocus : Roo.emptyFn,
21637     iframePad:3,
21638     hideMode:'offsets',
21639     
21640     clearUp: true,
21641     
21642     // blacklist + whitelisted elements..
21643     black: false,
21644     white: false,
21645      
21646     bodyCls : '',
21647
21648     /**
21649      * Protected method that will not generally be called directly. It
21650      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21651      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21652      */
21653     getDocMarkup : function(){
21654         // body styles..
21655         var st = '';
21656         
21657         // inherit styels from page...?? 
21658         if (this.stylesheets === false) {
21659             
21660             Roo.get(document.head).select('style').each(function(node) {
21661                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21662             });
21663             
21664             Roo.get(document.head).select('link').each(function(node) { 
21665                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21666             });
21667             
21668         } else if (!this.stylesheets.length) {
21669                 // simple..
21670                 st = '<style type="text/css">' +
21671                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21672                    '</style>';
21673         } else { 
21674             st = '<style type="text/css">' +
21675                     this.stylesheets +
21676                 '</style>';
21677         }
21678         
21679         st +=  '<style type="text/css">' +
21680             'IMG { cursor: pointer } ' +
21681         '</style>';
21682
21683         var cls = 'roo-htmleditor-body';
21684         
21685         if(this.bodyCls.length){
21686             cls += ' ' + this.bodyCls;
21687         }
21688         
21689         return '<html><head>' + st  +
21690             //<style type="text/css">' +
21691             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21692             //'</style>' +
21693             ' </head><body class="' +  cls + '"></body></html>';
21694     },
21695
21696     // private
21697     onRender : function(ct, position)
21698     {
21699         var _t = this;
21700         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21701         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21702         
21703         
21704         this.el.dom.style.border = '0 none';
21705         this.el.dom.setAttribute('tabIndex', -1);
21706         this.el.addClass('x-hidden hide');
21707         
21708         
21709         
21710         if(Roo.isIE){ // fix IE 1px bogus margin
21711             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21712         }
21713        
21714         
21715         this.frameId = Roo.id();
21716         
21717          
21718         
21719         var iframe = this.owner.wrap.createChild({
21720             tag: 'iframe',
21721             cls: 'form-control', // bootstrap..
21722             id: this.frameId,
21723             name: this.frameId,
21724             frameBorder : 'no',
21725             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21726         }, this.el
21727         );
21728         
21729         
21730         this.iframe = iframe.dom;
21731
21732          this.assignDocWin();
21733         
21734         this.doc.designMode = 'on';
21735        
21736         this.doc.open();
21737         this.doc.write(this.getDocMarkup());
21738         this.doc.close();
21739
21740         
21741         var task = { // must defer to wait for browser to be ready
21742             run : function(){
21743                 //console.log("run task?" + this.doc.readyState);
21744                 this.assignDocWin();
21745                 if(this.doc.body || this.doc.readyState == 'complete'){
21746                     try {
21747                         this.doc.designMode="on";
21748                     } catch (e) {
21749                         return;
21750                     }
21751                     Roo.TaskMgr.stop(task);
21752                     this.initEditor.defer(10, this);
21753                 }
21754             },
21755             interval : 10,
21756             duration: 10000,
21757             scope: this
21758         };
21759         Roo.TaskMgr.start(task);
21760
21761     },
21762
21763     // private
21764     onResize : function(w, h)
21765     {
21766          Roo.log('resize: ' +w + ',' + h );
21767         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21768         if(!this.iframe){
21769             return;
21770         }
21771         if(typeof w == 'number'){
21772             
21773             this.iframe.style.width = w + 'px';
21774         }
21775         if(typeof h == 'number'){
21776             
21777             this.iframe.style.height = h + 'px';
21778             if(this.doc){
21779                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21780             }
21781         }
21782         
21783     },
21784
21785     /**
21786      * Toggles the editor between standard and source edit mode.
21787      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21788      */
21789     toggleSourceEdit : function(sourceEditMode){
21790         
21791         this.sourceEditMode = sourceEditMode === true;
21792         
21793         if(this.sourceEditMode){
21794  
21795             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21796             
21797         }else{
21798             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21799             //this.iframe.className = '';
21800             this.deferFocus();
21801         }
21802         //this.setSize(this.owner.wrap.getSize());
21803         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21804     },
21805
21806     
21807   
21808
21809     /**
21810      * Protected method that will not generally be called directly. If you need/want
21811      * custom HTML cleanup, this is the method you should override.
21812      * @param {String} html The HTML to be cleaned
21813      * return {String} The cleaned HTML
21814      */
21815     cleanHtml : function(html){
21816         html = String(html);
21817         if(html.length > 5){
21818             if(Roo.isSafari){ // strip safari nonsense
21819                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21820             }
21821         }
21822         if(html == '&nbsp;'){
21823             html = '';
21824         }
21825         return html;
21826     },
21827
21828     /**
21829      * HTML Editor -> Textarea
21830      * Protected method that will not generally be called directly. Syncs the contents
21831      * of the editor iframe with the textarea.
21832      */
21833     syncValue : function(){
21834         if(this.initialized){
21835             var bd = (this.doc.body || this.doc.documentElement);
21836             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21837             var html = bd.innerHTML;
21838             if(Roo.isSafari){
21839                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21840                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21841                 if(m && m[1]){
21842                     html = '<div style="'+m[0]+'">' + html + '</div>';
21843                 }
21844             }
21845             html = this.cleanHtml(html);
21846             // fix up the special chars.. normaly like back quotes in word...
21847             // however we do not want to do this with chinese..
21848             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21849                 var cc = b.charCodeAt();
21850                 if (
21851                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21852                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21853                     (cc >= 0xf900 && cc < 0xfb00 )
21854                 ) {
21855                         return b;
21856                 }
21857                 return "&#"+cc+";" 
21858             });
21859             if(this.owner.fireEvent('beforesync', this, html) !== false){
21860                 this.el.dom.value = html;
21861                 this.owner.fireEvent('sync', this, html);
21862             }
21863         }
21864     },
21865
21866     /**
21867      * Protected method that will not generally be called directly. Pushes the value of the textarea
21868      * into the iframe editor.
21869      */
21870     pushValue : function(){
21871         if(this.initialized){
21872             var v = this.el.dom.value.trim();
21873             
21874 //            if(v.length < 1){
21875 //                v = '&#160;';
21876 //            }
21877             
21878             if(this.owner.fireEvent('beforepush', this, v) !== false){
21879                 var d = (this.doc.body || this.doc.documentElement);
21880                 d.innerHTML = v;
21881                 this.cleanUpPaste();
21882                 this.el.dom.value = d.innerHTML;
21883                 this.owner.fireEvent('push', this, v);
21884             }
21885         }
21886     },
21887
21888     // private
21889     deferFocus : function(){
21890         this.focus.defer(10, this);
21891     },
21892
21893     // doc'ed in Field
21894     focus : function(){
21895         if(this.win && !this.sourceEditMode){
21896             this.win.focus();
21897         }else{
21898             this.el.focus();
21899         }
21900     },
21901     
21902     assignDocWin: function()
21903     {
21904         var iframe = this.iframe;
21905         
21906          if(Roo.isIE){
21907             this.doc = iframe.contentWindow.document;
21908             this.win = iframe.contentWindow;
21909         } else {
21910 //            if (!Roo.get(this.frameId)) {
21911 //                return;
21912 //            }
21913 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21914 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21915             
21916             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21917                 return;
21918             }
21919             
21920             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21921             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21922         }
21923     },
21924     
21925     // private
21926     initEditor : function(){
21927         //console.log("INIT EDITOR");
21928         this.assignDocWin();
21929         
21930         
21931         
21932         this.doc.designMode="on";
21933         this.doc.open();
21934         this.doc.write(this.getDocMarkup());
21935         this.doc.close();
21936         
21937         var dbody = (this.doc.body || this.doc.documentElement);
21938         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21939         // this copies styles from the containing element into thsi one..
21940         // not sure why we need all of this..
21941         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21942         
21943         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21944         //ss['background-attachment'] = 'fixed'; // w3c
21945         dbody.bgProperties = 'fixed'; // ie
21946         //Roo.DomHelper.applyStyles(dbody, ss);
21947         Roo.EventManager.on(this.doc, {
21948             //'mousedown': this.onEditorEvent,
21949             'mouseup': this.onEditorEvent,
21950             'dblclick': this.onEditorEvent,
21951             'click': this.onEditorEvent,
21952             'keyup': this.onEditorEvent,
21953             buffer:100,
21954             scope: this
21955         });
21956         if(Roo.isGecko){
21957             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21958         }
21959         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21960             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21961         }
21962         this.initialized = true;
21963
21964         this.owner.fireEvent('initialize', this);
21965         this.pushValue();
21966     },
21967
21968     // private
21969     onDestroy : function(){
21970         
21971         
21972         
21973         if(this.rendered){
21974             
21975             //for (var i =0; i < this.toolbars.length;i++) {
21976             //    // fixme - ask toolbars for heights?
21977             //    this.toolbars[i].onDestroy();
21978            // }
21979             
21980             //this.wrap.dom.innerHTML = '';
21981             //this.wrap.remove();
21982         }
21983     },
21984
21985     // private
21986     onFirstFocus : function(){
21987         
21988         this.assignDocWin();
21989         
21990         
21991         this.activated = true;
21992          
21993     
21994         if(Roo.isGecko){ // prevent silly gecko errors
21995             this.win.focus();
21996             var s = this.win.getSelection();
21997             if(!s.focusNode || s.focusNode.nodeType != 3){
21998                 var r = s.getRangeAt(0);
21999                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22000                 r.collapse(true);
22001                 this.deferFocus();
22002             }
22003             try{
22004                 this.execCmd('useCSS', true);
22005                 this.execCmd('styleWithCSS', false);
22006             }catch(e){}
22007         }
22008         this.owner.fireEvent('activate', this);
22009     },
22010
22011     // private
22012     adjustFont: function(btn){
22013         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22014         //if(Roo.isSafari){ // safari
22015         //    adjust *= 2;
22016        // }
22017         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22018         if(Roo.isSafari){ // safari
22019             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22020             v =  (v < 10) ? 10 : v;
22021             v =  (v > 48) ? 48 : v;
22022             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22023             
22024         }
22025         
22026         
22027         v = Math.max(1, v+adjust);
22028         
22029         this.execCmd('FontSize', v  );
22030     },
22031
22032     onEditorEvent : function(e)
22033     {
22034         this.owner.fireEvent('editorevent', this, e);
22035       //  this.updateToolbar();
22036         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22037     },
22038
22039     insertTag : function(tg)
22040     {
22041         // could be a bit smarter... -> wrap the current selected tRoo..
22042         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22043             
22044             range = this.createRange(this.getSelection());
22045             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22046             wrappingNode.appendChild(range.extractContents());
22047             range.insertNode(wrappingNode);
22048
22049             return;
22050             
22051             
22052             
22053         }
22054         this.execCmd("formatblock",   tg);
22055         
22056     },
22057     
22058     insertText : function(txt)
22059     {
22060         
22061         
22062         var range = this.createRange();
22063         range.deleteContents();
22064                //alert(Sender.getAttribute('label'));
22065                
22066         range.insertNode(this.doc.createTextNode(txt));
22067     } ,
22068     
22069      
22070
22071     /**
22072      * Executes a Midas editor command on the editor document and performs necessary focus and
22073      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22074      * @param {String} cmd The Midas command
22075      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22076      */
22077     relayCmd : function(cmd, value){
22078         this.win.focus();
22079         this.execCmd(cmd, value);
22080         this.owner.fireEvent('editorevent', this);
22081         //this.updateToolbar();
22082         this.owner.deferFocus();
22083     },
22084
22085     /**
22086      * Executes a Midas editor command directly on the editor document.
22087      * For visual commands, you should use {@link #relayCmd} instead.
22088      * <b>This should only be called after the editor is initialized.</b>
22089      * @param {String} cmd The Midas command
22090      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22091      */
22092     execCmd : function(cmd, value){
22093         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22094         this.syncValue();
22095     },
22096  
22097  
22098    
22099     /**
22100      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22101      * to insert tRoo.
22102      * @param {String} text | dom node.. 
22103      */
22104     insertAtCursor : function(text)
22105     {
22106         
22107         if(!this.activated){
22108             return;
22109         }
22110         /*
22111         if(Roo.isIE){
22112             this.win.focus();
22113             var r = this.doc.selection.createRange();
22114             if(r){
22115                 r.collapse(true);
22116                 r.pasteHTML(text);
22117                 this.syncValue();
22118                 this.deferFocus();
22119             
22120             }
22121             return;
22122         }
22123         */
22124         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22125             this.win.focus();
22126             
22127             
22128             // from jquery ui (MIT licenced)
22129             var range, node;
22130             var win = this.win;
22131             
22132             if (win.getSelection && win.getSelection().getRangeAt) {
22133                 range = win.getSelection().getRangeAt(0);
22134                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22135                 range.insertNode(node);
22136             } else if (win.document.selection && win.document.selection.createRange) {
22137                 // no firefox support
22138                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22139                 win.document.selection.createRange().pasteHTML(txt);
22140             } else {
22141                 // no firefox support
22142                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22143                 this.execCmd('InsertHTML', txt);
22144             } 
22145             
22146             this.syncValue();
22147             
22148             this.deferFocus();
22149         }
22150     },
22151  // private
22152     mozKeyPress : function(e){
22153         if(e.ctrlKey){
22154             var c = e.getCharCode(), cmd;
22155           
22156             if(c > 0){
22157                 c = String.fromCharCode(c).toLowerCase();
22158                 switch(c){
22159                     case 'b':
22160                         cmd = 'bold';
22161                         break;
22162                     case 'i':
22163                         cmd = 'italic';
22164                         break;
22165                     
22166                     case 'u':
22167                         cmd = 'underline';
22168                         break;
22169                     
22170                     case 'v':
22171                         this.cleanUpPaste.defer(100, this);
22172                         return;
22173                         
22174                 }
22175                 if(cmd){
22176                     this.win.focus();
22177                     this.execCmd(cmd);
22178                     this.deferFocus();
22179                     e.preventDefault();
22180                 }
22181                 
22182             }
22183         }
22184     },
22185
22186     // private
22187     fixKeys : function(){ // load time branching for fastest keydown performance
22188         if(Roo.isIE){
22189             return function(e){
22190                 var k = e.getKey(), r;
22191                 if(k == e.TAB){
22192                     e.stopEvent();
22193                     r = this.doc.selection.createRange();
22194                     if(r){
22195                         r.collapse(true);
22196                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22197                         this.deferFocus();
22198                     }
22199                     return;
22200                 }
22201                 
22202                 if(k == e.ENTER){
22203                     r = this.doc.selection.createRange();
22204                     if(r){
22205                         var target = r.parentElement();
22206                         if(!target || target.tagName.toLowerCase() != 'li'){
22207                             e.stopEvent();
22208                             r.pasteHTML('<br />');
22209                             r.collapse(false);
22210                             r.select();
22211                         }
22212                     }
22213                 }
22214                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22215                     this.cleanUpPaste.defer(100, this);
22216                     return;
22217                 }
22218                 
22219                 
22220             };
22221         }else if(Roo.isOpera){
22222             return function(e){
22223                 var k = e.getKey();
22224                 if(k == e.TAB){
22225                     e.stopEvent();
22226                     this.win.focus();
22227                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22228                     this.deferFocus();
22229                 }
22230                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22231                     this.cleanUpPaste.defer(100, this);
22232                     return;
22233                 }
22234                 
22235             };
22236         }else if(Roo.isSafari){
22237             return function(e){
22238                 var k = e.getKey();
22239                 
22240                 if(k == e.TAB){
22241                     e.stopEvent();
22242                     this.execCmd('InsertText','\t');
22243                     this.deferFocus();
22244                     return;
22245                 }
22246                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22247                     this.cleanUpPaste.defer(100, this);
22248                     return;
22249                 }
22250                 
22251              };
22252         }
22253     }(),
22254     
22255     getAllAncestors: function()
22256     {
22257         var p = this.getSelectedNode();
22258         var a = [];
22259         if (!p) {
22260             a.push(p); // push blank onto stack..
22261             p = this.getParentElement();
22262         }
22263         
22264         
22265         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22266             a.push(p);
22267             p = p.parentNode;
22268         }
22269         a.push(this.doc.body);
22270         return a;
22271     },
22272     lastSel : false,
22273     lastSelNode : false,
22274     
22275     
22276     getSelection : function() 
22277     {
22278         this.assignDocWin();
22279         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22280     },
22281     
22282     getSelectedNode: function() 
22283     {
22284         // this may only work on Gecko!!!
22285         
22286         // should we cache this!!!!
22287         
22288         
22289         
22290          
22291         var range = this.createRange(this.getSelection()).cloneRange();
22292         
22293         if (Roo.isIE) {
22294             var parent = range.parentElement();
22295             while (true) {
22296                 var testRange = range.duplicate();
22297                 testRange.moveToElementText(parent);
22298                 if (testRange.inRange(range)) {
22299                     break;
22300                 }
22301                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22302                     break;
22303                 }
22304                 parent = parent.parentElement;
22305             }
22306             return parent;
22307         }
22308         
22309         // is ancestor a text element.
22310         var ac =  range.commonAncestorContainer;
22311         if (ac.nodeType == 3) {
22312             ac = ac.parentNode;
22313         }
22314         
22315         var ar = ac.childNodes;
22316          
22317         var nodes = [];
22318         var other_nodes = [];
22319         var has_other_nodes = false;
22320         for (var i=0;i<ar.length;i++) {
22321             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22322                 continue;
22323             }
22324             // fullly contained node.
22325             
22326             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22327                 nodes.push(ar[i]);
22328                 continue;
22329             }
22330             
22331             // probably selected..
22332             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22333                 other_nodes.push(ar[i]);
22334                 continue;
22335             }
22336             // outer..
22337             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22338                 continue;
22339             }
22340             
22341             
22342             has_other_nodes = true;
22343         }
22344         if (!nodes.length && other_nodes.length) {
22345             nodes= other_nodes;
22346         }
22347         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22348             return false;
22349         }
22350         
22351         return nodes[0];
22352     },
22353     createRange: function(sel)
22354     {
22355         // this has strange effects when using with 
22356         // top toolbar - not sure if it's a great idea.
22357         //this.editor.contentWindow.focus();
22358         if (typeof sel != "undefined") {
22359             try {
22360                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22361             } catch(e) {
22362                 return this.doc.createRange();
22363             }
22364         } else {
22365             return this.doc.createRange();
22366         }
22367     },
22368     getParentElement: function()
22369     {
22370         
22371         this.assignDocWin();
22372         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22373         
22374         var range = this.createRange(sel);
22375          
22376         try {
22377             var p = range.commonAncestorContainer;
22378             while (p.nodeType == 3) { // text node
22379                 p = p.parentNode;
22380             }
22381             return p;
22382         } catch (e) {
22383             return null;
22384         }
22385     
22386     },
22387     /***
22388      *
22389      * Range intersection.. the hard stuff...
22390      *  '-1' = before
22391      *  '0' = hits..
22392      *  '1' = after.
22393      *         [ -- selected range --- ]
22394      *   [fail]                        [fail]
22395      *
22396      *    basically..
22397      *      if end is before start or  hits it. fail.
22398      *      if start is after end or hits it fail.
22399      *
22400      *   if either hits (but other is outside. - then it's not 
22401      *   
22402      *    
22403      **/
22404     
22405     
22406     // @see http://www.thismuchiknow.co.uk/?p=64.
22407     rangeIntersectsNode : function(range, node)
22408     {
22409         var nodeRange = node.ownerDocument.createRange();
22410         try {
22411             nodeRange.selectNode(node);
22412         } catch (e) {
22413             nodeRange.selectNodeContents(node);
22414         }
22415     
22416         var rangeStartRange = range.cloneRange();
22417         rangeStartRange.collapse(true);
22418     
22419         var rangeEndRange = range.cloneRange();
22420         rangeEndRange.collapse(false);
22421     
22422         var nodeStartRange = nodeRange.cloneRange();
22423         nodeStartRange.collapse(true);
22424     
22425         var nodeEndRange = nodeRange.cloneRange();
22426         nodeEndRange.collapse(false);
22427     
22428         return rangeStartRange.compareBoundaryPoints(
22429                  Range.START_TO_START, nodeEndRange) == -1 &&
22430                rangeEndRange.compareBoundaryPoints(
22431                  Range.START_TO_START, nodeStartRange) == 1;
22432         
22433          
22434     },
22435     rangeCompareNode : function(range, node)
22436     {
22437         var nodeRange = node.ownerDocument.createRange();
22438         try {
22439             nodeRange.selectNode(node);
22440         } catch (e) {
22441             nodeRange.selectNodeContents(node);
22442         }
22443         
22444         
22445         range.collapse(true);
22446     
22447         nodeRange.collapse(true);
22448      
22449         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22450         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22451          
22452         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22453         
22454         var nodeIsBefore   =  ss == 1;
22455         var nodeIsAfter    = ee == -1;
22456         
22457         if (nodeIsBefore && nodeIsAfter) {
22458             return 0; // outer
22459         }
22460         if (!nodeIsBefore && nodeIsAfter) {
22461             return 1; //right trailed.
22462         }
22463         
22464         if (nodeIsBefore && !nodeIsAfter) {
22465             return 2;  // left trailed.
22466         }
22467         // fully contined.
22468         return 3;
22469     },
22470
22471     // private? - in a new class?
22472     cleanUpPaste :  function()
22473     {
22474         // cleans up the whole document..
22475         Roo.log('cleanuppaste');
22476         
22477         this.cleanUpChildren(this.doc.body);
22478         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22479         if (clean != this.doc.body.innerHTML) {
22480             this.doc.body.innerHTML = clean;
22481         }
22482         
22483     },
22484     
22485     cleanWordChars : function(input) {// change the chars to hex code
22486         var he = Roo.HtmlEditorCore;
22487         
22488         var output = input;
22489         Roo.each(he.swapCodes, function(sw) { 
22490             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22491             
22492             output = output.replace(swapper, sw[1]);
22493         });
22494         
22495         return output;
22496     },
22497     
22498     
22499     cleanUpChildren : function (n)
22500     {
22501         if (!n.childNodes.length) {
22502             return;
22503         }
22504         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22505            this.cleanUpChild(n.childNodes[i]);
22506         }
22507     },
22508     
22509     
22510         
22511     
22512     cleanUpChild : function (node)
22513     {
22514         var ed = this;
22515         //console.log(node);
22516         if (node.nodeName == "#text") {
22517             // clean up silly Windows -- stuff?
22518             return; 
22519         }
22520         if (node.nodeName == "#comment") {
22521             node.parentNode.removeChild(node);
22522             // clean up silly Windows -- stuff?
22523             return; 
22524         }
22525         var lcname = node.tagName.toLowerCase();
22526         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22527         // whitelist of tags..
22528         
22529         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22530             // remove node.
22531             node.parentNode.removeChild(node);
22532             return;
22533             
22534         }
22535         
22536         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22537         
22538         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22539         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22540         
22541         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22542         //    remove_keep_children = true;
22543         //}
22544         
22545         if (remove_keep_children) {
22546             this.cleanUpChildren(node);
22547             // inserts everything just before this node...
22548             while (node.childNodes.length) {
22549                 var cn = node.childNodes[0];
22550                 node.removeChild(cn);
22551                 node.parentNode.insertBefore(cn, node);
22552             }
22553             node.parentNode.removeChild(node);
22554             return;
22555         }
22556         
22557         if (!node.attributes || !node.attributes.length) {
22558             this.cleanUpChildren(node);
22559             return;
22560         }
22561         
22562         function cleanAttr(n,v)
22563         {
22564             
22565             if (v.match(/^\./) || v.match(/^\//)) {
22566                 return;
22567             }
22568             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22569                 return;
22570             }
22571             if (v.match(/^#/)) {
22572                 return;
22573             }
22574 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22575             node.removeAttribute(n);
22576             
22577         }
22578         
22579         var cwhite = this.cwhite;
22580         var cblack = this.cblack;
22581             
22582         function cleanStyle(n,v)
22583         {
22584             if (v.match(/expression/)) { //XSS?? should we even bother..
22585                 node.removeAttribute(n);
22586                 return;
22587             }
22588             
22589             var parts = v.split(/;/);
22590             var clean = [];
22591             
22592             Roo.each(parts, function(p) {
22593                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22594                 if (!p.length) {
22595                     return true;
22596                 }
22597                 var l = p.split(':').shift().replace(/\s+/g,'');
22598                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22599                 
22600                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22601 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22602                     //node.removeAttribute(n);
22603                     return true;
22604                 }
22605                 //Roo.log()
22606                 // only allow 'c whitelisted system attributes'
22607                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22608 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22609                     //node.removeAttribute(n);
22610                     return true;
22611                 }
22612                 
22613                 
22614                  
22615                 
22616                 clean.push(p);
22617                 return true;
22618             });
22619             if (clean.length) { 
22620                 node.setAttribute(n, clean.join(';'));
22621             } else {
22622                 node.removeAttribute(n);
22623             }
22624             
22625         }
22626         
22627         
22628         for (var i = node.attributes.length-1; i > -1 ; i--) {
22629             var a = node.attributes[i];
22630             //console.log(a);
22631             
22632             if (a.name.toLowerCase().substr(0,2)=='on')  {
22633                 node.removeAttribute(a.name);
22634                 continue;
22635             }
22636             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22637                 node.removeAttribute(a.name);
22638                 continue;
22639             }
22640             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22641                 cleanAttr(a.name,a.value); // fixme..
22642                 continue;
22643             }
22644             if (a.name == 'style') {
22645                 cleanStyle(a.name,a.value);
22646                 continue;
22647             }
22648             /// clean up MS crap..
22649             // tecnically this should be a list of valid class'es..
22650             
22651             
22652             if (a.name == 'class') {
22653                 if (a.value.match(/^Mso/)) {
22654                     node.className = '';
22655                 }
22656                 
22657                 if (a.value.match(/^body$/)) {
22658                     node.className = '';
22659                 }
22660                 continue;
22661             }
22662             
22663             // style cleanup!?
22664             // class cleanup?
22665             
22666         }
22667         
22668         
22669         this.cleanUpChildren(node);
22670         
22671         
22672     },
22673     
22674     /**
22675      * Clean up MS wordisms...
22676      */
22677     cleanWord : function(node)
22678     {
22679         
22680         
22681         if (!node) {
22682             this.cleanWord(this.doc.body);
22683             return;
22684         }
22685         if (node.nodeName == "#text") {
22686             // clean up silly Windows -- stuff?
22687             return; 
22688         }
22689         if (node.nodeName == "#comment") {
22690             node.parentNode.removeChild(node);
22691             // clean up silly Windows -- stuff?
22692             return; 
22693         }
22694         
22695         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22696             node.parentNode.removeChild(node);
22697             return;
22698         }
22699         
22700         // remove - but keep children..
22701         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22702             while (node.childNodes.length) {
22703                 var cn = node.childNodes[0];
22704                 node.removeChild(cn);
22705                 node.parentNode.insertBefore(cn, node);
22706             }
22707             node.parentNode.removeChild(node);
22708             this.iterateChildren(node, this.cleanWord);
22709             return;
22710         }
22711         // clean styles
22712         if (node.className.length) {
22713             
22714             var cn = node.className.split(/\W+/);
22715             var cna = [];
22716             Roo.each(cn, function(cls) {
22717                 if (cls.match(/Mso[a-zA-Z]+/)) {
22718                     return;
22719                 }
22720                 cna.push(cls);
22721             });
22722             node.className = cna.length ? cna.join(' ') : '';
22723             if (!cna.length) {
22724                 node.removeAttribute("class");
22725             }
22726         }
22727         
22728         if (node.hasAttribute("lang")) {
22729             node.removeAttribute("lang");
22730         }
22731         
22732         if (node.hasAttribute("style")) {
22733             
22734             var styles = node.getAttribute("style").split(";");
22735             var nstyle = [];
22736             Roo.each(styles, function(s) {
22737                 if (!s.match(/:/)) {
22738                     return;
22739                 }
22740                 var kv = s.split(":");
22741                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22742                     return;
22743                 }
22744                 // what ever is left... we allow.
22745                 nstyle.push(s);
22746             });
22747             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22748             if (!nstyle.length) {
22749                 node.removeAttribute('style');
22750             }
22751         }
22752         this.iterateChildren(node, this.cleanWord);
22753         
22754         
22755         
22756     },
22757     /**
22758      * iterateChildren of a Node, calling fn each time, using this as the scole..
22759      * @param {DomNode} node node to iterate children of.
22760      * @param {Function} fn method of this class to call on each item.
22761      */
22762     iterateChildren : function(node, fn)
22763     {
22764         if (!node.childNodes.length) {
22765                 return;
22766         }
22767         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22768            fn.call(this, node.childNodes[i])
22769         }
22770     },
22771     
22772     
22773     /**
22774      * cleanTableWidths.
22775      *
22776      * Quite often pasting from word etc.. results in tables with column and widths.
22777      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22778      *
22779      */
22780     cleanTableWidths : function(node)
22781     {
22782          
22783          
22784         if (!node) {
22785             this.cleanTableWidths(this.doc.body);
22786             return;
22787         }
22788         
22789         // ignore list...
22790         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22791             return; 
22792         }
22793         Roo.log(node.tagName);
22794         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22795             this.iterateChildren(node, this.cleanTableWidths);
22796             return;
22797         }
22798         if (node.hasAttribute('width')) {
22799             node.removeAttribute('width');
22800         }
22801         
22802          
22803         if (node.hasAttribute("style")) {
22804             // pretty basic...
22805             
22806             var styles = node.getAttribute("style").split(";");
22807             var nstyle = [];
22808             Roo.each(styles, function(s) {
22809                 if (!s.match(/:/)) {
22810                     return;
22811                 }
22812                 var kv = s.split(":");
22813                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22814                     return;
22815                 }
22816                 // what ever is left... we allow.
22817                 nstyle.push(s);
22818             });
22819             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22820             if (!nstyle.length) {
22821                 node.removeAttribute('style');
22822             }
22823         }
22824         
22825         this.iterateChildren(node, this.cleanTableWidths);
22826         
22827         
22828     },
22829     
22830     
22831     
22832     
22833     domToHTML : function(currentElement, depth, nopadtext) {
22834         
22835         depth = depth || 0;
22836         nopadtext = nopadtext || false;
22837     
22838         if (!currentElement) {
22839             return this.domToHTML(this.doc.body);
22840         }
22841         
22842         //Roo.log(currentElement);
22843         var j;
22844         var allText = false;
22845         var nodeName = currentElement.nodeName;
22846         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22847         
22848         if  (nodeName == '#text') {
22849             
22850             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22851         }
22852         
22853         
22854         var ret = '';
22855         if (nodeName != 'BODY') {
22856              
22857             var i = 0;
22858             // Prints the node tagName, such as <A>, <IMG>, etc
22859             if (tagName) {
22860                 var attr = [];
22861                 for(i = 0; i < currentElement.attributes.length;i++) {
22862                     // quoting?
22863                     var aname = currentElement.attributes.item(i).name;
22864                     if (!currentElement.attributes.item(i).value.length) {
22865                         continue;
22866                     }
22867                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22868                 }
22869                 
22870                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22871             } 
22872             else {
22873                 
22874                 // eack
22875             }
22876         } else {
22877             tagName = false;
22878         }
22879         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22880             return ret;
22881         }
22882         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22883             nopadtext = true;
22884         }
22885         
22886         
22887         // Traverse the tree
22888         i = 0;
22889         var currentElementChild = currentElement.childNodes.item(i);
22890         var allText = true;
22891         var innerHTML  = '';
22892         lastnode = '';
22893         while (currentElementChild) {
22894             // Formatting code (indent the tree so it looks nice on the screen)
22895             var nopad = nopadtext;
22896             if (lastnode == 'SPAN') {
22897                 nopad  = true;
22898             }
22899             // text
22900             if  (currentElementChild.nodeName == '#text') {
22901                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22902                 toadd = nopadtext ? toadd : toadd.trim();
22903                 if (!nopad && toadd.length > 80) {
22904                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22905                 }
22906                 innerHTML  += toadd;
22907                 
22908                 i++;
22909                 currentElementChild = currentElement.childNodes.item(i);
22910                 lastNode = '';
22911                 continue;
22912             }
22913             allText = false;
22914             
22915             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22916                 
22917             // Recursively traverse the tree structure of the child node
22918             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22919             lastnode = currentElementChild.nodeName;
22920             i++;
22921             currentElementChild=currentElement.childNodes.item(i);
22922         }
22923         
22924         ret += innerHTML;
22925         
22926         if (!allText) {
22927                 // The remaining code is mostly for formatting the tree
22928             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22929         }
22930         
22931         
22932         if (tagName) {
22933             ret+= "</"+tagName+">";
22934         }
22935         return ret;
22936         
22937     },
22938         
22939     applyBlacklists : function()
22940     {
22941         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22942         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22943         
22944         this.white = [];
22945         this.black = [];
22946         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22947             if (b.indexOf(tag) > -1) {
22948                 return;
22949             }
22950             this.white.push(tag);
22951             
22952         }, this);
22953         
22954         Roo.each(w, function(tag) {
22955             if (b.indexOf(tag) > -1) {
22956                 return;
22957             }
22958             if (this.white.indexOf(tag) > -1) {
22959                 return;
22960             }
22961             this.white.push(tag);
22962             
22963         }, this);
22964         
22965         
22966         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22967             if (w.indexOf(tag) > -1) {
22968                 return;
22969             }
22970             this.black.push(tag);
22971             
22972         }, this);
22973         
22974         Roo.each(b, function(tag) {
22975             if (w.indexOf(tag) > -1) {
22976                 return;
22977             }
22978             if (this.black.indexOf(tag) > -1) {
22979                 return;
22980             }
22981             this.black.push(tag);
22982             
22983         }, this);
22984         
22985         
22986         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22987         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22988         
22989         this.cwhite = [];
22990         this.cblack = [];
22991         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22992             if (b.indexOf(tag) > -1) {
22993                 return;
22994             }
22995             this.cwhite.push(tag);
22996             
22997         }, this);
22998         
22999         Roo.each(w, function(tag) {
23000             if (b.indexOf(tag) > -1) {
23001                 return;
23002             }
23003             if (this.cwhite.indexOf(tag) > -1) {
23004                 return;
23005             }
23006             this.cwhite.push(tag);
23007             
23008         }, this);
23009         
23010         
23011         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23012             if (w.indexOf(tag) > -1) {
23013                 return;
23014             }
23015             this.cblack.push(tag);
23016             
23017         }, this);
23018         
23019         Roo.each(b, function(tag) {
23020             if (w.indexOf(tag) > -1) {
23021                 return;
23022             }
23023             if (this.cblack.indexOf(tag) > -1) {
23024                 return;
23025             }
23026             this.cblack.push(tag);
23027             
23028         }, this);
23029     },
23030     
23031     setStylesheets : function(stylesheets)
23032     {
23033         if(typeof(stylesheets) == 'string'){
23034             Roo.get(this.iframe.contentDocument.head).createChild({
23035                 tag : 'link',
23036                 rel : 'stylesheet',
23037                 type : 'text/css',
23038                 href : stylesheets
23039             });
23040             
23041             return;
23042         }
23043         var _this = this;
23044      
23045         Roo.each(stylesheets, function(s) {
23046             if(!s.length){
23047                 return;
23048             }
23049             
23050             Roo.get(_this.iframe.contentDocument.head).createChild({
23051                 tag : 'link',
23052                 rel : 'stylesheet',
23053                 type : 'text/css',
23054                 href : s
23055             });
23056         });
23057
23058         
23059     },
23060     
23061     removeStylesheets : function()
23062     {
23063         var _this = this;
23064         
23065         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23066             s.remove();
23067         });
23068     },
23069     
23070     setStyle : function(style)
23071     {
23072         Roo.get(this.iframe.contentDocument.head).createChild({
23073             tag : 'style',
23074             type : 'text/css',
23075             html : style
23076         });
23077
23078         return;
23079     }
23080     
23081     // hide stuff that is not compatible
23082     /**
23083      * @event blur
23084      * @hide
23085      */
23086     /**
23087      * @event change
23088      * @hide
23089      */
23090     /**
23091      * @event focus
23092      * @hide
23093      */
23094     /**
23095      * @event specialkey
23096      * @hide
23097      */
23098     /**
23099      * @cfg {String} fieldClass @hide
23100      */
23101     /**
23102      * @cfg {String} focusClass @hide
23103      */
23104     /**
23105      * @cfg {String} autoCreate @hide
23106      */
23107     /**
23108      * @cfg {String} inputType @hide
23109      */
23110     /**
23111      * @cfg {String} invalidClass @hide
23112      */
23113     /**
23114      * @cfg {String} invalidText @hide
23115      */
23116     /**
23117      * @cfg {String} msgFx @hide
23118      */
23119     /**
23120      * @cfg {String} validateOnBlur @hide
23121      */
23122 });
23123
23124 Roo.HtmlEditorCore.white = [
23125         'area', 'br', 'img', 'input', 'hr', 'wbr',
23126         
23127        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23128        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23129        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23130        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23131        'table',   'ul',         'xmp', 
23132        
23133        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23134       'thead',   'tr', 
23135      
23136       'dir', 'menu', 'ol', 'ul', 'dl',
23137        
23138       'embed',  'object'
23139 ];
23140
23141
23142 Roo.HtmlEditorCore.black = [
23143     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23144         'applet', // 
23145         'base',   'basefont', 'bgsound', 'blink',  'body', 
23146         'frame',  'frameset', 'head',    'html',   'ilayer', 
23147         'iframe', 'layer',  'link',     'meta',    'object',   
23148         'script', 'style' ,'title',  'xml' // clean later..
23149 ];
23150 Roo.HtmlEditorCore.clean = [
23151     'script', 'style', 'title', 'xml'
23152 ];
23153 Roo.HtmlEditorCore.remove = [
23154     'font'
23155 ];
23156 // attributes..
23157
23158 Roo.HtmlEditorCore.ablack = [
23159     'on'
23160 ];
23161     
23162 Roo.HtmlEditorCore.aclean = [ 
23163     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23164 ];
23165
23166 // protocols..
23167 Roo.HtmlEditorCore.pwhite= [
23168         'http',  'https',  'mailto'
23169 ];
23170
23171 // white listed style attributes.
23172 Roo.HtmlEditorCore.cwhite= [
23173       //  'text-align', /// default is to allow most things..
23174       
23175          
23176 //        'font-size'//??
23177 ];
23178
23179 // black listed style attributes.
23180 Roo.HtmlEditorCore.cblack= [
23181       //  'font-size' -- this can be set by the project 
23182 ];
23183
23184
23185 Roo.HtmlEditorCore.swapCodes   =[ 
23186     [    8211, "--" ], 
23187     [    8212, "--" ], 
23188     [    8216,  "'" ],  
23189     [    8217, "'" ],  
23190     [    8220, '"' ],  
23191     [    8221, '"' ],  
23192     [    8226, "*" ],  
23193     [    8230, "..." ]
23194 ]; 
23195
23196     /*
23197  * - LGPL
23198  *
23199  * HtmlEditor
23200  * 
23201  */
23202
23203 /**
23204  * @class Roo.bootstrap.HtmlEditor
23205  * @extends Roo.bootstrap.TextArea
23206  * Bootstrap HtmlEditor class
23207
23208  * @constructor
23209  * Create a new HtmlEditor
23210  * @param {Object} config The config object
23211  */
23212
23213 Roo.bootstrap.HtmlEditor = function(config){
23214     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23215     if (!this.toolbars) {
23216         this.toolbars = [];
23217     }
23218     
23219     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23220     this.addEvents({
23221             /**
23222              * @event initialize
23223              * Fires when the editor is fully initialized (including the iframe)
23224              * @param {HtmlEditor} this
23225              */
23226             initialize: true,
23227             /**
23228              * @event activate
23229              * Fires when the editor is first receives the focus. Any insertion must wait
23230              * until after this event.
23231              * @param {HtmlEditor} this
23232              */
23233             activate: true,
23234              /**
23235              * @event beforesync
23236              * Fires before the textarea is updated with content from the editor iframe. Return false
23237              * to cancel the sync.
23238              * @param {HtmlEditor} this
23239              * @param {String} html
23240              */
23241             beforesync: true,
23242              /**
23243              * @event beforepush
23244              * Fires before the iframe editor is updated with content from the textarea. Return false
23245              * to cancel the push.
23246              * @param {HtmlEditor} this
23247              * @param {String} html
23248              */
23249             beforepush: true,
23250              /**
23251              * @event sync
23252              * Fires when the textarea is updated with content from the editor iframe.
23253              * @param {HtmlEditor} this
23254              * @param {String} html
23255              */
23256             sync: true,
23257              /**
23258              * @event push
23259              * Fires when the iframe editor is updated with content from the textarea.
23260              * @param {HtmlEditor} this
23261              * @param {String} html
23262              */
23263             push: true,
23264              /**
23265              * @event editmodechange
23266              * Fires when the editor switches edit modes
23267              * @param {HtmlEditor} this
23268              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23269              */
23270             editmodechange: true,
23271             /**
23272              * @event editorevent
23273              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23274              * @param {HtmlEditor} this
23275              */
23276             editorevent: true,
23277             /**
23278              * @event firstfocus
23279              * Fires when on first focus - needed by toolbars..
23280              * @param {HtmlEditor} this
23281              */
23282             firstfocus: true,
23283             /**
23284              * @event autosave
23285              * Auto save the htmlEditor value as a file into Events
23286              * @param {HtmlEditor} this
23287              */
23288             autosave: true,
23289             /**
23290              * @event savedpreview
23291              * preview the saved version of htmlEditor
23292              * @param {HtmlEditor} this
23293              */
23294             savedpreview: true
23295         });
23296 };
23297
23298
23299 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23300     
23301     
23302       /**
23303      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23304      */
23305     toolbars : false,
23306     
23307      /**
23308     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23309     */
23310     btns : [],
23311    
23312      /**
23313      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23314      *                        Roo.resizable.
23315      */
23316     resizable : false,
23317      /**
23318      * @cfg {Number} height (in pixels)
23319      */   
23320     height: 300,
23321    /**
23322      * @cfg {Number} width (in pixels)
23323      */   
23324     width: false,
23325     
23326     /**
23327      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23328      * 
23329      */
23330     stylesheets: false,
23331     
23332     // id of frame..
23333     frameId: false,
23334     
23335     // private properties
23336     validationEvent : false,
23337     deferHeight: true,
23338     initialized : false,
23339     activated : false,
23340     
23341     onFocus : Roo.emptyFn,
23342     iframePad:3,
23343     hideMode:'offsets',
23344     
23345     tbContainer : false,
23346     
23347     bodyCls : '',
23348     
23349     toolbarContainer :function() {
23350         return this.wrap.select('.x-html-editor-tb',true).first();
23351     },
23352
23353     /**
23354      * Protected method that will not generally be called directly. It
23355      * is called when the editor creates its toolbar. Override this method if you need to
23356      * add custom toolbar buttons.
23357      * @param {HtmlEditor} editor
23358      */
23359     createToolbar : function(){
23360         Roo.log('renewing');
23361         Roo.log("create toolbars");
23362         
23363         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23364         this.toolbars[0].render(this.toolbarContainer());
23365         
23366         return;
23367         
23368 //        if (!editor.toolbars || !editor.toolbars.length) {
23369 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23370 //        }
23371 //        
23372 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23373 //            editor.toolbars[i] = Roo.factory(
23374 //                    typeof(editor.toolbars[i]) == 'string' ?
23375 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23376 //                Roo.bootstrap.HtmlEditor);
23377 //            editor.toolbars[i].init(editor);
23378 //        }
23379     },
23380
23381      
23382     // private
23383     onRender : function(ct, position)
23384     {
23385        // Roo.log("Call onRender: " + this.xtype);
23386         var _t = this;
23387         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23388       
23389         this.wrap = this.inputEl().wrap({
23390             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23391         });
23392         
23393         this.editorcore.onRender(ct, position);
23394          
23395         if (this.resizable) {
23396             this.resizeEl = new Roo.Resizable(this.wrap, {
23397                 pinned : true,
23398                 wrap: true,
23399                 dynamic : true,
23400                 minHeight : this.height,
23401                 height: this.height,
23402                 handles : this.resizable,
23403                 width: this.width,
23404                 listeners : {
23405                     resize : function(r, w, h) {
23406                         _t.onResize(w,h); // -something
23407                     }
23408                 }
23409             });
23410             
23411         }
23412         this.createToolbar(this);
23413        
23414         
23415         if(!this.width && this.resizable){
23416             this.setSize(this.wrap.getSize());
23417         }
23418         if (this.resizeEl) {
23419             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23420             // should trigger onReize..
23421         }
23422         
23423     },
23424
23425     // private
23426     onResize : function(w, h)
23427     {
23428         Roo.log('resize: ' +w + ',' + h );
23429         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23430         var ew = false;
23431         var eh = false;
23432         
23433         if(this.inputEl() ){
23434             if(typeof w == 'number'){
23435                 var aw = w - this.wrap.getFrameWidth('lr');
23436                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23437                 ew = aw;
23438             }
23439             if(typeof h == 'number'){
23440                  var tbh = -11;  // fixme it needs to tool bar size!
23441                 for (var i =0; i < this.toolbars.length;i++) {
23442                     // fixme - ask toolbars for heights?
23443                     tbh += this.toolbars[i].el.getHeight();
23444                     //if (this.toolbars[i].footer) {
23445                     //    tbh += this.toolbars[i].footer.el.getHeight();
23446                     //}
23447                 }
23448               
23449                 
23450                 
23451                 
23452                 
23453                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23454                 ah -= 5; // knock a few pixes off for look..
23455                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23456                 var eh = ah;
23457             }
23458         }
23459         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23460         this.editorcore.onResize(ew,eh);
23461         
23462     },
23463
23464     /**
23465      * Toggles the editor between standard and source edit mode.
23466      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23467      */
23468     toggleSourceEdit : function(sourceEditMode)
23469     {
23470         this.editorcore.toggleSourceEdit(sourceEditMode);
23471         
23472         if(this.editorcore.sourceEditMode){
23473             Roo.log('editor - showing textarea');
23474             
23475 //            Roo.log('in');
23476 //            Roo.log(this.syncValue());
23477             this.syncValue();
23478             this.inputEl().removeClass(['hide', 'x-hidden']);
23479             this.inputEl().dom.removeAttribute('tabIndex');
23480             this.inputEl().focus();
23481         }else{
23482             Roo.log('editor - hiding textarea');
23483 //            Roo.log('out')
23484 //            Roo.log(this.pushValue()); 
23485             this.pushValue();
23486             
23487             this.inputEl().addClass(['hide', 'x-hidden']);
23488             this.inputEl().dom.setAttribute('tabIndex', -1);
23489             //this.deferFocus();
23490         }
23491          
23492         if(this.resizable){
23493             this.setSize(this.wrap.getSize());
23494         }
23495         
23496         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23497     },
23498  
23499     // private (for BoxComponent)
23500     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23501
23502     // private (for BoxComponent)
23503     getResizeEl : function(){
23504         return this.wrap;
23505     },
23506
23507     // private (for BoxComponent)
23508     getPositionEl : function(){
23509         return this.wrap;
23510     },
23511
23512     // private
23513     initEvents : function(){
23514         this.originalValue = this.getValue();
23515     },
23516
23517 //    /**
23518 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23519 //     * @method
23520 //     */
23521 //    markInvalid : Roo.emptyFn,
23522 //    /**
23523 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23524 //     * @method
23525 //     */
23526 //    clearInvalid : Roo.emptyFn,
23527
23528     setValue : function(v){
23529         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23530         this.editorcore.pushValue();
23531     },
23532
23533      
23534     // private
23535     deferFocus : function(){
23536         this.focus.defer(10, this);
23537     },
23538
23539     // doc'ed in Field
23540     focus : function(){
23541         this.editorcore.focus();
23542         
23543     },
23544       
23545
23546     // private
23547     onDestroy : function(){
23548         
23549         
23550         
23551         if(this.rendered){
23552             
23553             for (var i =0; i < this.toolbars.length;i++) {
23554                 // fixme - ask toolbars for heights?
23555                 this.toolbars[i].onDestroy();
23556             }
23557             
23558             this.wrap.dom.innerHTML = '';
23559             this.wrap.remove();
23560         }
23561     },
23562
23563     // private
23564     onFirstFocus : function(){
23565         //Roo.log("onFirstFocus");
23566         this.editorcore.onFirstFocus();
23567          for (var i =0; i < this.toolbars.length;i++) {
23568             this.toolbars[i].onFirstFocus();
23569         }
23570         
23571     },
23572     
23573     // private
23574     syncValue : function()
23575     {   
23576         this.editorcore.syncValue();
23577     },
23578     
23579     pushValue : function()
23580     {   
23581         this.editorcore.pushValue();
23582     }
23583      
23584     
23585     // hide stuff that is not compatible
23586     /**
23587      * @event blur
23588      * @hide
23589      */
23590     /**
23591      * @event change
23592      * @hide
23593      */
23594     /**
23595      * @event focus
23596      * @hide
23597      */
23598     /**
23599      * @event specialkey
23600      * @hide
23601      */
23602     /**
23603      * @cfg {String} fieldClass @hide
23604      */
23605     /**
23606      * @cfg {String} focusClass @hide
23607      */
23608     /**
23609      * @cfg {String} autoCreate @hide
23610      */
23611     /**
23612      * @cfg {String} inputType @hide
23613      */
23614     /**
23615      * @cfg {String} invalidClass @hide
23616      */
23617     /**
23618      * @cfg {String} invalidText @hide
23619      */
23620     /**
23621      * @cfg {String} msgFx @hide
23622      */
23623     /**
23624      * @cfg {String} validateOnBlur @hide
23625      */
23626 });
23627  
23628     
23629    
23630    
23631    
23632       
23633 Roo.namespace('Roo.bootstrap.htmleditor');
23634 /**
23635  * @class Roo.bootstrap.HtmlEditorToolbar1
23636  * Basic Toolbar
23637  * 
23638  * Usage:
23639  *
23640  new Roo.bootstrap.HtmlEditor({
23641     ....
23642     toolbars : [
23643         new Roo.bootstrap.HtmlEditorToolbar1({
23644             disable : { fonts: 1 , format: 1, ..., ... , ...],
23645             btns : [ .... ]
23646         })
23647     }
23648      
23649  * 
23650  * @cfg {Object} disable List of elements to disable..
23651  * @cfg {Array} btns List of additional buttons.
23652  * 
23653  * 
23654  * NEEDS Extra CSS? 
23655  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23656  */
23657  
23658 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23659 {
23660     
23661     Roo.apply(this, config);
23662     
23663     // default disabled, based on 'good practice'..
23664     this.disable = this.disable || {};
23665     Roo.applyIf(this.disable, {
23666         fontSize : true,
23667         colors : true,
23668         specialElements : true
23669     });
23670     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23671     
23672     this.editor = config.editor;
23673     this.editorcore = config.editor.editorcore;
23674     
23675     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23676     
23677     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23678     // dont call parent... till later.
23679 }
23680 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23681      
23682     bar : true,
23683     
23684     editor : false,
23685     editorcore : false,
23686     
23687     
23688     formats : [
23689         "p" ,  
23690         "h1","h2","h3","h4","h5","h6", 
23691         "pre", "code", 
23692         "abbr", "acronym", "address", "cite", "samp", "var",
23693         'div','span'
23694     ],
23695     
23696     onRender : function(ct, position)
23697     {
23698        // Roo.log("Call onRender: " + this.xtype);
23699         
23700        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23701        Roo.log(this.el);
23702        this.el.dom.style.marginBottom = '0';
23703        var _this = this;
23704        var editorcore = this.editorcore;
23705        var editor= this.editor;
23706        
23707        var children = [];
23708        var btn = function(id,cmd , toggle, handler, html){
23709        
23710             var  event = toggle ? 'toggle' : 'click';
23711        
23712             var a = {
23713                 size : 'sm',
23714                 xtype: 'Button',
23715                 xns: Roo.bootstrap,
23716                 glyphicon : id,
23717                 cmd : id || cmd,
23718                 enableToggle:toggle !== false,
23719                 html : html || '',
23720                 pressed : toggle ? false : null,
23721                 listeners : {}
23722             };
23723             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23724                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23725             };
23726             children.push(a);
23727             return a;
23728        }
23729        
23730     //    var cb_box = function...
23731         
23732         var style = {
23733                 xtype: 'Button',
23734                 size : 'sm',
23735                 xns: Roo.bootstrap,
23736                 glyphicon : 'font',
23737                 //html : 'submit'
23738                 menu : {
23739                     xtype: 'Menu',
23740                     xns: Roo.bootstrap,
23741                     items:  []
23742                 }
23743         };
23744         Roo.each(this.formats, function(f) {
23745             style.menu.items.push({
23746                 xtype :'MenuItem',
23747                 xns: Roo.bootstrap,
23748                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23749                 tagname : f,
23750                 listeners : {
23751                     click : function()
23752                     {
23753                         editorcore.insertTag(this.tagname);
23754                         editor.focus();
23755                     }
23756                 }
23757                 
23758             });
23759         });
23760         children.push(style);   
23761         
23762         btn('bold',false,true);
23763         btn('italic',false,true);
23764         btn('align-left', 'justifyleft',true);
23765         btn('align-center', 'justifycenter',true);
23766         btn('align-right' , 'justifyright',true);
23767         btn('link', false, false, function(btn) {
23768             //Roo.log("create link?");
23769             var url = prompt(this.createLinkText, this.defaultLinkValue);
23770             if(url && url != 'http:/'+'/'){
23771                 this.editorcore.relayCmd('createlink', url);
23772             }
23773         }),
23774         btn('list','insertunorderedlist',true);
23775         btn('pencil', false,true, function(btn){
23776                 Roo.log(this);
23777                 this.toggleSourceEdit(btn.pressed);
23778         });
23779         
23780         if (this.editor.btns.length > 0) {
23781             for (var i = 0; i<this.editor.btns.length; i++) {
23782                 children.push(this.editor.btns[i]);
23783             }
23784         }
23785         
23786         /*
23787         var cog = {
23788                 xtype: 'Button',
23789                 size : 'sm',
23790                 xns: Roo.bootstrap,
23791                 glyphicon : 'cog',
23792                 //html : 'submit'
23793                 menu : {
23794                     xtype: 'Menu',
23795                     xns: Roo.bootstrap,
23796                     items:  []
23797                 }
23798         };
23799         
23800         cog.menu.items.push({
23801             xtype :'MenuItem',
23802             xns: Roo.bootstrap,
23803             html : Clean styles,
23804             tagname : f,
23805             listeners : {
23806                 click : function()
23807                 {
23808                     editorcore.insertTag(this.tagname);
23809                     editor.focus();
23810                 }
23811             }
23812             
23813         });
23814        */
23815         
23816          
23817        this.xtype = 'NavSimplebar';
23818         
23819         for(var i=0;i< children.length;i++) {
23820             
23821             this.buttons.add(this.addxtypeChild(children[i]));
23822             
23823         }
23824         
23825         editor.on('editorevent', this.updateToolbar, this);
23826     },
23827     onBtnClick : function(id)
23828     {
23829        this.editorcore.relayCmd(id);
23830        this.editorcore.focus();
23831     },
23832     
23833     /**
23834      * Protected method that will not generally be called directly. It triggers
23835      * a toolbar update by reading the markup state of the current selection in the editor.
23836      */
23837     updateToolbar: function(){
23838
23839         if(!this.editorcore.activated){
23840             this.editor.onFirstFocus(); // is this neeed?
23841             return;
23842         }
23843
23844         var btns = this.buttons; 
23845         var doc = this.editorcore.doc;
23846         btns.get('bold').setActive(doc.queryCommandState('bold'));
23847         btns.get('italic').setActive(doc.queryCommandState('italic'));
23848         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23849         
23850         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23851         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23852         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23853         
23854         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23855         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23856          /*
23857         
23858         var ans = this.editorcore.getAllAncestors();
23859         if (this.formatCombo) {
23860             
23861             
23862             var store = this.formatCombo.store;
23863             this.formatCombo.setValue("");
23864             for (var i =0; i < ans.length;i++) {
23865                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23866                     // select it..
23867                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23868                     break;
23869                 }
23870             }
23871         }
23872         
23873         
23874         
23875         // hides menus... - so this cant be on a menu...
23876         Roo.bootstrap.MenuMgr.hideAll();
23877         */
23878         Roo.bootstrap.MenuMgr.hideAll();
23879         //this.editorsyncValue();
23880     },
23881     onFirstFocus: function() {
23882         this.buttons.each(function(item){
23883            item.enable();
23884         });
23885     },
23886     toggleSourceEdit : function(sourceEditMode){
23887         
23888           
23889         if(sourceEditMode){
23890             Roo.log("disabling buttons");
23891            this.buttons.each( function(item){
23892                 if(item.cmd != 'pencil'){
23893                     item.disable();
23894                 }
23895             });
23896           
23897         }else{
23898             Roo.log("enabling buttons");
23899             if(this.editorcore.initialized){
23900                 this.buttons.each( function(item){
23901                     item.enable();
23902                 });
23903             }
23904             
23905         }
23906         Roo.log("calling toggole on editor");
23907         // tell the editor that it's been pressed..
23908         this.editor.toggleSourceEdit(sourceEditMode);
23909        
23910     }
23911 });
23912
23913
23914
23915
23916
23917 /**
23918  * @class Roo.bootstrap.Table.AbstractSelectionModel
23919  * @extends Roo.util.Observable
23920  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23921  * implemented by descendant classes.  This class should not be directly instantiated.
23922  * @constructor
23923  */
23924 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23925     this.locked = false;
23926     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23927 };
23928
23929
23930 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23931     /** @ignore Called by the grid automatically. Do not call directly. */
23932     init : function(grid){
23933         this.grid = grid;
23934         this.initEvents();
23935     },
23936
23937     /**
23938      * Locks the selections.
23939      */
23940     lock : function(){
23941         this.locked = true;
23942     },
23943
23944     /**
23945      * Unlocks the selections.
23946      */
23947     unlock : function(){
23948         this.locked = false;
23949     },
23950
23951     /**
23952      * Returns true if the selections are locked.
23953      * @return {Boolean}
23954      */
23955     isLocked : function(){
23956         return this.locked;
23957     }
23958 });
23959 /**
23960  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23961  * @class Roo.bootstrap.Table.RowSelectionModel
23962  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23963  * It supports multiple selections and keyboard selection/navigation. 
23964  * @constructor
23965  * @param {Object} config
23966  */
23967
23968 Roo.bootstrap.Table.RowSelectionModel = function(config){
23969     Roo.apply(this, config);
23970     this.selections = new Roo.util.MixedCollection(false, function(o){
23971         return o.id;
23972     });
23973
23974     this.last = false;
23975     this.lastActive = false;
23976
23977     this.addEvents({
23978         /**
23979              * @event selectionchange
23980              * Fires when the selection changes
23981              * @param {SelectionModel} this
23982              */
23983             "selectionchange" : true,
23984         /**
23985              * @event afterselectionchange
23986              * Fires after the selection changes (eg. by key press or clicking)
23987              * @param {SelectionModel} this
23988              */
23989             "afterselectionchange" : true,
23990         /**
23991              * @event beforerowselect
23992              * Fires when a row is selected being selected, return false to cancel.
23993              * @param {SelectionModel} this
23994              * @param {Number} rowIndex The selected index
23995              * @param {Boolean} keepExisting False if other selections will be cleared
23996              */
23997             "beforerowselect" : true,
23998         /**
23999              * @event rowselect
24000              * Fires when a row is selected.
24001              * @param {SelectionModel} this
24002              * @param {Number} rowIndex The selected index
24003              * @param {Roo.data.Record} r The record
24004              */
24005             "rowselect" : true,
24006         /**
24007              * @event rowdeselect
24008              * Fires when a row is deselected.
24009              * @param {SelectionModel} this
24010              * @param {Number} rowIndex The selected index
24011              */
24012         "rowdeselect" : true
24013     });
24014     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24015     this.locked = false;
24016  };
24017
24018 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24019     /**
24020      * @cfg {Boolean} singleSelect
24021      * True to allow selection of only one row at a time (defaults to false)
24022      */
24023     singleSelect : false,
24024
24025     // private
24026     initEvents : function()
24027     {
24028
24029         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24030         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24031         //}else{ // allow click to work like normal
24032          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24033         //}
24034         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24035         this.grid.on("rowclick", this.handleMouseDown, this);
24036         
24037         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24038             "up" : function(e){
24039                 if(!e.shiftKey){
24040                     this.selectPrevious(e.shiftKey);
24041                 }else if(this.last !== false && this.lastActive !== false){
24042                     var last = this.last;
24043                     this.selectRange(this.last,  this.lastActive-1);
24044                     this.grid.getView().focusRow(this.lastActive);
24045                     if(last !== false){
24046                         this.last = last;
24047                     }
24048                 }else{
24049                     this.selectFirstRow();
24050                 }
24051                 this.fireEvent("afterselectionchange", this);
24052             },
24053             "down" : function(e){
24054                 if(!e.shiftKey){
24055                     this.selectNext(e.shiftKey);
24056                 }else if(this.last !== false && this.lastActive !== false){
24057                     var last = this.last;
24058                     this.selectRange(this.last,  this.lastActive+1);
24059                     this.grid.getView().focusRow(this.lastActive);
24060                     if(last !== false){
24061                         this.last = last;
24062                     }
24063                 }else{
24064                     this.selectFirstRow();
24065                 }
24066                 this.fireEvent("afterselectionchange", this);
24067             },
24068             scope: this
24069         });
24070         this.grid.store.on('load', function(){
24071             this.selections.clear();
24072         },this);
24073         /*
24074         var view = this.grid.view;
24075         view.on("refresh", this.onRefresh, this);
24076         view.on("rowupdated", this.onRowUpdated, this);
24077         view.on("rowremoved", this.onRemove, this);
24078         */
24079     },
24080
24081     // private
24082     onRefresh : function()
24083     {
24084         var ds = this.grid.store, i, v = this.grid.view;
24085         var s = this.selections;
24086         s.each(function(r){
24087             if((i = ds.indexOfId(r.id)) != -1){
24088                 v.onRowSelect(i);
24089             }else{
24090                 s.remove(r);
24091             }
24092         });
24093     },
24094
24095     // private
24096     onRemove : function(v, index, r){
24097         this.selections.remove(r);
24098     },
24099
24100     // private
24101     onRowUpdated : function(v, index, r){
24102         if(this.isSelected(r)){
24103             v.onRowSelect(index);
24104         }
24105     },
24106
24107     /**
24108      * Select records.
24109      * @param {Array} records The records to select
24110      * @param {Boolean} keepExisting (optional) True to keep existing selections
24111      */
24112     selectRecords : function(records, keepExisting)
24113     {
24114         if(!keepExisting){
24115             this.clearSelections();
24116         }
24117             var ds = this.grid.store;
24118         for(var i = 0, len = records.length; i < len; i++){
24119             this.selectRow(ds.indexOf(records[i]), true);
24120         }
24121     },
24122
24123     /**
24124      * Gets the number of selected rows.
24125      * @return {Number}
24126      */
24127     getCount : function(){
24128         return this.selections.length;
24129     },
24130
24131     /**
24132      * Selects the first row in the grid.
24133      */
24134     selectFirstRow : function(){
24135         this.selectRow(0);
24136     },
24137
24138     /**
24139      * Select the last row.
24140      * @param {Boolean} keepExisting (optional) True to keep existing selections
24141      */
24142     selectLastRow : function(keepExisting){
24143         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24144         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24145     },
24146
24147     /**
24148      * Selects the row immediately following the last selected row.
24149      * @param {Boolean} keepExisting (optional) True to keep existing selections
24150      */
24151     selectNext : function(keepExisting)
24152     {
24153             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24154             this.selectRow(this.last+1, keepExisting);
24155             this.grid.getView().focusRow(this.last);
24156         }
24157     },
24158
24159     /**
24160      * Selects the row that precedes the last selected row.
24161      * @param {Boolean} keepExisting (optional) True to keep existing selections
24162      */
24163     selectPrevious : function(keepExisting){
24164         if(this.last){
24165             this.selectRow(this.last-1, keepExisting);
24166             this.grid.getView().focusRow(this.last);
24167         }
24168     },
24169
24170     /**
24171      * Returns the selected records
24172      * @return {Array} Array of selected records
24173      */
24174     getSelections : function(){
24175         return [].concat(this.selections.items);
24176     },
24177
24178     /**
24179      * Returns the first selected record.
24180      * @return {Record}
24181      */
24182     getSelected : function(){
24183         return this.selections.itemAt(0);
24184     },
24185
24186
24187     /**
24188      * Clears all selections.
24189      */
24190     clearSelections : function(fast)
24191     {
24192         if(this.locked) {
24193             return;
24194         }
24195         if(fast !== true){
24196                 var ds = this.grid.store;
24197             var s = this.selections;
24198             s.each(function(r){
24199                 this.deselectRow(ds.indexOfId(r.id));
24200             }, this);
24201             s.clear();
24202         }else{
24203             this.selections.clear();
24204         }
24205         this.last = false;
24206     },
24207
24208
24209     /**
24210      * Selects all rows.
24211      */
24212     selectAll : function(){
24213         if(this.locked) {
24214             return;
24215         }
24216         this.selections.clear();
24217         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24218             this.selectRow(i, true);
24219         }
24220     },
24221
24222     /**
24223      * Returns True if there is a selection.
24224      * @return {Boolean}
24225      */
24226     hasSelection : function(){
24227         return this.selections.length > 0;
24228     },
24229
24230     /**
24231      * Returns True if the specified row is selected.
24232      * @param {Number/Record} record The record or index of the record to check
24233      * @return {Boolean}
24234      */
24235     isSelected : function(index){
24236             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24237         return (r && this.selections.key(r.id) ? true : false);
24238     },
24239
24240     /**
24241      * Returns True if the specified record id is selected.
24242      * @param {String} id The id of record to check
24243      * @return {Boolean}
24244      */
24245     isIdSelected : function(id){
24246         return (this.selections.key(id) ? true : false);
24247     },
24248
24249
24250     // private
24251     handleMouseDBClick : function(e, t){
24252         
24253     },
24254     // private
24255     handleMouseDown : function(e, t)
24256     {
24257             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24258         if(this.isLocked() || rowIndex < 0 ){
24259             return;
24260         };
24261         if(e.shiftKey && this.last !== false){
24262             var last = this.last;
24263             this.selectRange(last, rowIndex, e.ctrlKey);
24264             this.last = last; // reset the last
24265             t.focus();
24266     
24267         }else{
24268             var isSelected = this.isSelected(rowIndex);
24269             //Roo.log("select row:" + rowIndex);
24270             if(isSelected){
24271                 this.deselectRow(rowIndex);
24272             } else {
24273                         this.selectRow(rowIndex, true);
24274             }
24275     
24276             /*
24277                 if(e.button !== 0 && isSelected){
24278                 alert('rowIndex 2: ' + rowIndex);
24279                     view.focusRow(rowIndex);
24280                 }else if(e.ctrlKey && isSelected){
24281                     this.deselectRow(rowIndex);
24282                 }else if(!isSelected){
24283                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24284                     view.focusRow(rowIndex);
24285                 }
24286             */
24287         }
24288         this.fireEvent("afterselectionchange", this);
24289     },
24290     // private
24291     handleDragableRowClick :  function(grid, rowIndex, e) 
24292     {
24293         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24294             this.selectRow(rowIndex, false);
24295             grid.view.focusRow(rowIndex);
24296              this.fireEvent("afterselectionchange", this);
24297         }
24298     },
24299     
24300     /**
24301      * Selects multiple rows.
24302      * @param {Array} rows Array of the indexes of the row to select
24303      * @param {Boolean} keepExisting (optional) True to keep existing selections
24304      */
24305     selectRows : function(rows, keepExisting){
24306         if(!keepExisting){
24307             this.clearSelections();
24308         }
24309         for(var i = 0, len = rows.length; i < len; i++){
24310             this.selectRow(rows[i], true);
24311         }
24312     },
24313
24314     /**
24315      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24316      * @param {Number} startRow The index of the first row in the range
24317      * @param {Number} endRow The index of the last row in the range
24318      * @param {Boolean} keepExisting (optional) True to retain existing selections
24319      */
24320     selectRange : function(startRow, endRow, keepExisting){
24321         if(this.locked) {
24322             return;
24323         }
24324         if(!keepExisting){
24325             this.clearSelections();
24326         }
24327         if(startRow <= endRow){
24328             for(var i = startRow; i <= endRow; i++){
24329                 this.selectRow(i, true);
24330             }
24331         }else{
24332             for(var i = startRow; i >= endRow; i--){
24333                 this.selectRow(i, true);
24334             }
24335         }
24336     },
24337
24338     /**
24339      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24340      * @param {Number} startRow The index of the first row in the range
24341      * @param {Number} endRow The index of the last row in the range
24342      */
24343     deselectRange : function(startRow, endRow, preventViewNotify){
24344         if(this.locked) {
24345             return;
24346         }
24347         for(var i = startRow; i <= endRow; i++){
24348             this.deselectRow(i, preventViewNotify);
24349         }
24350     },
24351
24352     /**
24353      * Selects a row.
24354      * @param {Number} row The index of the row to select
24355      * @param {Boolean} keepExisting (optional) True to keep existing selections
24356      */
24357     selectRow : function(index, keepExisting, preventViewNotify)
24358     {
24359             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24360             return;
24361         }
24362         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24363             if(!keepExisting || this.singleSelect){
24364                 this.clearSelections();
24365             }
24366             
24367             var r = this.grid.store.getAt(index);
24368             //console.log('selectRow - record id :' + r.id);
24369             
24370             this.selections.add(r);
24371             this.last = this.lastActive = index;
24372             if(!preventViewNotify){
24373                 var proxy = new Roo.Element(
24374                                 this.grid.getRowDom(index)
24375                 );
24376                 proxy.addClass('bg-info info');
24377             }
24378             this.fireEvent("rowselect", this, index, r);
24379             this.fireEvent("selectionchange", this);
24380         }
24381     },
24382
24383     /**
24384      * Deselects a row.
24385      * @param {Number} row The index of the row to deselect
24386      */
24387     deselectRow : function(index, preventViewNotify)
24388     {
24389         if(this.locked) {
24390             return;
24391         }
24392         if(this.last == index){
24393             this.last = false;
24394         }
24395         if(this.lastActive == index){
24396             this.lastActive = false;
24397         }
24398         
24399         var r = this.grid.store.getAt(index);
24400         if (!r) {
24401             return;
24402         }
24403         
24404         this.selections.remove(r);
24405         //.console.log('deselectRow - record id :' + r.id);
24406         if(!preventViewNotify){
24407         
24408             var proxy = new Roo.Element(
24409                 this.grid.getRowDom(index)
24410             );
24411             proxy.removeClass('bg-info info');
24412         }
24413         this.fireEvent("rowdeselect", this, index);
24414         this.fireEvent("selectionchange", this);
24415     },
24416
24417     // private
24418     restoreLast : function(){
24419         if(this._last){
24420             this.last = this._last;
24421         }
24422     },
24423
24424     // private
24425     acceptsNav : function(row, col, cm){
24426         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24427     },
24428
24429     // private
24430     onEditorKey : function(field, e){
24431         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24432         if(k == e.TAB){
24433             e.stopEvent();
24434             ed.completeEdit();
24435             if(e.shiftKey){
24436                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24437             }else{
24438                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24439             }
24440         }else if(k == e.ENTER && !e.ctrlKey){
24441             e.stopEvent();
24442             ed.completeEdit();
24443             if(e.shiftKey){
24444                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24445             }else{
24446                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24447             }
24448         }else if(k == e.ESC){
24449             ed.cancelEdit();
24450         }
24451         if(newCell){
24452             g.startEditing(newCell[0], newCell[1]);
24453         }
24454     }
24455 });
24456 /*
24457  * Based on:
24458  * Ext JS Library 1.1.1
24459  * Copyright(c) 2006-2007, Ext JS, LLC.
24460  *
24461  * Originally Released Under LGPL - original licence link has changed is not relivant.
24462  *
24463  * Fork - LGPL
24464  * <script type="text/javascript">
24465  */
24466  
24467 /**
24468  * @class Roo.bootstrap.PagingToolbar
24469  * @extends Roo.bootstrap.NavSimplebar
24470  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24471  * @constructor
24472  * Create a new PagingToolbar
24473  * @param {Object} config The config object
24474  * @param {Roo.data.Store} store
24475  */
24476 Roo.bootstrap.PagingToolbar = function(config)
24477 {
24478     // old args format still supported... - xtype is prefered..
24479         // created from xtype...
24480     
24481     this.ds = config.dataSource;
24482     
24483     if (config.store && !this.ds) {
24484         this.store= Roo.factory(config.store, Roo.data);
24485         this.ds = this.store;
24486         this.ds.xmodule = this.xmodule || false;
24487     }
24488     
24489     this.toolbarItems = [];
24490     if (config.items) {
24491         this.toolbarItems = config.items;
24492     }
24493     
24494     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24495     
24496     this.cursor = 0;
24497     
24498     if (this.ds) { 
24499         this.bind(this.ds);
24500     }
24501     
24502     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24503     
24504 };
24505
24506 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24507     /**
24508      * @cfg {Roo.data.Store} dataSource
24509      * The underlying data store providing the paged data
24510      */
24511     /**
24512      * @cfg {String/HTMLElement/Element} container
24513      * container The id or element that will contain the toolbar
24514      */
24515     /**
24516      * @cfg {Boolean} displayInfo
24517      * True to display the displayMsg (defaults to false)
24518      */
24519     /**
24520      * @cfg {Number} pageSize
24521      * The number of records to display per page (defaults to 20)
24522      */
24523     pageSize: 20,
24524     /**
24525      * @cfg {String} displayMsg
24526      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24527      */
24528     displayMsg : 'Displaying {0} - {1} of {2}',
24529     /**
24530      * @cfg {String} emptyMsg
24531      * The message to display when no records are found (defaults to "No data to display")
24532      */
24533     emptyMsg : 'No data to display',
24534     /**
24535      * Customizable piece of the default paging text (defaults to "Page")
24536      * @type String
24537      */
24538     beforePageText : "Page",
24539     /**
24540      * Customizable piece of the default paging text (defaults to "of %0")
24541      * @type String
24542      */
24543     afterPageText : "of {0}",
24544     /**
24545      * Customizable piece of the default paging text (defaults to "First Page")
24546      * @type String
24547      */
24548     firstText : "First Page",
24549     /**
24550      * Customizable piece of the default paging text (defaults to "Previous Page")
24551      * @type String
24552      */
24553     prevText : "Previous Page",
24554     /**
24555      * Customizable piece of the default paging text (defaults to "Next Page")
24556      * @type String
24557      */
24558     nextText : "Next Page",
24559     /**
24560      * Customizable piece of the default paging text (defaults to "Last Page")
24561      * @type String
24562      */
24563     lastText : "Last Page",
24564     /**
24565      * Customizable piece of the default paging text (defaults to "Refresh")
24566      * @type String
24567      */
24568     refreshText : "Refresh",
24569
24570     buttons : false,
24571     // private
24572     onRender : function(ct, position) 
24573     {
24574         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24575         this.navgroup.parentId = this.id;
24576         this.navgroup.onRender(this.el, null);
24577         // add the buttons to the navgroup
24578         
24579         if(this.displayInfo){
24580             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24581             this.displayEl = this.el.select('.x-paging-info', true).first();
24582 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24583 //            this.displayEl = navel.el.select('span',true).first();
24584         }
24585         
24586         var _this = this;
24587         
24588         if(this.buttons){
24589             Roo.each(_this.buttons, function(e){ // this might need to use render????
24590                Roo.factory(e).render(_this.el);
24591             });
24592         }
24593             
24594         Roo.each(_this.toolbarItems, function(e) {
24595             _this.navgroup.addItem(e);
24596         });
24597         
24598         
24599         this.first = this.navgroup.addItem({
24600             tooltip: this.firstText,
24601             cls: "prev",
24602             icon : 'fa fa-backward',
24603             disabled: true,
24604             preventDefault: true,
24605             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24606         });
24607         
24608         this.prev =  this.navgroup.addItem({
24609             tooltip: this.prevText,
24610             cls: "prev",
24611             icon : 'fa fa-step-backward',
24612             disabled: true,
24613             preventDefault: true,
24614             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24615         });
24616     //this.addSeparator();
24617         
24618         
24619         var field = this.navgroup.addItem( {
24620             tagtype : 'span',
24621             cls : 'x-paging-position',
24622             
24623             html : this.beforePageText  +
24624                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24625                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24626          } ); //?? escaped?
24627         
24628         this.field = field.el.select('input', true).first();
24629         this.field.on("keydown", this.onPagingKeydown, this);
24630         this.field.on("focus", function(){this.dom.select();});
24631     
24632     
24633         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24634         //this.field.setHeight(18);
24635         //this.addSeparator();
24636         this.next = this.navgroup.addItem({
24637             tooltip: this.nextText,
24638             cls: "next",
24639             html : ' <i class="fa fa-step-forward">',
24640             disabled: true,
24641             preventDefault: true,
24642             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24643         });
24644         this.last = this.navgroup.addItem({
24645             tooltip: this.lastText,
24646             icon : 'fa fa-forward',
24647             cls: "next",
24648             disabled: true,
24649             preventDefault: true,
24650             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24651         });
24652     //this.addSeparator();
24653         this.loading = this.navgroup.addItem({
24654             tooltip: this.refreshText,
24655             icon: 'fa fa-refresh',
24656             preventDefault: true,
24657             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24658         });
24659         
24660     },
24661
24662     // private
24663     updateInfo : function(){
24664         if(this.displayEl){
24665             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24666             var msg = count == 0 ?
24667                 this.emptyMsg :
24668                 String.format(
24669                     this.displayMsg,
24670                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24671                 );
24672             this.displayEl.update(msg);
24673         }
24674     },
24675
24676     // private
24677     onLoad : function(ds, r, o)
24678     {
24679         this.cursor = o.params.start ? o.params.start : 0;
24680         
24681         var d = this.getPageData(),
24682             ap = d.activePage,
24683             ps = d.pages;
24684         
24685         
24686         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24687         this.field.dom.value = ap;
24688         this.first.setDisabled(ap == 1);
24689         this.prev.setDisabled(ap == 1);
24690         this.next.setDisabled(ap == ps);
24691         this.last.setDisabled(ap == ps);
24692         this.loading.enable();
24693         this.updateInfo();
24694     },
24695
24696     // private
24697     getPageData : function(){
24698         var total = this.ds.getTotalCount();
24699         return {
24700             total : total,
24701             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24702             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24703         };
24704     },
24705
24706     // private
24707     onLoadError : function(){
24708         this.loading.enable();
24709     },
24710
24711     // private
24712     onPagingKeydown : function(e){
24713         var k = e.getKey();
24714         var d = this.getPageData();
24715         if(k == e.RETURN){
24716             var v = this.field.dom.value, pageNum;
24717             if(!v || isNaN(pageNum = parseInt(v, 10))){
24718                 this.field.dom.value = d.activePage;
24719                 return;
24720             }
24721             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24722             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24723             e.stopEvent();
24724         }
24725         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))
24726         {
24727           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24728           this.field.dom.value = pageNum;
24729           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24730           e.stopEvent();
24731         }
24732         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24733         {
24734           var v = this.field.dom.value, pageNum; 
24735           var increment = (e.shiftKey) ? 10 : 1;
24736           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24737                 increment *= -1;
24738           }
24739           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24740             this.field.dom.value = d.activePage;
24741             return;
24742           }
24743           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24744           {
24745             this.field.dom.value = parseInt(v, 10) + increment;
24746             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24747             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24748           }
24749           e.stopEvent();
24750         }
24751     },
24752
24753     // private
24754     beforeLoad : function(){
24755         if(this.loading){
24756             this.loading.disable();
24757         }
24758     },
24759
24760     // private
24761     onClick : function(which){
24762         
24763         var ds = this.ds;
24764         if (!ds) {
24765             return;
24766         }
24767         
24768         switch(which){
24769             case "first":
24770                 ds.load({params:{start: 0, limit: this.pageSize}});
24771             break;
24772             case "prev":
24773                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24774             break;
24775             case "next":
24776                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24777             break;
24778             case "last":
24779                 var total = ds.getTotalCount();
24780                 var extra = total % this.pageSize;
24781                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24782                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24783             break;
24784             case "refresh":
24785                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24786             break;
24787         }
24788     },
24789
24790     /**
24791      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24792      * @param {Roo.data.Store} store The data store to unbind
24793      */
24794     unbind : function(ds){
24795         ds.un("beforeload", this.beforeLoad, this);
24796         ds.un("load", this.onLoad, this);
24797         ds.un("loadexception", this.onLoadError, this);
24798         ds.un("remove", this.updateInfo, this);
24799         ds.un("add", this.updateInfo, this);
24800         this.ds = undefined;
24801     },
24802
24803     /**
24804      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24805      * @param {Roo.data.Store} store The data store to bind
24806      */
24807     bind : function(ds){
24808         ds.on("beforeload", this.beforeLoad, this);
24809         ds.on("load", this.onLoad, this);
24810         ds.on("loadexception", this.onLoadError, this);
24811         ds.on("remove", this.updateInfo, this);
24812         ds.on("add", this.updateInfo, this);
24813         this.ds = ds;
24814     }
24815 });/*
24816  * - LGPL
24817  *
24818  * element
24819  * 
24820  */
24821
24822 /**
24823  * @class Roo.bootstrap.MessageBar
24824  * @extends Roo.bootstrap.Component
24825  * Bootstrap MessageBar class
24826  * @cfg {String} html contents of the MessageBar
24827  * @cfg {String} weight (info | success | warning | danger) default info
24828  * @cfg {String} beforeClass insert the bar before the given class
24829  * @cfg {Boolean} closable (true | false) default false
24830  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24831  * 
24832  * @constructor
24833  * Create a new Element
24834  * @param {Object} config The config object
24835  */
24836
24837 Roo.bootstrap.MessageBar = function(config){
24838     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24839 };
24840
24841 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24842     
24843     html: '',
24844     weight: 'info',
24845     closable: false,
24846     fixed: false,
24847     beforeClass: 'bootstrap-sticky-wrap',
24848     
24849     getAutoCreate : function(){
24850         
24851         var cfg = {
24852             tag: 'div',
24853             cls: 'alert alert-dismissable alert-' + this.weight,
24854             cn: [
24855                 {
24856                     tag: 'span',
24857                     cls: 'message',
24858                     html: this.html || ''
24859                 }
24860             ]
24861         };
24862         
24863         if(this.fixed){
24864             cfg.cls += ' alert-messages-fixed';
24865         }
24866         
24867         if(this.closable){
24868             cfg.cn.push({
24869                 tag: 'button',
24870                 cls: 'close',
24871                 html: 'x'
24872             });
24873         }
24874         
24875         return cfg;
24876     },
24877     
24878     onRender : function(ct, position)
24879     {
24880         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24881         
24882         if(!this.el){
24883             var cfg = Roo.apply({},  this.getAutoCreate());
24884             cfg.id = Roo.id();
24885             
24886             if (this.cls) {
24887                 cfg.cls += ' ' + this.cls;
24888             }
24889             if (this.style) {
24890                 cfg.style = this.style;
24891             }
24892             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24893             
24894             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24895         }
24896         
24897         this.el.select('>button.close').on('click', this.hide, this);
24898         
24899     },
24900     
24901     show : function()
24902     {
24903         if (!this.rendered) {
24904             this.render();
24905         }
24906         
24907         this.el.show();
24908         
24909         this.fireEvent('show', this);
24910         
24911     },
24912     
24913     hide : function()
24914     {
24915         if (!this.rendered) {
24916             this.render();
24917         }
24918         
24919         this.el.hide();
24920         
24921         this.fireEvent('hide', this);
24922     },
24923     
24924     update : function()
24925     {
24926 //        var e = this.el.dom.firstChild;
24927 //        
24928 //        if(this.closable){
24929 //            e = e.nextSibling;
24930 //        }
24931 //        
24932 //        e.data = this.html || '';
24933
24934         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24935     }
24936    
24937 });
24938
24939  
24940
24941      /*
24942  * - LGPL
24943  *
24944  * Graph
24945  * 
24946  */
24947
24948
24949 /**
24950  * @class Roo.bootstrap.Graph
24951  * @extends Roo.bootstrap.Component
24952  * Bootstrap Graph class
24953 > Prameters
24954  -sm {number} sm 4
24955  -md {number} md 5
24956  @cfg {String} graphtype  bar | vbar | pie
24957  @cfg {number} g_x coodinator | centre x (pie)
24958  @cfg {number} g_y coodinator | centre y (pie)
24959  @cfg {number} g_r radius (pie)
24960  @cfg {number} g_height height of the chart (respected by all elements in the set)
24961  @cfg {number} g_width width of the chart (respected by all elements in the set)
24962  @cfg {Object} title The title of the chart
24963     
24964  -{Array}  values
24965  -opts (object) options for the chart 
24966      o {
24967      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24968      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24969      o vgutter (number)
24970      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.
24971      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24972      o to
24973      o stretch (boolean)
24974      o }
24975  -opts (object) options for the pie
24976      o{
24977      o cut
24978      o startAngle (number)
24979      o endAngle (number)
24980      } 
24981  *
24982  * @constructor
24983  * Create a new Input
24984  * @param {Object} config The config object
24985  */
24986
24987 Roo.bootstrap.Graph = function(config){
24988     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24989     
24990     this.addEvents({
24991         // img events
24992         /**
24993          * @event click
24994          * The img click event for the img.
24995          * @param {Roo.EventObject} e
24996          */
24997         "click" : true
24998     });
24999 };
25000
25001 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25002     
25003     sm: 4,
25004     md: 5,
25005     graphtype: 'bar',
25006     g_height: 250,
25007     g_width: 400,
25008     g_x: 50,
25009     g_y: 50,
25010     g_r: 30,
25011     opts:{
25012         //g_colors: this.colors,
25013         g_type: 'soft',
25014         g_gutter: '20%'
25015
25016     },
25017     title : false,
25018
25019     getAutoCreate : function(){
25020         
25021         var cfg = {
25022             tag: 'div',
25023             html : null
25024         };
25025         
25026         
25027         return  cfg;
25028     },
25029
25030     onRender : function(ct,position){
25031         
25032         
25033         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25034         
25035         if (typeof(Raphael) == 'undefined') {
25036             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25037             return;
25038         }
25039         
25040         this.raphael = Raphael(this.el.dom);
25041         
25042                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25043                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25044                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25045                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25046                 /*
25047                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25048                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25049                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25050                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25051                 
25052                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25053                 r.barchart(330, 10, 300, 220, data1);
25054                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25055                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25056                 */
25057                 
25058                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25059                 // r.barchart(30, 30, 560, 250,  xdata, {
25060                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25061                 //     axis : "0 0 1 1",
25062                 //     axisxlabels :  xdata
25063                 //     //yvalues : cols,
25064                    
25065                 // });
25066 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25067 //        
25068 //        this.load(null,xdata,{
25069 //                axis : "0 0 1 1",
25070 //                axisxlabels :  xdata
25071 //                });
25072
25073     },
25074
25075     load : function(graphtype,xdata,opts)
25076     {
25077         this.raphael.clear();
25078         if(!graphtype) {
25079             graphtype = this.graphtype;
25080         }
25081         if(!opts){
25082             opts = this.opts;
25083         }
25084         var r = this.raphael,
25085             fin = function () {
25086                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25087             },
25088             fout = function () {
25089                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25090             },
25091             pfin = function() {
25092                 this.sector.stop();
25093                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25094
25095                 if (this.label) {
25096                     this.label[0].stop();
25097                     this.label[0].attr({ r: 7.5 });
25098                     this.label[1].attr({ "font-weight": 800 });
25099                 }
25100             },
25101             pfout = function() {
25102                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25103
25104                 if (this.label) {
25105                     this.label[0].animate({ r: 5 }, 500, "bounce");
25106                     this.label[1].attr({ "font-weight": 400 });
25107                 }
25108             };
25109
25110         switch(graphtype){
25111             case 'bar':
25112                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25113                 break;
25114             case 'hbar':
25115                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25116                 break;
25117             case 'pie':
25118 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25119 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25120 //            
25121                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25122                 
25123                 break;
25124
25125         }
25126         
25127         if(this.title){
25128             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25129         }
25130         
25131     },
25132     
25133     setTitle: function(o)
25134     {
25135         this.title = o;
25136     },
25137     
25138     initEvents: function() {
25139         
25140         if(!this.href){
25141             this.el.on('click', this.onClick, this);
25142         }
25143     },
25144     
25145     onClick : function(e)
25146     {
25147         Roo.log('img onclick');
25148         this.fireEvent('click', this, e);
25149     }
25150    
25151 });
25152
25153  
25154 /*
25155  * - LGPL
25156  *
25157  * numberBox
25158  * 
25159  */
25160 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25161
25162 /**
25163  * @class Roo.bootstrap.dash.NumberBox
25164  * @extends Roo.bootstrap.Component
25165  * Bootstrap NumberBox class
25166  * @cfg {String} headline Box headline
25167  * @cfg {String} content Box content
25168  * @cfg {String} icon Box icon
25169  * @cfg {String} footer Footer text
25170  * @cfg {String} fhref Footer href
25171  * 
25172  * @constructor
25173  * Create a new NumberBox
25174  * @param {Object} config The config object
25175  */
25176
25177
25178 Roo.bootstrap.dash.NumberBox = function(config){
25179     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25180     
25181 };
25182
25183 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25184     
25185     headline : '',
25186     content : '',
25187     icon : '',
25188     footer : '',
25189     fhref : '',
25190     ficon : '',
25191     
25192     getAutoCreate : function(){
25193         
25194         var cfg = {
25195             tag : 'div',
25196             cls : 'small-box ',
25197             cn : [
25198                 {
25199                     tag : 'div',
25200                     cls : 'inner',
25201                     cn :[
25202                         {
25203                             tag : 'h3',
25204                             cls : 'roo-headline',
25205                             html : this.headline
25206                         },
25207                         {
25208                             tag : 'p',
25209                             cls : 'roo-content',
25210                             html : this.content
25211                         }
25212                     ]
25213                 }
25214             ]
25215         };
25216         
25217         if(this.icon){
25218             cfg.cn.push({
25219                 tag : 'div',
25220                 cls : 'icon',
25221                 cn :[
25222                     {
25223                         tag : 'i',
25224                         cls : 'ion ' + this.icon
25225                     }
25226                 ]
25227             });
25228         }
25229         
25230         if(this.footer){
25231             var footer = {
25232                 tag : 'a',
25233                 cls : 'small-box-footer',
25234                 href : this.fhref || '#',
25235                 html : this.footer
25236             };
25237             
25238             cfg.cn.push(footer);
25239             
25240         }
25241         
25242         return  cfg;
25243     },
25244
25245     onRender : function(ct,position){
25246         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25247
25248
25249        
25250                 
25251     },
25252
25253     setHeadline: function (value)
25254     {
25255         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25256     },
25257     
25258     setFooter: function (value, href)
25259     {
25260         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25261         
25262         if(href){
25263             this.el.select('a.small-box-footer',true).first().attr('href', href);
25264         }
25265         
25266     },
25267
25268     setContent: function (value)
25269     {
25270         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25271     },
25272
25273     initEvents: function() 
25274     {   
25275         
25276     }
25277     
25278 });
25279
25280  
25281 /*
25282  * - LGPL
25283  *
25284  * TabBox
25285  * 
25286  */
25287 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25288
25289 /**
25290  * @class Roo.bootstrap.dash.TabBox
25291  * @extends Roo.bootstrap.Component
25292  * Bootstrap TabBox class
25293  * @cfg {String} title Title of the TabBox
25294  * @cfg {String} icon Icon of the TabBox
25295  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25296  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25297  * 
25298  * @constructor
25299  * Create a new TabBox
25300  * @param {Object} config The config object
25301  */
25302
25303
25304 Roo.bootstrap.dash.TabBox = function(config){
25305     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25306     this.addEvents({
25307         // raw events
25308         /**
25309          * @event addpane
25310          * When a pane is added
25311          * @param {Roo.bootstrap.dash.TabPane} pane
25312          */
25313         "addpane" : true,
25314         /**
25315          * @event activatepane
25316          * When a pane is activated
25317          * @param {Roo.bootstrap.dash.TabPane} pane
25318          */
25319         "activatepane" : true
25320         
25321          
25322     });
25323     
25324     this.panes = [];
25325 };
25326
25327 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25328
25329     title : '',
25330     icon : false,
25331     showtabs : true,
25332     tabScrollable : false,
25333     
25334     getChildContainer : function()
25335     {
25336         return this.el.select('.tab-content', true).first();
25337     },
25338     
25339     getAutoCreate : function(){
25340         
25341         var header = {
25342             tag: 'li',
25343             cls: 'pull-left header',
25344             html: this.title,
25345             cn : []
25346         };
25347         
25348         if(this.icon){
25349             header.cn.push({
25350                 tag: 'i',
25351                 cls: 'fa ' + this.icon
25352             });
25353         }
25354         
25355         var h = {
25356             tag: 'ul',
25357             cls: 'nav nav-tabs pull-right',
25358             cn: [
25359                 header
25360             ]
25361         };
25362         
25363         if(this.tabScrollable){
25364             h = {
25365                 tag: 'div',
25366                 cls: 'tab-header',
25367                 cn: [
25368                     {
25369                         tag: 'ul',
25370                         cls: 'nav nav-tabs pull-right',
25371                         cn: [
25372                             header
25373                         ]
25374                     }
25375                 ]
25376             };
25377         }
25378         
25379         var cfg = {
25380             tag: 'div',
25381             cls: 'nav-tabs-custom',
25382             cn: [
25383                 h,
25384                 {
25385                     tag: 'div',
25386                     cls: 'tab-content no-padding',
25387                     cn: []
25388                 }
25389             ]
25390         };
25391
25392         return  cfg;
25393     },
25394     initEvents : function()
25395     {
25396         //Roo.log('add add pane handler');
25397         this.on('addpane', this.onAddPane, this);
25398     },
25399      /**
25400      * Updates the box title
25401      * @param {String} html to set the title to.
25402      */
25403     setTitle : function(value)
25404     {
25405         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25406     },
25407     onAddPane : function(pane)
25408     {
25409         this.panes.push(pane);
25410         //Roo.log('addpane');
25411         //Roo.log(pane);
25412         // tabs are rendere left to right..
25413         if(!this.showtabs){
25414             return;
25415         }
25416         
25417         var ctr = this.el.select('.nav-tabs', true).first();
25418          
25419          
25420         var existing = ctr.select('.nav-tab',true);
25421         var qty = existing.getCount();;
25422         
25423         
25424         var tab = ctr.createChild({
25425             tag : 'li',
25426             cls : 'nav-tab' + (qty ? '' : ' active'),
25427             cn : [
25428                 {
25429                     tag : 'a',
25430                     href:'#',
25431                     html : pane.title
25432                 }
25433             ]
25434         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25435         pane.tab = tab;
25436         
25437         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25438         if (!qty) {
25439             pane.el.addClass('active');
25440         }
25441         
25442                 
25443     },
25444     onTabClick : function(ev,un,ob,pane)
25445     {
25446         //Roo.log('tab - prev default');
25447         ev.preventDefault();
25448         
25449         
25450         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25451         pane.tab.addClass('active');
25452         //Roo.log(pane.title);
25453         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25454         // technically we should have a deactivate event.. but maybe add later.
25455         // and it should not de-activate the selected tab...
25456         this.fireEvent('activatepane', pane);
25457         pane.el.addClass('active');
25458         pane.fireEvent('activate');
25459         
25460         
25461     },
25462     
25463     getActivePane : function()
25464     {
25465         var r = false;
25466         Roo.each(this.panes, function(p) {
25467             if(p.el.hasClass('active')){
25468                 r = p;
25469                 return false;
25470             }
25471             
25472             return;
25473         });
25474         
25475         return r;
25476     }
25477     
25478     
25479 });
25480
25481  
25482 /*
25483  * - LGPL
25484  *
25485  * Tab pane
25486  * 
25487  */
25488 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25489 /**
25490  * @class Roo.bootstrap.TabPane
25491  * @extends Roo.bootstrap.Component
25492  * Bootstrap TabPane class
25493  * @cfg {Boolean} active (false | true) Default false
25494  * @cfg {String} title title of panel
25495
25496  * 
25497  * @constructor
25498  * Create a new TabPane
25499  * @param {Object} config The config object
25500  */
25501
25502 Roo.bootstrap.dash.TabPane = function(config){
25503     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25504     
25505     this.addEvents({
25506         // raw events
25507         /**
25508          * @event activate
25509          * When a pane is activated
25510          * @param {Roo.bootstrap.dash.TabPane} pane
25511          */
25512         "activate" : true
25513          
25514     });
25515 };
25516
25517 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25518     
25519     active : false,
25520     title : '',
25521     
25522     // the tabBox that this is attached to.
25523     tab : false,
25524      
25525     getAutoCreate : function() 
25526     {
25527         var cfg = {
25528             tag: 'div',
25529             cls: 'tab-pane'
25530         };
25531         
25532         if(this.active){
25533             cfg.cls += ' active';
25534         }
25535         
25536         return cfg;
25537     },
25538     initEvents  : function()
25539     {
25540         //Roo.log('trigger add pane handler');
25541         this.parent().fireEvent('addpane', this)
25542     },
25543     
25544      /**
25545      * Updates the tab title 
25546      * @param {String} html to set the title to.
25547      */
25548     setTitle: function(str)
25549     {
25550         if (!this.tab) {
25551             return;
25552         }
25553         this.title = str;
25554         this.tab.select('a', true).first().dom.innerHTML = str;
25555         
25556     }
25557     
25558     
25559     
25560 });
25561
25562  
25563
25564
25565  /*
25566  * - LGPL
25567  *
25568  * menu
25569  * 
25570  */
25571 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25572
25573 /**
25574  * @class Roo.bootstrap.menu.Menu
25575  * @extends Roo.bootstrap.Component
25576  * Bootstrap Menu class - container for Menu
25577  * @cfg {String} html Text of the menu
25578  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25579  * @cfg {String} icon Font awesome icon
25580  * @cfg {String} pos Menu align to (top | bottom) default bottom
25581  * 
25582  * 
25583  * @constructor
25584  * Create a new Menu
25585  * @param {Object} config The config object
25586  */
25587
25588
25589 Roo.bootstrap.menu.Menu = function(config){
25590     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25591     
25592     this.addEvents({
25593         /**
25594          * @event beforeshow
25595          * Fires before this menu is displayed
25596          * @param {Roo.bootstrap.menu.Menu} this
25597          */
25598         beforeshow : true,
25599         /**
25600          * @event beforehide
25601          * Fires before this menu is hidden
25602          * @param {Roo.bootstrap.menu.Menu} this
25603          */
25604         beforehide : true,
25605         /**
25606          * @event show
25607          * Fires after this menu is displayed
25608          * @param {Roo.bootstrap.menu.Menu} this
25609          */
25610         show : true,
25611         /**
25612          * @event hide
25613          * Fires after this menu is hidden
25614          * @param {Roo.bootstrap.menu.Menu} this
25615          */
25616         hide : true,
25617         /**
25618          * @event click
25619          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25620          * @param {Roo.bootstrap.menu.Menu} this
25621          * @param {Roo.EventObject} e
25622          */
25623         click : true
25624     });
25625     
25626 };
25627
25628 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25629     
25630     submenu : false,
25631     html : '',
25632     weight : 'default',
25633     icon : false,
25634     pos : 'bottom',
25635     
25636     
25637     getChildContainer : function() {
25638         if(this.isSubMenu){
25639             return this.el;
25640         }
25641         
25642         return this.el.select('ul.dropdown-menu', true).first();  
25643     },
25644     
25645     getAutoCreate : function()
25646     {
25647         var text = [
25648             {
25649                 tag : 'span',
25650                 cls : 'roo-menu-text',
25651                 html : this.html
25652             }
25653         ];
25654         
25655         if(this.icon){
25656             text.unshift({
25657                 tag : 'i',
25658                 cls : 'fa ' + this.icon
25659             })
25660         }
25661         
25662         
25663         var cfg = {
25664             tag : 'div',
25665             cls : 'btn-group',
25666             cn : [
25667                 {
25668                     tag : 'button',
25669                     cls : 'dropdown-button btn btn-' + this.weight,
25670                     cn : text
25671                 },
25672                 {
25673                     tag : 'button',
25674                     cls : 'dropdown-toggle btn btn-' + this.weight,
25675                     cn : [
25676                         {
25677                             tag : 'span',
25678                             cls : 'caret'
25679                         }
25680                     ]
25681                 },
25682                 {
25683                     tag : 'ul',
25684                     cls : 'dropdown-menu'
25685                 }
25686             ]
25687             
25688         };
25689         
25690         if(this.pos == 'top'){
25691             cfg.cls += ' dropup';
25692         }
25693         
25694         if(this.isSubMenu){
25695             cfg = {
25696                 tag : 'ul',
25697                 cls : 'dropdown-menu'
25698             }
25699         }
25700         
25701         return cfg;
25702     },
25703     
25704     onRender : function(ct, position)
25705     {
25706         this.isSubMenu = ct.hasClass('dropdown-submenu');
25707         
25708         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25709     },
25710     
25711     initEvents : function() 
25712     {
25713         if(this.isSubMenu){
25714             return;
25715         }
25716         
25717         this.hidden = true;
25718         
25719         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25720         this.triggerEl.on('click', this.onTriggerPress, this);
25721         
25722         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25723         this.buttonEl.on('click', this.onClick, this);
25724         
25725     },
25726     
25727     list : function()
25728     {
25729         if(this.isSubMenu){
25730             return this.el;
25731         }
25732         
25733         return this.el.select('ul.dropdown-menu', true).first();
25734     },
25735     
25736     onClick : function(e)
25737     {
25738         this.fireEvent("click", this, e);
25739     },
25740     
25741     onTriggerPress  : function(e)
25742     {   
25743         if (this.isVisible()) {
25744             this.hide();
25745         } else {
25746             this.show();
25747         }
25748     },
25749     
25750     isVisible : function(){
25751         return !this.hidden;
25752     },
25753     
25754     show : function()
25755     {
25756         this.fireEvent("beforeshow", this);
25757         
25758         this.hidden = false;
25759         this.el.addClass('open');
25760         
25761         Roo.get(document).on("mouseup", this.onMouseUp, this);
25762         
25763         this.fireEvent("show", this);
25764         
25765         
25766     },
25767     
25768     hide : function()
25769     {
25770         this.fireEvent("beforehide", this);
25771         
25772         this.hidden = true;
25773         this.el.removeClass('open');
25774         
25775         Roo.get(document).un("mouseup", this.onMouseUp);
25776         
25777         this.fireEvent("hide", this);
25778     },
25779     
25780     onMouseUp : function()
25781     {
25782         this.hide();
25783     }
25784     
25785 });
25786
25787  
25788  /*
25789  * - LGPL
25790  *
25791  * menu item
25792  * 
25793  */
25794 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25795
25796 /**
25797  * @class Roo.bootstrap.menu.Item
25798  * @extends Roo.bootstrap.Component
25799  * Bootstrap MenuItem class
25800  * @cfg {Boolean} submenu (true | false) default false
25801  * @cfg {String} html text of the item
25802  * @cfg {String} href the link
25803  * @cfg {Boolean} disable (true | false) default false
25804  * @cfg {Boolean} preventDefault (true | false) default true
25805  * @cfg {String} icon Font awesome icon
25806  * @cfg {String} pos Submenu align to (left | right) default right 
25807  * 
25808  * 
25809  * @constructor
25810  * Create a new Item
25811  * @param {Object} config The config object
25812  */
25813
25814
25815 Roo.bootstrap.menu.Item = function(config){
25816     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25817     this.addEvents({
25818         /**
25819          * @event mouseover
25820          * Fires when the mouse is hovering over this menu
25821          * @param {Roo.bootstrap.menu.Item} this
25822          * @param {Roo.EventObject} e
25823          */
25824         mouseover : true,
25825         /**
25826          * @event mouseout
25827          * Fires when the mouse exits this menu
25828          * @param {Roo.bootstrap.menu.Item} this
25829          * @param {Roo.EventObject} e
25830          */
25831         mouseout : true,
25832         // raw events
25833         /**
25834          * @event click
25835          * The raw click event for the entire grid.
25836          * @param {Roo.EventObject} e
25837          */
25838         click : true
25839     });
25840 };
25841
25842 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25843     
25844     submenu : false,
25845     href : '',
25846     html : '',
25847     preventDefault: true,
25848     disable : false,
25849     icon : false,
25850     pos : 'right',
25851     
25852     getAutoCreate : function()
25853     {
25854         var text = [
25855             {
25856                 tag : 'span',
25857                 cls : 'roo-menu-item-text',
25858                 html : this.html
25859             }
25860         ];
25861         
25862         if(this.icon){
25863             text.unshift({
25864                 tag : 'i',
25865                 cls : 'fa ' + this.icon
25866             })
25867         }
25868         
25869         var cfg = {
25870             tag : 'li',
25871             cn : [
25872                 {
25873                     tag : 'a',
25874                     href : this.href || '#',
25875                     cn : text
25876                 }
25877             ]
25878         };
25879         
25880         if(this.disable){
25881             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25882         }
25883         
25884         if(this.submenu){
25885             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25886             
25887             if(this.pos == 'left'){
25888                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25889             }
25890         }
25891         
25892         return cfg;
25893     },
25894     
25895     initEvents : function() 
25896     {
25897         this.el.on('mouseover', this.onMouseOver, this);
25898         this.el.on('mouseout', this.onMouseOut, this);
25899         
25900         this.el.select('a', true).first().on('click', this.onClick, this);
25901         
25902     },
25903     
25904     onClick : function(e)
25905     {
25906         if(this.preventDefault){
25907             e.preventDefault();
25908         }
25909         
25910         this.fireEvent("click", this, e);
25911     },
25912     
25913     onMouseOver : function(e)
25914     {
25915         if(this.submenu && this.pos == 'left'){
25916             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25917         }
25918         
25919         this.fireEvent("mouseover", this, e);
25920     },
25921     
25922     onMouseOut : function(e)
25923     {
25924         this.fireEvent("mouseout", this, e);
25925     }
25926 });
25927
25928  
25929
25930  /*
25931  * - LGPL
25932  *
25933  * menu separator
25934  * 
25935  */
25936 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25937
25938 /**
25939  * @class Roo.bootstrap.menu.Separator
25940  * @extends Roo.bootstrap.Component
25941  * Bootstrap Separator class
25942  * 
25943  * @constructor
25944  * Create a new Separator
25945  * @param {Object} config The config object
25946  */
25947
25948
25949 Roo.bootstrap.menu.Separator = function(config){
25950     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25951 };
25952
25953 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25954     
25955     getAutoCreate : function(){
25956         var cfg = {
25957             tag : 'li',
25958             cls: 'divider'
25959         };
25960         
25961         return cfg;
25962     }
25963    
25964 });
25965
25966  
25967
25968  /*
25969  * - LGPL
25970  *
25971  * Tooltip
25972  * 
25973  */
25974
25975 /**
25976  * @class Roo.bootstrap.Tooltip
25977  * Bootstrap Tooltip class
25978  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25979  * to determine which dom element triggers the tooltip.
25980  * 
25981  * It needs to add support for additional attributes like tooltip-position
25982  * 
25983  * @constructor
25984  * Create a new Toolti
25985  * @param {Object} config The config object
25986  */
25987
25988 Roo.bootstrap.Tooltip = function(config){
25989     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25990     
25991     this.alignment = Roo.bootstrap.Tooltip.alignment;
25992     
25993     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25994         this.alignment = config.alignment;
25995     }
25996     
25997 };
25998
25999 Roo.apply(Roo.bootstrap.Tooltip, {
26000     /**
26001      * @function init initialize tooltip monitoring.
26002      * @static
26003      */
26004     currentEl : false,
26005     currentTip : false,
26006     currentRegion : false,
26007     
26008     //  init : delay?
26009     
26010     init : function()
26011     {
26012         Roo.get(document).on('mouseover', this.enter ,this);
26013         Roo.get(document).on('mouseout', this.leave, this);
26014          
26015         
26016         this.currentTip = new Roo.bootstrap.Tooltip();
26017     },
26018     
26019     enter : function(ev)
26020     {
26021         var dom = ev.getTarget();
26022         
26023         //Roo.log(['enter',dom]);
26024         var el = Roo.fly(dom);
26025         if (this.currentEl) {
26026             //Roo.log(dom);
26027             //Roo.log(this.currentEl);
26028             //Roo.log(this.currentEl.contains(dom));
26029             if (this.currentEl == el) {
26030                 return;
26031             }
26032             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26033                 return;
26034             }
26035
26036         }
26037         
26038         if (this.currentTip.el) {
26039             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26040         }    
26041         //Roo.log(ev);
26042         
26043         if(!el || el.dom == document){
26044             return;
26045         }
26046         
26047         var bindEl = el;
26048         
26049         // you can not look for children, as if el is the body.. then everythign is the child..
26050         if (!el.attr('tooltip')) { //
26051             if (!el.select("[tooltip]").elements.length) {
26052                 return;
26053             }
26054             // is the mouse over this child...?
26055             bindEl = el.select("[tooltip]").first();
26056             var xy = ev.getXY();
26057             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26058                 //Roo.log("not in region.");
26059                 return;
26060             }
26061             //Roo.log("child element over..");
26062             
26063         }
26064         this.currentEl = bindEl;
26065         this.currentTip.bind(bindEl);
26066         this.currentRegion = Roo.lib.Region.getRegion(dom);
26067         this.currentTip.enter();
26068         
26069     },
26070     leave : function(ev)
26071     {
26072         var dom = ev.getTarget();
26073         //Roo.log(['leave',dom]);
26074         if (!this.currentEl) {
26075             return;
26076         }
26077         
26078         
26079         if (dom != this.currentEl.dom) {
26080             return;
26081         }
26082         var xy = ev.getXY();
26083         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26084             return;
26085         }
26086         // only activate leave if mouse cursor is outside... bounding box..
26087         
26088         
26089         
26090         
26091         if (this.currentTip) {
26092             this.currentTip.leave();
26093         }
26094         //Roo.log('clear currentEl');
26095         this.currentEl = false;
26096         
26097         
26098     },
26099     alignment : {
26100         'left' : ['r-l', [-2,0], 'right'],
26101         'right' : ['l-r', [2,0], 'left'],
26102         'bottom' : ['t-b', [0,2], 'top'],
26103         'top' : [ 'b-t', [0,-2], 'bottom']
26104     }
26105     
26106 });
26107
26108
26109 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26110     
26111     
26112     bindEl : false,
26113     
26114     delay : null, // can be { show : 300 , hide: 500}
26115     
26116     timeout : null,
26117     
26118     hoverState : null, //???
26119     
26120     placement : 'bottom', 
26121     
26122     alignment : false,
26123     
26124     getAutoCreate : function(){
26125     
26126         var cfg = {
26127            cls : 'tooltip',
26128            role : 'tooltip',
26129            cn : [
26130                 {
26131                     cls : 'tooltip-arrow'
26132                 },
26133                 {
26134                     cls : 'tooltip-inner'
26135                 }
26136            ]
26137         };
26138         
26139         return cfg;
26140     },
26141     bind : function(el)
26142     {
26143         this.bindEl = el;
26144     },
26145       
26146     
26147     enter : function () {
26148        
26149         if (this.timeout != null) {
26150             clearTimeout(this.timeout);
26151         }
26152         
26153         this.hoverState = 'in';
26154          //Roo.log("enter - show");
26155         if (!this.delay || !this.delay.show) {
26156             this.show();
26157             return;
26158         }
26159         var _t = this;
26160         this.timeout = setTimeout(function () {
26161             if (_t.hoverState == 'in') {
26162                 _t.show();
26163             }
26164         }, this.delay.show);
26165     },
26166     leave : function()
26167     {
26168         clearTimeout(this.timeout);
26169     
26170         this.hoverState = 'out';
26171          if (!this.delay || !this.delay.hide) {
26172             this.hide();
26173             return;
26174         }
26175        
26176         var _t = this;
26177         this.timeout = setTimeout(function () {
26178             //Roo.log("leave - timeout");
26179             
26180             if (_t.hoverState == 'out') {
26181                 _t.hide();
26182                 Roo.bootstrap.Tooltip.currentEl = false;
26183             }
26184         }, delay);
26185     },
26186     
26187     show : function (msg)
26188     {
26189         if (!this.el) {
26190             this.render(document.body);
26191         }
26192         // set content.
26193         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26194         
26195         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26196         
26197         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26198         
26199         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26200         
26201         var placement = typeof this.placement == 'function' ?
26202             this.placement.call(this, this.el, on_el) :
26203             this.placement;
26204             
26205         var autoToken = /\s?auto?\s?/i;
26206         var autoPlace = autoToken.test(placement);
26207         if (autoPlace) {
26208             placement = placement.replace(autoToken, '') || 'top';
26209         }
26210         
26211         //this.el.detach()
26212         //this.el.setXY([0,0]);
26213         this.el.show();
26214         //this.el.dom.style.display='block';
26215         
26216         //this.el.appendTo(on_el);
26217         
26218         var p = this.getPosition();
26219         var box = this.el.getBox();
26220         
26221         if (autoPlace) {
26222             // fixme..
26223         }
26224         
26225         var align = this.alignment[placement];
26226         
26227         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26228         
26229         if(placement == 'top' || placement == 'bottom'){
26230             if(xy[0] < 0){
26231                 placement = 'right';
26232             }
26233             
26234             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26235                 placement = 'left';
26236             }
26237             
26238             var scroll = Roo.select('body', true).first().getScroll();
26239             
26240             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26241                 placement = 'top';
26242             }
26243             
26244             align = this.alignment[placement];
26245         }
26246         
26247         this.el.alignTo(this.bindEl, align[0],align[1]);
26248         //var arrow = this.el.select('.arrow',true).first();
26249         //arrow.set(align[2], 
26250         
26251         this.el.addClass(placement);
26252         
26253         this.el.addClass('in fade');
26254         
26255         this.hoverState = null;
26256         
26257         if (this.el.hasClass('fade')) {
26258             // fade it?
26259         }
26260         
26261     },
26262     hide : function()
26263     {
26264          
26265         if (!this.el) {
26266             return;
26267         }
26268         //this.el.setXY([0,0]);
26269         this.el.removeClass('in');
26270         //this.el.hide();
26271         
26272     }
26273     
26274 });
26275  
26276
26277  /*
26278  * - LGPL
26279  *
26280  * Location Picker
26281  * 
26282  */
26283
26284 /**
26285  * @class Roo.bootstrap.LocationPicker
26286  * @extends Roo.bootstrap.Component
26287  * Bootstrap LocationPicker class
26288  * @cfg {Number} latitude Position when init default 0
26289  * @cfg {Number} longitude Position when init default 0
26290  * @cfg {Number} zoom default 15
26291  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26292  * @cfg {Boolean} mapTypeControl default false
26293  * @cfg {Boolean} disableDoubleClickZoom default false
26294  * @cfg {Boolean} scrollwheel default true
26295  * @cfg {Boolean} streetViewControl default false
26296  * @cfg {Number} radius default 0
26297  * @cfg {String} locationName
26298  * @cfg {Boolean} draggable default true
26299  * @cfg {Boolean} enableAutocomplete default false
26300  * @cfg {Boolean} enableReverseGeocode default true
26301  * @cfg {String} markerTitle
26302  * 
26303  * @constructor
26304  * Create a new LocationPicker
26305  * @param {Object} config The config object
26306  */
26307
26308
26309 Roo.bootstrap.LocationPicker = function(config){
26310     
26311     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26312     
26313     this.addEvents({
26314         /**
26315          * @event initial
26316          * Fires when the picker initialized.
26317          * @param {Roo.bootstrap.LocationPicker} this
26318          * @param {Google Location} location
26319          */
26320         initial : true,
26321         /**
26322          * @event positionchanged
26323          * Fires when the picker position changed.
26324          * @param {Roo.bootstrap.LocationPicker} this
26325          * @param {Google Location} location
26326          */
26327         positionchanged : true,
26328         /**
26329          * @event resize
26330          * Fires when the map resize.
26331          * @param {Roo.bootstrap.LocationPicker} this
26332          */
26333         resize : true,
26334         /**
26335          * @event show
26336          * Fires when the map show.
26337          * @param {Roo.bootstrap.LocationPicker} this
26338          */
26339         show : true,
26340         /**
26341          * @event hide
26342          * Fires when the map hide.
26343          * @param {Roo.bootstrap.LocationPicker} this
26344          */
26345         hide : true,
26346         /**
26347          * @event mapClick
26348          * Fires when click the map.
26349          * @param {Roo.bootstrap.LocationPicker} this
26350          * @param {Map event} e
26351          */
26352         mapClick : true,
26353         /**
26354          * @event mapRightClick
26355          * Fires when right click the map.
26356          * @param {Roo.bootstrap.LocationPicker} this
26357          * @param {Map event} e
26358          */
26359         mapRightClick : true,
26360         /**
26361          * @event markerClick
26362          * Fires when click the marker.
26363          * @param {Roo.bootstrap.LocationPicker} this
26364          * @param {Map event} e
26365          */
26366         markerClick : true,
26367         /**
26368          * @event markerRightClick
26369          * Fires when right click the marker.
26370          * @param {Roo.bootstrap.LocationPicker} this
26371          * @param {Map event} e
26372          */
26373         markerRightClick : true,
26374         /**
26375          * @event OverlayViewDraw
26376          * Fires when OverlayView Draw
26377          * @param {Roo.bootstrap.LocationPicker} this
26378          */
26379         OverlayViewDraw : true,
26380         /**
26381          * @event OverlayViewOnAdd
26382          * Fires when OverlayView Draw
26383          * @param {Roo.bootstrap.LocationPicker} this
26384          */
26385         OverlayViewOnAdd : true,
26386         /**
26387          * @event OverlayViewOnRemove
26388          * Fires when OverlayView Draw
26389          * @param {Roo.bootstrap.LocationPicker} this
26390          */
26391         OverlayViewOnRemove : true,
26392         /**
26393          * @event OverlayViewShow
26394          * Fires when OverlayView Draw
26395          * @param {Roo.bootstrap.LocationPicker} this
26396          * @param {Pixel} cpx
26397          */
26398         OverlayViewShow : true,
26399         /**
26400          * @event OverlayViewHide
26401          * Fires when OverlayView Draw
26402          * @param {Roo.bootstrap.LocationPicker} this
26403          */
26404         OverlayViewHide : true,
26405         /**
26406          * @event loadexception
26407          * Fires when load google lib failed.
26408          * @param {Roo.bootstrap.LocationPicker} this
26409          */
26410         loadexception : true
26411     });
26412         
26413 };
26414
26415 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26416     
26417     gMapContext: false,
26418     
26419     latitude: 0,
26420     longitude: 0,
26421     zoom: 15,
26422     mapTypeId: false,
26423     mapTypeControl: false,
26424     disableDoubleClickZoom: false,
26425     scrollwheel: true,
26426     streetViewControl: false,
26427     radius: 0,
26428     locationName: '',
26429     draggable: true,
26430     enableAutocomplete: false,
26431     enableReverseGeocode: true,
26432     markerTitle: '',
26433     
26434     getAutoCreate: function()
26435     {
26436
26437         var cfg = {
26438             tag: 'div',
26439             cls: 'roo-location-picker'
26440         };
26441         
26442         return cfg
26443     },
26444     
26445     initEvents: function(ct, position)
26446     {       
26447         if(!this.el.getWidth() || this.isApplied()){
26448             return;
26449         }
26450         
26451         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26452         
26453         this.initial();
26454     },
26455     
26456     initial: function()
26457     {
26458         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26459             this.fireEvent('loadexception', this);
26460             return;
26461         }
26462         
26463         if(!this.mapTypeId){
26464             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26465         }
26466         
26467         this.gMapContext = this.GMapContext();
26468         
26469         this.initOverlayView();
26470         
26471         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26472         
26473         var _this = this;
26474                 
26475         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26476             _this.setPosition(_this.gMapContext.marker.position);
26477         });
26478         
26479         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26480             _this.fireEvent('mapClick', this, event);
26481             
26482         });
26483
26484         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26485             _this.fireEvent('mapRightClick', this, event);
26486             
26487         });
26488         
26489         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26490             _this.fireEvent('markerClick', this, event);
26491             
26492         });
26493
26494         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26495             _this.fireEvent('markerRightClick', this, event);
26496             
26497         });
26498         
26499         this.setPosition(this.gMapContext.location);
26500         
26501         this.fireEvent('initial', this, this.gMapContext.location);
26502     },
26503     
26504     initOverlayView: function()
26505     {
26506         var _this = this;
26507         
26508         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26509             
26510             draw: function()
26511             {
26512                 _this.fireEvent('OverlayViewDraw', _this);
26513             },
26514             
26515             onAdd: function()
26516             {
26517                 _this.fireEvent('OverlayViewOnAdd', _this);
26518             },
26519             
26520             onRemove: function()
26521             {
26522                 _this.fireEvent('OverlayViewOnRemove', _this);
26523             },
26524             
26525             show: function(cpx)
26526             {
26527                 _this.fireEvent('OverlayViewShow', _this, cpx);
26528             },
26529             
26530             hide: function()
26531             {
26532                 _this.fireEvent('OverlayViewHide', _this);
26533             }
26534             
26535         });
26536     },
26537     
26538     fromLatLngToContainerPixel: function(event)
26539     {
26540         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26541     },
26542     
26543     isApplied: function() 
26544     {
26545         return this.getGmapContext() == false ? false : true;
26546     },
26547     
26548     getGmapContext: function() 
26549     {
26550         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26551     },
26552     
26553     GMapContext: function() 
26554     {
26555         var position = new google.maps.LatLng(this.latitude, this.longitude);
26556         
26557         var _map = new google.maps.Map(this.el.dom, {
26558             center: position,
26559             zoom: this.zoom,
26560             mapTypeId: this.mapTypeId,
26561             mapTypeControl: this.mapTypeControl,
26562             disableDoubleClickZoom: this.disableDoubleClickZoom,
26563             scrollwheel: this.scrollwheel,
26564             streetViewControl: this.streetViewControl,
26565             locationName: this.locationName,
26566             draggable: this.draggable,
26567             enableAutocomplete: this.enableAutocomplete,
26568             enableReverseGeocode: this.enableReverseGeocode
26569         });
26570         
26571         var _marker = new google.maps.Marker({
26572             position: position,
26573             map: _map,
26574             title: this.markerTitle,
26575             draggable: this.draggable
26576         });
26577         
26578         return {
26579             map: _map,
26580             marker: _marker,
26581             circle: null,
26582             location: position,
26583             radius: this.radius,
26584             locationName: this.locationName,
26585             addressComponents: {
26586                 formatted_address: null,
26587                 addressLine1: null,
26588                 addressLine2: null,
26589                 streetName: null,
26590                 streetNumber: null,
26591                 city: null,
26592                 district: null,
26593                 state: null,
26594                 stateOrProvince: null
26595             },
26596             settings: this,
26597             domContainer: this.el.dom,
26598             geodecoder: new google.maps.Geocoder()
26599         };
26600     },
26601     
26602     drawCircle: function(center, radius, options) 
26603     {
26604         if (this.gMapContext.circle != null) {
26605             this.gMapContext.circle.setMap(null);
26606         }
26607         if (radius > 0) {
26608             radius *= 1;
26609             options = Roo.apply({}, options, {
26610                 strokeColor: "#0000FF",
26611                 strokeOpacity: .35,
26612                 strokeWeight: 2,
26613                 fillColor: "#0000FF",
26614                 fillOpacity: .2
26615             });
26616             
26617             options.map = this.gMapContext.map;
26618             options.radius = radius;
26619             options.center = center;
26620             this.gMapContext.circle = new google.maps.Circle(options);
26621             return this.gMapContext.circle;
26622         }
26623         
26624         return null;
26625     },
26626     
26627     setPosition: function(location) 
26628     {
26629         this.gMapContext.location = location;
26630         this.gMapContext.marker.setPosition(location);
26631         this.gMapContext.map.panTo(location);
26632         this.drawCircle(location, this.gMapContext.radius, {});
26633         
26634         var _this = this;
26635         
26636         if (this.gMapContext.settings.enableReverseGeocode) {
26637             this.gMapContext.geodecoder.geocode({
26638                 latLng: this.gMapContext.location
26639             }, function(results, status) {
26640                 
26641                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26642                     _this.gMapContext.locationName = results[0].formatted_address;
26643                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26644                     
26645                     _this.fireEvent('positionchanged', this, location);
26646                 }
26647             });
26648             
26649             return;
26650         }
26651         
26652         this.fireEvent('positionchanged', this, location);
26653     },
26654     
26655     resize: function()
26656     {
26657         google.maps.event.trigger(this.gMapContext.map, "resize");
26658         
26659         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26660         
26661         this.fireEvent('resize', this);
26662     },
26663     
26664     setPositionByLatLng: function(latitude, longitude)
26665     {
26666         this.setPosition(new google.maps.LatLng(latitude, longitude));
26667     },
26668     
26669     getCurrentPosition: function() 
26670     {
26671         return {
26672             latitude: this.gMapContext.location.lat(),
26673             longitude: this.gMapContext.location.lng()
26674         };
26675     },
26676     
26677     getAddressName: function() 
26678     {
26679         return this.gMapContext.locationName;
26680     },
26681     
26682     getAddressComponents: function() 
26683     {
26684         return this.gMapContext.addressComponents;
26685     },
26686     
26687     address_component_from_google_geocode: function(address_components) 
26688     {
26689         var result = {};
26690         
26691         for (var i = 0; i < address_components.length; i++) {
26692             var component = address_components[i];
26693             if (component.types.indexOf("postal_code") >= 0) {
26694                 result.postalCode = component.short_name;
26695             } else if (component.types.indexOf("street_number") >= 0) {
26696                 result.streetNumber = component.short_name;
26697             } else if (component.types.indexOf("route") >= 0) {
26698                 result.streetName = component.short_name;
26699             } else if (component.types.indexOf("neighborhood") >= 0) {
26700                 result.city = component.short_name;
26701             } else if (component.types.indexOf("locality") >= 0) {
26702                 result.city = component.short_name;
26703             } else if (component.types.indexOf("sublocality") >= 0) {
26704                 result.district = component.short_name;
26705             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26706                 result.stateOrProvince = component.short_name;
26707             } else if (component.types.indexOf("country") >= 0) {
26708                 result.country = component.short_name;
26709             }
26710         }
26711         
26712         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26713         result.addressLine2 = "";
26714         return result;
26715     },
26716     
26717     setZoomLevel: function(zoom)
26718     {
26719         this.gMapContext.map.setZoom(zoom);
26720     },
26721     
26722     show: function()
26723     {
26724         if(!this.el){
26725             return;
26726         }
26727         
26728         this.el.show();
26729         
26730         this.resize();
26731         
26732         this.fireEvent('show', this);
26733     },
26734     
26735     hide: function()
26736     {
26737         if(!this.el){
26738             return;
26739         }
26740         
26741         this.el.hide();
26742         
26743         this.fireEvent('hide', this);
26744     }
26745     
26746 });
26747
26748 Roo.apply(Roo.bootstrap.LocationPicker, {
26749     
26750     OverlayView : function(map, options)
26751     {
26752         options = options || {};
26753         
26754         this.setMap(map);
26755     }
26756     
26757     
26758 });/*
26759  * - LGPL
26760  *
26761  * Alert
26762  * 
26763  */
26764
26765 /**
26766  * @class Roo.bootstrap.Alert
26767  * @extends Roo.bootstrap.Component
26768  * Bootstrap Alert class
26769  * @cfg {String} title The title of alert
26770  * @cfg {String} html The content of alert
26771  * @cfg {String} weight (  success | info | warning | danger )
26772  * @cfg {String} faicon font-awesomeicon
26773  * 
26774  * @constructor
26775  * Create a new alert
26776  * @param {Object} config The config object
26777  */
26778
26779
26780 Roo.bootstrap.Alert = function(config){
26781     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26782     
26783 };
26784
26785 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26786     
26787     title: '',
26788     html: '',
26789     weight: false,
26790     faicon: false,
26791     
26792     getAutoCreate : function()
26793     {
26794         
26795         var cfg = {
26796             tag : 'div',
26797             cls : 'alert',
26798             cn : [
26799                 {
26800                     tag : 'i',
26801                     cls : 'roo-alert-icon'
26802                     
26803                 },
26804                 {
26805                     tag : 'b',
26806                     cls : 'roo-alert-title',
26807                     html : this.title
26808                 },
26809                 {
26810                     tag : 'span',
26811                     cls : 'roo-alert-text',
26812                     html : this.html
26813                 }
26814             ]
26815         };
26816         
26817         if(this.faicon){
26818             cfg.cn[0].cls += ' fa ' + this.faicon;
26819         }
26820         
26821         if(this.weight){
26822             cfg.cls += ' alert-' + this.weight;
26823         }
26824         
26825         return cfg;
26826     },
26827     
26828     initEvents: function() 
26829     {
26830         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26831     },
26832     
26833     setTitle : function(str)
26834     {
26835         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26836     },
26837     
26838     setText : function(str)
26839     {
26840         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26841     },
26842     
26843     setWeight : function(weight)
26844     {
26845         if(this.weight){
26846             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26847         }
26848         
26849         this.weight = weight;
26850         
26851         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26852     },
26853     
26854     setIcon : function(icon)
26855     {
26856         if(this.faicon){
26857             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26858         }
26859         
26860         this.faicon = icon;
26861         
26862         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26863     },
26864     
26865     hide: function() 
26866     {
26867         this.el.hide();   
26868     },
26869     
26870     show: function() 
26871     {  
26872         this.el.show();   
26873     }
26874     
26875 });
26876
26877  
26878 /*
26879 * Licence: LGPL
26880 */
26881
26882 /**
26883  * @class Roo.bootstrap.UploadCropbox
26884  * @extends Roo.bootstrap.Component
26885  * Bootstrap UploadCropbox class
26886  * @cfg {String} emptyText show when image has been loaded
26887  * @cfg {String} rotateNotify show when image too small to rotate
26888  * @cfg {Number} errorTimeout default 3000
26889  * @cfg {Number} minWidth default 300
26890  * @cfg {Number} minHeight default 300
26891  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26892  * @cfg {Boolean} isDocument (true|false) default false
26893  * @cfg {String} url action url
26894  * @cfg {String} paramName default 'imageUpload'
26895  * @cfg {String} method default POST
26896  * @cfg {Boolean} loadMask (true|false) default true
26897  * @cfg {Boolean} loadingText default 'Loading...'
26898  * 
26899  * @constructor
26900  * Create a new UploadCropbox
26901  * @param {Object} config The config object
26902  */
26903
26904 Roo.bootstrap.UploadCropbox = function(config){
26905     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26906     
26907     this.addEvents({
26908         /**
26909          * @event beforeselectfile
26910          * Fire before select file
26911          * @param {Roo.bootstrap.UploadCropbox} this
26912          */
26913         "beforeselectfile" : true,
26914         /**
26915          * @event initial
26916          * Fire after initEvent
26917          * @param {Roo.bootstrap.UploadCropbox} this
26918          */
26919         "initial" : true,
26920         /**
26921          * @event crop
26922          * Fire after initEvent
26923          * @param {Roo.bootstrap.UploadCropbox} this
26924          * @param {String} data
26925          */
26926         "crop" : true,
26927         /**
26928          * @event prepare
26929          * Fire when preparing the file data
26930          * @param {Roo.bootstrap.UploadCropbox} this
26931          * @param {Object} file
26932          */
26933         "prepare" : true,
26934         /**
26935          * @event exception
26936          * Fire when get exception
26937          * @param {Roo.bootstrap.UploadCropbox} this
26938          * @param {XMLHttpRequest} xhr
26939          */
26940         "exception" : true,
26941         /**
26942          * @event beforeloadcanvas
26943          * Fire before load the canvas
26944          * @param {Roo.bootstrap.UploadCropbox} this
26945          * @param {String} src
26946          */
26947         "beforeloadcanvas" : true,
26948         /**
26949          * @event trash
26950          * Fire when trash image
26951          * @param {Roo.bootstrap.UploadCropbox} this
26952          */
26953         "trash" : true,
26954         /**
26955          * @event download
26956          * Fire when download the image
26957          * @param {Roo.bootstrap.UploadCropbox} this
26958          */
26959         "download" : true,
26960         /**
26961          * @event footerbuttonclick
26962          * Fire when footerbuttonclick
26963          * @param {Roo.bootstrap.UploadCropbox} this
26964          * @param {String} type
26965          */
26966         "footerbuttonclick" : true,
26967         /**
26968          * @event resize
26969          * Fire when resize
26970          * @param {Roo.bootstrap.UploadCropbox} this
26971          */
26972         "resize" : true,
26973         /**
26974          * @event rotate
26975          * Fire when rotate the image
26976          * @param {Roo.bootstrap.UploadCropbox} this
26977          * @param {String} pos
26978          */
26979         "rotate" : true,
26980         /**
26981          * @event inspect
26982          * Fire when inspect the file
26983          * @param {Roo.bootstrap.UploadCropbox} this
26984          * @param {Object} file
26985          */
26986         "inspect" : true,
26987         /**
26988          * @event upload
26989          * Fire when xhr upload the file
26990          * @param {Roo.bootstrap.UploadCropbox} this
26991          * @param {Object} data
26992          */
26993         "upload" : true,
26994         /**
26995          * @event arrange
26996          * Fire when arrange the file data
26997          * @param {Roo.bootstrap.UploadCropbox} this
26998          * @param {Object} formData
26999          */
27000         "arrange" : true
27001     });
27002     
27003     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27004 };
27005
27006 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27007     
27008     emptyText : 'Click to upload image',
27009     rotateNotify : 'Image is too small to rotate',
27010     errorTimeout : 3000,
27011     scale : 0,
27012     baseScale : 1,
27013     rotate : 0,
27014     dragable : false,
27015     pinching : false,
27016     mouseX : 0,
27017     mouseY : 0,
27018     cropData : false,
27019     minWidth : 300,
27020     minHeight : 300,
27021     file : false,
27022     exif : {},
27023     baseRotate : 1,
27024     cropType : 'image/jpeg',
27025     buttons : false,
27026     canvasLoaded : false,
27027     isDocument : false,
27028     method : 'POST',
27029     paramName : 'imageUpload',
27030     loadMask : true,
27031     loadingText : 'Loading...',
27032     maskEl : false,
27033     
27034     getAutoCreate : function()
27035     {
27036         var cfg = {
27037             tag : 'div',
27038             cls : 'roo-upload-cropbox',
27039             cn : [
27040                 {
27041                     tag : 'input',
27042                     cls : 'roo-upload-cropbox-selector',
27043                     type : 'file'
27044                 },
27045                 {
27046                     tag : 'div',
27047                     cls : 'roo-upload-cropbox-body',
27048                     style : 'cursor:pointer',
27049                     cn : [
27050                         {
27051                             tag : 'div',
27052                             cls : 'roo-upload-cropbox-preview'
27053                         },
27054                         {
27055                             tag : 'div',
27056                             cls : 'roo-upload-cropbox-thumb'
27057                         },
27058                         {
27059                             tag : 'div',
27060                             cls : 'roo-upload-cropbox-empty-notify',
27061                             html : this.emptyText
27062                         },
27063                         {
27064                             tag : 'div',
27065                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27066                             html : this.rotateNotify
27067                         }
27068                     ]
27069                 },
27070                 {
27071                     tag : 'div',
27072                     cls : 'roo-upload-cropbox-footer',
27073                     cn : {
27074                         tag : 'div',
27075                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27076                         cn : []
27077                     }
27078                 }
27079             ]
27080         };
27081         
27082         return cfg;
27083     },
27084     
27085     onRender : function(ct, position)
27086     {
27087         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27088         
27089         if (this.buttons.length) {
27090             
27091             Roo.each(this.buttons, function(bb) {
27092                 
27093                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27094                 
27095                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27096                 
27097             }, this);
27098         }
27099         
27100         if(this.loadMask){
27101             this.maskEl = this.el;
27102         }
27103     },
27104     
27105     initEvents : function()
27106     {
27107         this.urlAPI = (window.createObjectURL && window) || 
27108                                 (window.URL && URL.revokeObjectURL && URL) || 
27109                                 (window.webkitURL && webkitURL);
27110                         
27111         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27112         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27113         
27114         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27115         this.selectorEl.hide();
27116         
27117         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27118         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27119         
27120         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27121         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27122         this.thumbEl.hide();
27123         
27124         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27125         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27126         
27127         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27128         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27129         this.errorEl.hide();
27130         
27131         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27132         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27133         this.footerEl.hide();
27134         
27135         this.setThumbBoxSize();
27136         
27137         this.bind();
27138         
27139         this.resize();
27140         
27141         this.fireEvent('initial', this);
27142     },
27143
27144     bind : function()
27145     {
27146         var _this = this;
27147         
27148         window.addEventListener("resize", function() { _this.resize(); } );
27149         
27150         this.bodyEl.on('click', this.beforeSelectFile, this);
27151         
27152         if(Roo.isTouch){
27153             this.bodyEl.on('touchstart', this.onTouchStart, this);
27154             this.bodyEl.on('touchmove', this.onTouchMove, this);
27155             this.bodyEl.on('touchend', this.onTouchEnd, this);
27156         }
27157         
27158         if(!Roo.isTouch){
27159             this.bodyEl.on('mousedown', this.onMouseDown, this);
27160             this.bodyEl.on('mousemove', this.onMouseMove, this);
27161             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27162             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27163             Roo.get(document).on('mouseup', this.onMouseUp, this);
27164         }
27165         
27166         this.selectorEl.on('change', this.onFileSelected, this);
27167     },
27168     
27169     reset : function()
27170     {    
27171         this.scale = 0;
27172         this.baseScale = 1;
27173         this.rotate = 0;
27174         this.baseRotate = 1;
27175         this.dragable = false;
27176         this.pinching = false;
27177         this.mouseX = 0;
27178         this.mouseY = 0;
27179         this.cropData = false;
27180         this.notifyEl.dom.innerHTML = this.emptyText;
27181         
27182         this.selectorEl.dom.value = '';
27183         
27184     },
27185     
27186     resize : function()
27187     {
27188         if(this.fireEvent('resize', this) != false){
27189             this.setThumbBoxPosition();
27190             this.setCanvasPosition();
27191         }
27192     },
27193     
27194     onFooterButtonClick : function(e, el, o, type)
27195     {
27196         switch (type) {
27197             case 'rotate-left' :
27198                 this.onRotateLeft(e);
27199                 break;
27200             case 'rotate-right' :
27201                 this.onRotateRight(e);
27202                 break;
27203             case 'picture' :
27204                 this.beforeSelectFile(e);
27205                 break;
27206             case 'trash' :
27207                 this.trash(e);
27208                 break;
27209             case 'crop' :
27210                 this.crop(e);
27211                 break;
27212             case 'download' :
27213                 this.download(e);
27214                 break;
27215             default :
27216                 break;
27217         }
27218         
27219         this.fireEvent('footerbuttonclick', this, type);
27220     },
27221     
27222     beforeSelectFile : function(e)
27223     {
27224         e.preventDefault();
27225         
27226         if(this.fireEvent('beforeselectfile', this) != false){
27227             this.selectorEl.dom.click();
27228         }
27229     },
27230     
27231     onFileSelected : function(e)
27232     {
27233         e.preventDefault();
27234         
27235         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27236             return;
27237         }
27238         
27239         var file = this.selectorEl.dom.files[0];
27240         
27241         if(this.fireEvent('inspect', this, file) != false){
27242             this.prepare(file);
27243         }
27244         
27245     },
27246     
27247     trash : function(e)
27248     {
27249         this.fireEvent('trash', this);
27250     },
27251     
27252     download : function(e)
27253     {
27254         this.fireEvent('download', this);
27255     },
27256     
27257     loadCanvas : function(src)
27258     {   
27259         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27260             
27261             this.reset();
27262             
27263             this.imageEl = document.createElement('img');
27264             
27265             var _this = this;
27266             
27267             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27268             
27269             this.imageEl.src = src;
27270         }
27271     },
27272     
27273     onLoadCanvas : function()
27274     {   
27275         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27276         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27277         
27278         this.bodyEl.un('click', this.beforeSelectFile, this);
27279         
27280         this.notifyEl.hide();
27281         this.thumbEl.show();
27282         this.footerEl.show();
27283         
27284         this.baseRotateLevel();
27285         
27286         if(this.isDocument){
27287             this.setThumbBoxSize();
27288         }
27289         
27290         this.setThumbBoxPosition();
27291         
27292         this.baseScaleLevel();
27293         
27294         this.draw();
27295         
27296         this.resize();
27297         
27298         this.canvasLoaded = true;
27299         
27300         if(this.loadMask){
27301             this.maskEl.unmask();
27302         }
27303         
27304     },
27305     
27306     setCanvasPosition : function()
27307     {   
27308         if(!this.canvasEl){
27309             return;
27310         }
27311         
27312         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27313         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27314         
27315         this.previewEl.setLeft(pw);
27316         this.previewEl.setTop(ph);
27317         
27318     },
27319     
27320     onMouseDown : function(e)
27321     {   
27322         e.stopEvent();
27323         
27324         this.dragable = true;
27325         this.pinching = false;
27326         
27327         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27328             this.dragable = false;
27329             return;
27330         }
27331         
27332         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27333         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27334         
27335     },
27336     
27337     onMouseMove : function(e)
27338     {   
27339         e.stopEvent();
27340         
27341         if(!this.canvasLoaded){
27342             return;
27343         }
27344         
27345         if (!this.dragable){
27346             return;
27347         }
27348         
27349         var minX = Math.ceil(this.thumbEl.getLeft(true));
27350         var minY = Math.ceil(this.thumbEl.getTop(true));
27351         
27352         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27353         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27354         
27355         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27356         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27357         
27358         x = x - this.mouseX;
27359         y = y - this.mouseY;
27360         
27361         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27362         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27363         
27364         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27365         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27366         
27367         this.previewEl.setLeft(bgX);
27368         this.previewEl.setTop(bgY);
27369         
27370         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27371         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27372     },
27373     
27374     onMouseUp : function(e)
27375     {   
27376         e.stopEvent();
27377         
27378         this.dragable = false;
27379     },
27380     
27381     onMouseWheel : function(e)
27382     {   
27383         e.stopEvent();
27384         
27385         this.startScale = this.scale;
27386         
27387         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27388         
27389         if(!this.zoomable()){
27390             this.scale = this.startScale;
27391             return;
27392         }
27393         
27394         this.draw();
27395         
27396         return;
27397     },
27398     
27399     zoomable : function()
27400     {
27401         var minScale = this.thumbEl.getWidth() / this.minWidth;
27402         
27403         if(this.minWidth < this.minHeight){
27404             minScale = this.thumbEl.getHeight() / this.minHeight;
27405         }
27406         
27407         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27408         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27409         
27410         if(
27411                 this.isDocument &&
27412                 (this.rotate == 0 || this.rotate == 180) && 
27413                 (
27414                     width > this.imageEl.OriginWidth || 
27415                     height > this.imageEl.OriginHeight ||
27416                     (width < this.minWidth && height < this.minHeight)
27417                 )
27418         ){
27419             return false;
27420         }
27421         
27422         if(
27423                 this.isDocument &&
27424                 (this.rotate == 90 || this.rotate == 270) && 
27425                 (
27426                     width > this.imageEl.OriginWidth || 
27427                     height > this.imageEl.OriginHeight ||
27428                     (width < this.minHeight && height < this.minWidth)
27429                 )
27430         ){
27431             return false;
27432         }
27433         
27434         if(
27435                 !this.isDocument &&
27436                 (this.rotate == 0 || this.rotate == 180) && 
27437                 (
27438                     width < this.minWidth || 
27439                     width > this.imageEl.OriginWidth || 
27440                     height < this.minHeight || 
27441                     height > this.imageEl.OriginHeight
27442                 )
27443         ){
27444             return false;
27445         }
27446         
27447         if(
27448                 !this.isDocument &&
27449                 (this.rotate == 90 || this.rotate == 270) && 
27450                 (
27451                     width < this.minHeight || 
27452                     width > this.imageEl.OriginWidth || 
27453                     height < this.minWidth || 
27454                     height > this.imageEl.OriginHeight
27455                 )
27456         ){
27457             return false;
27458         }
27459         
27460         return true;
27461         
27462     },
27463     
27464     onRotateLeft : function(e)
27465     {   
27466         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27467             
27468             var minScale = this.thumbEl.getWidth() / this.minWidth;
27469             
27470             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27471             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27472             
27473             this.startScale = this.scale;
27474             
27475             while (this.getScaleLevel() < minScale){
27476             
27477                 this.scale = this.scale + 1;
27478                 
27479                 if(!this.zoomable()){
27480                     break;
27481                 }
27482                 
27483                 if(
27484                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27485                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27486                 ){
27487                     continue;
27488                 }
27489                 
27490                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27491
27492                 this.draw();
27493                 
27494                 return;
27495             }
27496             
27497             this.scale = this.startScale;
27498             
27499             this.onRotateFail();
27500             
27501             return false;
27502         }
27503         
27504         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27505
27506         if(this.isDocument){
27507             this.setThumbBoxSize();
27508             this.setThumbBoxPosition();
27509             this.setCanvasPosition();
27510         }
27511         
27512         this.draw();
27513         
27514         this.fireEvent('rotate', this, 'left');
27515         
27516     },
27517     
27518     onRotateRight : function(e)
27519     {
27520         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27521             
27522             var minScale = this.thumbEl.getWidth() / this.minWidth;
27523         
27524             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27525             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27526             
27527             this.startScale = this.scale;
27528             
27529             while (this.getScaleLevel() < minScale){
27530             
27531                 this.scale = this.scale + 1;
27532                 
27533                 if(!this.zoomable()){
27534                     break;
27535                 }
27536                 
27537                 if(
27538                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27539                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27540                 ){
27541                     continue;
27542                 }
27543                 
27544                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27545
27546                 this.draw();
27547                 
27548                 return;
27549             }
27550             
27551             this.scale = this.startScale;
27552             
27553             this.onRotateFail();
27554             
27555             return false;
27556         }
27557         
27558         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27559
27560         if(this.isDocument){
27561             this.setThumbBoxSize();
27562             this.setThumbBoxPosition();
27563             this.setCanvasPosition();
27564         }
27565         
27566         this.draw();
27567         
27568         this.fireEvent('rotate', this, 'right');
27569     },
27570     
27571     onRotateFail : function()
27572     {
27573         this.errorEl.show(true);
27574         
27575         var _this = this;
27576         
27577         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27578     },
27579     
27580     draw : function()
27581     {
27582         this.previewEl.dom.innerHTML = '';
27583         
27584         var canvasEl = document.createElement("canvas");
27585         
27586         var contextEl = canvasEl.getContext("2d");
27587         
27588         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27589         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27590         var center = this.imageEl.OriginWidth / 2;
27591         
27592         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27593             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27594             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27595             center = this.imageEl.OriginHeight / 2;
27596         }
27597         
27598         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27599         
27600         contextEl.translate(center, center);
27601         contextEl.rotate(this.rotate * Math.PI / 180);
27602
27603         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27604         
27605         this.canvasEl = document.createElement("canvas");
27606         
27607         this.contextEl = this.canvasEl.getContext("2d");
27608         
27609         switch (this.rotate) {
27610             case 0 :
27611                 
27612                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27613                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27614                 
27615                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27616                 
27617                 break;
27618             case 90 : 
27619                 
27620                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27621                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27622                 
27623                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27624                     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);
27625                     break;
27626                 }
27627                 
27628                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27629                 
27630                 break;
27631             case 180 :
27632                 
27633                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27634                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27635                 
27636                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27637                     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);
27638                     break;
27639                 }
27640                 
27641                 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);
27642                 
27643                 break;
27644             case 270 :
27645                 
27646                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27647                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27648         
27649                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27650                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27651                     break;
27652                 }
27653                 
27654                 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);
27655                 
27656                 break;
27657             default : 
27658                 break;
27659         }
27660         
27661         this.previewEl.appendChild(this.canvasEl);
27662         
27663         this.setCanvasPosition();
27664     },
27665     
27666     crop : function()
27667     {
27668         if(!this.canvasLoaded){
27669             return;
27670         }
27671         
27672         var imageCanvas = document.createElement("canvas");
27673         
27674         var imageContext = imageCanvas.getContext("2d");
27675         
27676         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27677         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27678         
27679         var center = imageCanvas.width / 2;
27680         
27681         imageContext.translate(center, center);
27682         
27683         imageContext.rotate(this.rotate * Math.PI / 180);
27684         
27685         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27686         
27687         var canvas = document.createElement("canvas");
27688         
27689         var context = canvas.getContext("2d");
27690                 
27691         canvas.width = this.minWidth;
27692         canvas.height = this.minHeight;
27693
27694         switch (this.rotate) {
27695             case 0 :
27696                 
27697                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27698                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27699                 
27700                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27701                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27702                 
27703                 var targetWidth = this.minWidth - 2 * x;
27704                 var targetHeight = this.minHeight - 2 * y;
27705                 
27706                 var scale = 1;
27707                 
27708                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27709                     scale = targetWidth / width;
27710                 }
27711                 
27712                 if(x > 0 && y == 0){
27713                     scale = targetHeight / height;
27714                 }
27715                 
27716                 if(x > 0 && y > 0){
27717                     scale = targetWidth / width;
27718                     
27719                     if(width < height){
27720                         scale = targetHeight / height;
27721                     }
27722                 }
27723                 
27724                 context.scale(scale, scale);
27725                 
27726                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27727                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27728
27729                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27730                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27731
27732                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27733                 
27734                 break;
27735             case 90 : 
27736                 
27737                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27738                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27739                 
27740                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27741                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27742                 
27743                 var targetWidth = this.minWidth - 2 * x;
27744                 var targetHeight = this.minHeight - 2 * y;
27745                 
27746                 var scale = 1;
27747                 
27748                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27749                     scale = targetWidth / width;
27750                 }
27751                 
27752                 if(x > 0 && y == 0){
27753                     scale = targetHeight / height;
27754                 }
27755                 
27756                 if(x > 0 && y > 0){
27757                     scale = targetWidth / width;
27758                     
27759                     if(width < height){
27760                         scale = targetHeight / height;
27761                     }
27762                 }
27763                 
27764                 context.scale(scale, scale);
27765                 
27766                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27767                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27768
27769                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27770                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27771                 
27772                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27773                 
27774                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27775                 
27776                 break;
27777             case 180 :
27778                 
27779                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27780                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27781                 
27782                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27783                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27784                 
27785                 var targetWidth = this.minWidth - 2 * x;
27786                 var targetHeight = this.minHeight - 2 * y;
27787                 
27788                 var scale = 1;
27789                 
27790                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27791                     scale = targetWidth / width;
27792                 }
27793                 
27794                 if(x > 0 && y == 0){
27795                     scale = targetHeight / height;
27796                 }
27797                 
27798                 if(x > 0 && y > 0){
27799                     scale = targetWidth / width;
27800                     
27801                     if(width < height){
27802                         scale = targetHeight / height;
27803                     }
27804                 }
27805                 
27806                 context.scale(scale, scale);
27807                 
27808                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27809                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27810
27811                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27812                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27813
27814                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27815                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27816                 
27817                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27818                 
27819                 break;
27820             case 270 :
27821                 
27822                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27823                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27824                 
27825                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27826                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27827                 
27828                 var targetWidth = this.minWidth - 2 * x;
27829                 var targetHeight = this.minHeight - 2 * y;
27830                 
27831                 var scale = 1;
27832                 
27833                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27834                     scale = targetWidth / width;
27835                 }
27836                 
27837                 if(x > 0 && y == 0){
27838                     scale = targetHeight / height;
27839                 }
27840                 
27841                 if(x > 0 && y > 0){
27842                     scale = targetWidth / width;
27843                     
27844                     if(width < height){
27845                         scale = targetHeight / height;
27846                     }
27847                 }
27848                 
27849                 context.scale(scale, scale);
27850                 
27851                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27852                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27853
27854                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27855                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27856                 
27857                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27858                 
27859                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27860                 
27861                 break;
27862             default : 
27863                 break;
27864         }
27865         
27866         this.cropData = canvas.toDataURL(this.cropType);
27867         
27868         if(this.fireEvent('crop', this, this.cropData) !== false){
27869             this.process(this.file, this.cropData);
27870         }
27871         
27872         return;
27873         
27874     },
27875     
27876     setThumbBoxSize : function()
27877     {
27878         var width, height;
27879         
27880         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27881             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27882             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27883             
27884             this.minWidth = width;
27885             this.minHeight = height;
27886             
27887             if(this.rotate == 90 || this.rotate == 270){
27888                 this.minWidth = height;
27889                 this.minHeight = width;
27890             }
27891         }
27892         
27893         height = 300;
27894         width = Math.ceil(this.minWidth * height / this.minHeight);
27895         
27896         if(this.minWidth > this.minHeight){
27897             width = 300;
27898             height = Math.ceil(this.minHeight * width / this.minWidth);
27899         }
27900         
27901         this.thumbEl.setStyle({
27902             width : width + 'px',
27903             height : height + 'px'
27904         });
27905
27906         return;
27907             
27908     },
27909     
27910     setThumbBoxPosition : function()
27911     {
27912         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27913         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27914         
27915         this.thumbEl.setLeft(x);
27916         this.thumbEl.setTop(y);
27917         
27918     },
27919     
27920     baseRotateLevel : function()
27921     {
27922         this.baseRotate = 1;
27923         
27924         if(
27925                 typeof(this.exif) != 'undefined' &&
27926                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27927                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27928         ){
27929             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27930         }
27931         
27932         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27933         
27934     },
27935     
27936     baseScaleLevel : function()
27937     {
27938         var width, height;
27939         
27940         if(this.isDocument){
27941             
27942             if(this.baseRotate == 6 || this.baseRotate == 8){
27943             
27944                 height = this.thumbEl.getHeight();
27945                 this.baseScale = height / this.imageEl.OriginWidth;
27946
27947                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27948                     width = this.thumbEl.getWidth();
27949                     this.baseScale = width / this.imageEl.OriginHeight;
27950                 }
27951
27952                 return;
27953             }
27954
27955             height = this.thumbEl.getHeight();
27956             this.baseScale = height / this.imageEl.OriginHeight;
27957
27958             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27959                 width = this.thumbEl.getWidth();
27960                 this.baseScale = width / this.imageEl.OriginWidth;
27961             }
27962
27963             return;
27964         }
27965         
27966         if(this.baseRotate == 6 || this.baseRotate == 8){
27967             
27968             width = this.thumbEl.getHeight();
27969             this.baseScale = width / this.imageEl.OriginHeight;
27970             
27971             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27972                 height = this.thumbEl.getWidth();
27973                 this.baseScale = height / this.imageEl.OriginHeight;
27974             }
27975             
27976             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27977                 height = this.thumbEl.getWidth();
27978                 this.baseScale = height / this.imageEl.OriginHeight;
27979                 
27980                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27981                     width = this.thumbEl.getHeight();
27982                     this.baseScale = width / this.imageEl.OriginWidth;
27983                 }
27984             }
27985             
27986             return;
27987         }
27988         
27989         width = this.thumbEl.getWidth();
27990         this.baseScale = width / this.imageEl.OriginWidth;
27991         
27992         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27993             height = this.thumbEl.getHeight();
27994             this.baseScale = height / this.imageEl.OriginHeight;
27995         }
27996         
27997         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27998             
27999             height = this.thumbEl.getHeight();
28000             this.baseScale = height / this.imageEl.OriginHeight;
28001             
28002             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28003                 width = this.thumbEl.getWidth();
28004                 this.baseScale = width / this.imageEl.OriginWidth;
28005             }
28006             
28007         }
28008         
28009         return;
28010     },
28011     
28012     getScaleLevel : function()
28013     {
28014         return this.baseScale * Math.pow(1.1, this.scale);
28015     },
28016     
28017     onTouchStart : function(e)
28018     {
28019         if(!this.canvasLoaded){
28020             this.beforeSelectFile(e);
28021             return;
28022         }
28023         
28024         var touches = e.browserEvent.touches;
28025         
28026         if(!touches){
28027             return;
28028         }
28029         
28030         if(touches.length == 1){
28031             this.onMouseDown(e);
28032             return;
28033         }
28034         
28035         if(touches.length != 2){
28036             return;
28037         }
28038         
28039         var coords = [];
28040         
28041         for(var i = 0, finger; finger = touches[i]; i++){
28042             coords.push(finger.pageX, finger.pageY);
28043         }
28044         
28045         var x = Math.pow(coords[0] - coords[2], 2);
28046         var y = Math.pow(coords[1] - coords[3], 2);
28047         
28048         this.startDistance = Math.sqrt(x + y);
28049         
28050         this.startScale = this.scale;
28051         
28052         this.pinching = true;
28053         this.dragable = false;
28054         
28055     },
28056     
28057     onTouchMove : function(e)
28058     {
28059         if(!this.pinching && !this.dragable){
28060             return;
28061         }
28062         
28063         var touches = e.browserEvent.touches;
28064         
28065         if(!touches){
28066             return;
28067         }
28068         
28069         if(this.dragable){
28070             this.onMouseMove(e);
28071             return;
28072         }
28073         
28074         var coords = [];
28075         
28076         for(var i = 0, finger; finger = touches[i]; i++){
28077             coords.push(finger.pageX, finger.pageY);
28078         }
28079         
28080         var x = Math.pow(coords[0] - coords[2], 2);
28081         var y = Math.pow(coords[1] - coords[3], 2);
28082         
28083         this.endDistance = Math.sqrt(x + y);
28084         
28085         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28086         
28087         if(!this.zoomable()){
28088             this.scale = this.startScale;
28089             return;
28090         }
28091         
28092         this.draw();
28093         
28094     },
28095     
28096     onTouchEnd : function(e)
28097     {
28098         this.pinching = false;
28099         this.dragable = false;
28100         
28101     },
28102     
28103     process : function(file, crop)
28104     {
28105         if(this.loadMask){
28106             this.maskEl.mask(this.loadingText);
28107         }
28108         
28109         this.xhr = new XMLHttpRequest();
28110         
28111         file.xhr = this.xhr;
28112
28113         this.xhr.open(this.method, this.url, true);
28114         
28115         var headers = {
28116             "Accept": "application/json",
28117             "Cache-Control": "no-cache",
28118             "X-Requested-With": "XMLHttpRequest"
28119         };
28120         
28121         for (var headerName in headers) {
28122             var headerValue = headers[headerName];
28123             if (headerValue) {
28124                 this.xhr.setRequestHeader(headerName, headerValue);
28125             }
28126         }
28127         
28128         var _this = this;
28129         
28130         this.xhr.onload = function()
28131         {
28132             _this.xhrOnLoad(_this.xhr);
28133         }
28134         
28135         this.xhr.onerror = function()
28136         {
28137             _this.xhrOnError(_this.xhr);
28138         }
28139         
28140         var formData = new FormData();
28141
28142         formData.append('returnHTML', 'NO');
28143         
28144         if(crop){
28145             formData.append('crop', crop);
28146         }
28147         
28148         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28149             formData.append(this.paramName, file, file.name);
28150         }
28151         
28152         if(typeof(file.filename) != 'undefined'){
28153             formData.append('filename', file.filename);
28154         }
28155         
28156         if(typeof(file.mimetype) != 'undefined'){
28157             formData.append('mimetype', file.mimetype);
28158         }
28159         
28160         if(this.fireEvent('arrange', this, formData) != false){
28161             this.xhr.send(formData);
28162         };
28163     },
28164     
28165     xhrOnLoad : function(xhr)
28166     {
28167         if(this.loadMask){
28168             this.maskEl.unmask();
28169         }
28170         
28171         if (xhr.readyState !== 4) {
28172             this.fireEvent('exception', this, xhr);
28173             return;
28174         }
28175
28176         var response = Roo.decode(xhr.responseText);
28177         
28178         if(!response.success){
28179             this.fireEvent('exception', this, xhr);
28180             return;
28181         }
28182         
28183         var response = Roo.decode(xhr.responseText);
28184         
28185         this.fireEvent('upload', this, response);
28186         
28187     },
28188     
28189     xhrOnError : function()
28190     {
28191         if(this.loadMask){
28192             this.maskEl.unmask();
28193         }
28194         
28195         Roo.log('xhr on error');
28196         
28197         var response = Roo.decode(xhr.responseText);
28198           
28199         Roo.log(response);
28200         
28201     },
28202     
28203     prepare : function(file)
28204     {   
28205         if(this.loadMask){
28206             this.maskEl.mask(this.loadingText);
28207         }
28208         
28209         this.file = false;
28210         this.exif = {};
28211         
28212         if(typeof(file) === 'string'){
28213             this.loadCanvas(file);
28214             return;
28215         }
28216         
28217         if(!file || !this.urlAPI){
28218             return;
28219         }
28220         
28221         this.file = file;
28222         this.cropType = file.type;
28223         
28224         var _this = this;
28225         
28226         if(this.fireEvent('prepare', this, this.file) != false){
28227             
28228             var reader = new FileReader();
28229             
28230             reader.onload = function (e) {
28231                 if (e.target.error) {
28232                     Roo.log(e.target.error);
28233                     return;
28234                 }
28235                 
28236                 var buffer = e.target.result,
28237                     dataView = new DataView(buffer),
28238                     offset = 2,
28239                     maxOffset = dataView.byteLength - 4,
28240                     markerBytes,
28241                     markerLength;
28242                 
28243                 if (dataView.getUint16(0) === 0xffd8) {
28244                     while (offset < maxOffset) {
28245                         markerBytes = dataView.getUint16(offset);
28246                         
28247                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28248                             markerLength = dataView.getUint16(offset + 2) + 2;
28249                             if (offset + markerLength > dataView.byteLength) {
28250                                 Roo.log('Invalid meta data: Invalid segment size.');
28251                                 break;
28252                             }
28253                             
28254                             if(markerBytes == 0xffe1){
28255                                 _this.parseExifData(
28256                                     dataView,
28257                                     offset,
28258                                     markerLength
28259                                 );
28260                             }
28261                             
28262                             offset += markerLength;
28263                             
28264                             continue;
28265                         }
28266                         
28267                         break;
28268                     }
28269                     
28270                 }
28271                 
28272                 var url = _this.urlAPI.createObjectURL(_this.file);
28273                 
28274                 _this.loadCanvas(url);
28275                 
28276                 return;
28277             }
28278             
28279             reader.readAsArrayBuffer(this.file);
28280             
28281         }
28282         
28283     },
28284     
28285     parseExifData : function(dataView, offset, length)
28286     {
28287         var tiffOffset = offset + 10,
28288             littleEndian,
28289             dirOffset;
28290     
28291         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28292             // No Exif data, might be XMP data instead
28293             return;
28294         }
28295         
28296         // Check for the ASCII code for "Exif" (0x45786966):
28297         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28298             // No Exif data, might be XMP data instead
28299             return;
28300         }
28301         if (tiffOffset + 8 > dataView.byteLength) {
28302             Roo.log('Invalid Exif data: Invalid segment size.');
28303             return;
28304         }
28305         // Check for the two null bytes:
28306         if (dataView.getUint16(offset + 8) !== 0x0000) {
28307             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28308             return;
28309         }
28310         // Check the byte alignment:
28311         switch (dataView.getUint16(tiffOffset)) {
28312         case 0x4949:
28313             littleEndian = true;
28314             break;
28315         case 0x4D4D:
28316             littleEndian = false;
28317             break;
28318         default:
28319             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28320             return;
28321         }
28322         // Check for the TIFF tag marker (0x002A):
28323         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28324             Roo.log('Invalid Exif data: Missing TIFF marker.');
28325             return;
28326         }
28327         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28328         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28329         
28330         this.parseExifTags(
28331             dataView,
28332             tiffOffset,
28333             tiffOffset + dirOffset,
28334             littleEndian
28335         );
28336     },
28337     
28338     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28339     {
28340         var tagsNumber,
28341             dirEndOffset,
28342             i;
28343         if (dirOffset + 6 > dataView.byteLength) {
28344             Roo.log('Invalid Exif data: Invalid directory offset.');
28345             return;
28346         }
28347         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28348         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28349         if (dirEndOffset + 4 > dataView.byteLength) {
28350             Roo.log('Invalid Exif data: Invalid directory size.');
28351             return;
28352         }
28353         for (i = 0; i < tagsNumber; i += 1) {
28354             this.parseExifTag(
28355                 dataView,
28356                 tiffOffset,
28357                 dirOffset + 2 + 12 * i, // tag offset
28358                 littleEndian
28359             );
28360         }
28361         // Return the offset to the next directory:
28362         return dataView.getUint32(dirEndOffset, littleEndian);
28363     },
28364     
28365     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28366     {
28367         var tag = dataView.getUint16(offset, littleEndian);
28368         
28369         this.exif[tag] = this.getExifValue(
28370             dataView,
28371             tiffOffset,
28372             offset,
28373             dataView.getUint16(offset + 2, littleEndian), // tag type
28374             dataView.getUint32(offset + 4, littleEndian), // tag length
28375             littleEndian
28376         );
28377     },
28378     
28379     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28380     {
28381         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28382             tagSize,
28383             dataOffset,
28384             values,
28385             i,
28386             str,
28387             c;
28388     
28389         if (!tagType) {
28390             Roo.log('Invalid Exif data: Invalid tag type.');
28391             return;
28392         }
28393         
28394         tagSize = tagType.size * length;
28395         // Determine if the value is contained in the dataOffset bytes,
28396         // or if the value at the dataOffset is a pointer to the actual data:
28397         dataOffset = tagSize > 4 ?
28398                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28399         if (dataOffset + tagSize > dataView.byteLength) {
28400             Roo.log('Invalid Exif data: Invalid data offset.');
28401             return;
28402         }
28403         if (length === 1) {
28404             return tagType.getValue(dataView, dataOffset, littleEndian);
28405         }
28406         values = [];
28407         for (i = 0; i < length; i += 1) {
28408             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28409         }
28410         
28411         if (tagType.ascii) {
28412             str = '';
28413             // Concatenate the chars:
28414             for (i = 0; i < values.length; i += 1) {
28415                 c = values[i];
28416                 // Ignore the terminating NULL byte(s):
28417                 if (c === '\u0000') {
28418                     break;
28419                 }
28420                 str += c;
28421             }
28422             return str;
28423         }
28424         return values;
28425     }
28426     
28427 });
28428
28429 Roo.apply(Roo.bootstrap.UploadCropbox, {
28430     tags : {
28431         'Orientation': 0x0112
28432     },
28433     
28434     Orientation: {
28435             1: 0, //'top-left',
28436 //            2: 'top-right',
28437             3: 180, //'bottom-right',
28438 //            4: 'bottom-left',
28439 //            5: 'left-top',
28440             6: 90, //'right-top',
28441 //            7: 'right-bottom',
28442             8: 270 //'left-bottom'
28443     },
28444     
28445     exifTagTypes : {
28446         // byte, 8-bit unsigned int:
28447         1: {
28448             getValue: function (dataView, dataOffset) {
28449                 return dataView.getUint8(dataOffset);
28450             },
28451             size: 1
28452         },
28453         // ascii, 8-bit byte:
28454         2: {
28455             getValue: function (dataView, dataOffset) {
28456                 return String.fromCharCode(dataView.getUint8(dataOffset));
28457             },
28458             size: 1,
28459             ascii: true
28460         },
28461         // short, 16 bit int:
28462         3: {
28463             getValue: function (dataView, dataOffset, littleEndian) {
28464                 return dataView.getUint16(dataOffset, littleEndian);
28465             },
28466             size: 2
28467         },
28468         // long, 32 bit int:
28469         4: {
28470             getValue: function (dataView, dataOffset, littleEndian) {
28471                 return dataView.getUint32(dataOffset, littleEndian);
28472             },
28473             size: 4
28474         },
28475         // rational = two long values, first is numerator, second is denominator:
28476         5: {
28477             getValue: function (dataView, dataOffset, littleEndian) {
28478                 return dataView.getUint32(dataOffset, littleEndian) /
28479                     dataView.getUint32(dataOffset + 4, littleEndian);
28480             },
28481             size: 8
28482         },
28483         // slong, 32 bit signed int:
28484         9: {
28485             getValue: function (dataView, dataOffset, littleEndian) {
28486                 return dataView.getInt32(dataOffset, littleEndian);
28487             },
28488             size: 4
28489         },
28490         // srational, two slongs, first is numerator, second is denominator:
28491         10: {
28492             getValue: function (dataView, dataOffset, littleEndian) {
28493                 return dataView.getInt32(dataOffset, littleEndian) /
28494                     dataView.getInt32(dataOffset + 4, littleEndian);
28495             },
28496             size: 8
28497         }
28498     },
28499     
28500     footer : {
28501         STANDARD : [
28502             {
28503                 tag : 'div',
28504                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28505                 action : 'rotate-left',
28506                 cn : [
28507                     {
28508                         tag : 'button',
28509                         cls : 'btn btn-default',
28510                         html : '<i class="fa fa-undo"></i>'
28511                     }
28512                 ]
28513             },
28514             {
28515                 tag : 'div',
28516                 cls : 'btn-group roo-upload-cropbox-picture',
28517                 action : 'picture',
28518                 cn : [
28519                     {
28520                         tag : 'button',
28521                         cls : 'btn btn-default',
28522                         html : '<i class="fa fa-picture-o"></i>'
28523                     }
28524                 ]
28525             },
28526             {
28527                 tag : 'div',
28528                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28529                 action : 'rotate-right',
28530                 cn : [
28531                     {
28532                         tag : 'button',
28533                         cls : 'btn btn-default',
28534                         html : '<i class="fa fa-repeat"></i>'
28535                     }
28536                 ]
28537             }
28538         ],
28539         DOCUMENT : [
28540             {
28541                 tag : 'div',
28542                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28543                 action : 'rotate-left',
28544                 cn : [
28545                     {
28546                         tag : 'button',
28547                         cls : 'btn btn-default',
28548                         html : '<i class="fa fa-undo"></i>'
28549                     }
28550                 ]
28551             },
28552             {
28553                 tag : 'div',
28554                 cls : 'btn-group roo-upload-cropbox-download',
28555                 action : 'download',
28556                 cn : [
28557                     {
28558                         tag : 'button',
28559                         cls : 'btn btn-default',
28560                         html : '<i class="fa fa-download"></i>'
28561                     }
28562                 ]
28563             },
28564             {
28565                 tag : 'div',
28566                 cls : 'btn-group roo-upload-cropbox-crop',
28567                 action : 'crop',
28568                 cn : [
28569                     {
28570                         tag : 'button',
28571                         cls : 'btn btn-default',
28572                         html : '<i class="fa fa-crop"></i>'
28573                     }
28574                 ]
28575             },
28576             {
28577                 tag : 'div',
28578                 cls : 'btn-group roo-upload-cropbox-trash',
28579                 action : 'trash',
28580                 cn : [
28581                     {
28582                         tag : 'button',
28583                         cls : 'btn btn-default',
28584                         html : '<i class="fa fa-trash"></i>'
28585                     }
28586                 ]
28587             },
28588             {
28589                 tag : 'div',
28590                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28591                 action : 'rotate-right',
28592                 cn : [
28593                     {
28594                         tag : 'button',
28595                         cls : 'btn btn-default',
28596                         html : '<i class="fa fa-repeat"></i>'
28597                     }
28598                 ]
28599             }
28600         ],
28601         ROTATOR : [
28602             {
28603                 tag : 'div',
28604                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28605                 action : 'rotate-left',
28606                 cn : [
28607                     {
28608                         tag : 'button',
28609                         cls : 'btn btn-default',
28610                         html : '<i class="fa fa-undo"></i>'
28611                     }
28612                 ]
28613             },
28614             {
28615                 tag : 'div',
28616                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28617                 action : 'rotate-right',
28618                 cn : [
28619                     {
28620                         tag : 'button',
28621                         cls : 'btn btn-default',
28622                         html : '<i class="fa fa-repeat"></i>'
28623                     }
28624                 ]
28625             }
28626         ]
28627     }
28628 });
28629
28630 /*
28631 * Licence: LGPL
28632 */
28633
28634 /**
28635  * @class Roo.bootstrap.DocumentManager
28636  * @extends Roo.bootstrap.Component
28637  * Bootstrap DocumentManager class
28638  * @cfg {String} paramName default 'imageUpload'
28639  * @cfg {String} toolTipName default 'filename'
28640  * @cfg {String} method default POST
28641  * @cfg {String} url action url
28642  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28643  * @cfg {Boolean} multiple multiple upload default true
28644  * @cfg {Number} thumbSize default 300
28645  * @cfg {String} fieldLabel
28646  * @cfg {Number} labelWidth default 4
28647  * @cfg {String} labelAlign (left|top) default left
28648  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28649 * @cfg {Number} labellg set the width of label (1-12)
28650  * @cfg {Number} labelmd set the width of label (1-12)
28651  * @cfg {Number} labelsm set the width of label (1-12)
28652  * @cfg {Number} labelxs set the width of label (1-12)
28653  * 
28654  * @constructor
28655  * Create a new DocumentManager
28656  * @param {Object} config The config object
28657  */
28658
28659 Roo.bootstrap.DocumentManager = function(config){
28660     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28661     
28662     this.files = [];
28663     this.delegates = [];
28664     
28665     this.addEvents({
28666         /**
28667          * @event initial
28668          * Fire when initial the DocumentManager
28669          * @param {Roo.bootstrap.DocumentManager} this
28670          */
28671         "initial" : true,
28672         /**
28673          * @event inspect
28674          * inspect selected file
28675          * @param {Roo.bootstrap.DocumentManager} this
28676          * @param {File} file
28677          */
28678         "inspect" : true,
28679         /**
28680          * @event exception
28681          * Fire when xhr load exception
28682          * @param {Roo.bootstrap.DocumentManager} this
28683          * @param {XMLHttpRequest} xhr
28684          */
28685         "exception" : true,
28686         /**
28687          * @event afterupload
28688          * Fire when xhr load exception
28689          * @param {Roo.bootstrap.DocumentManager} this
28690          * @param {XMLHttpRequest} xhr
28691          */
28692         "afterupload" : true,
28693         /**
28694          * @event prepare
28695          * prepare the form data
28696          * @param {Roo.bootstrap.DocumentManager} this
28697          * @param {Object} formData
28698          */
28699         "prepare" : true,
28700         /**
28701          * @event remove
28702          * Fire when remove the file
28703          * @param {Roo.bootstrap.DocumentManager} this
28704          * @param {Object} file
28705          */
28706         "remove" : true,
28707         /**
28708          * @event refresh
28709          * Fire after refresh the file
28710          * @param {Roo.bootstrap.DocumentManager} this
28711          */
28712         "refresh" : true,
28713         /**
28714          * @event click
28715          * Fire after click the image
28716          * @param {Roo.bootstrap.DocumentManager} this
28717          * @param {Object} file
28718          */
28719         "click" : true,
28720         /**
28721          * @event edit
28722          * Fire when upload a image and editable set to true
28723          * @param {Roo.bootstrap.DocumentManager} this
28724          * @param {Object} file
28725          */
28726         "edit" : true,
28727         /**
28728          * @event beforeselectfile
28729          * Fire before select file
28730          * @param {Roo.bootstrap.DocumentManager} this
28731          */
28732         "beforeselectfile" : true,
28733         /**
28734          * @event process
28735          * Fire before process file
28736          * @param {Roo.bootstrap.DocumentManager} this
28737          * @param {Object} file
28738          */
28739         "process" : true,
28740         /**
28741          * @event previewrendered
28742          * Fire when preview rendered
28743          * @param {Roo.bootstrap.DocumentManager} this
28744          * @param {Object} file
28745          */
28746         "previewrendered" : true,
28747         /**
28748          */
28749         "previewResize" : true
28750         
28751     });
28752 };
28753
28754 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28755     
28756     boxes : 0,
28757     inputName : '',
28758     thumbSize : 300,
28759     multiple : true,
28760     files : false,
28761     method : 'POST',
28762     url : '',
28763     paramName : 'imageUpload',
28764     toolTipName : 'filename',
28765     fieldLabel : '',
28766     labelWidth : 4,
28767     labelAlign : 'left',
28768     editable : true,
28769     delegates : false,
28770     xhr : false, 
28771     
28772     labellg : 0,
28773     labelmd : 0,
28774     labelsm : 0,
28775     labelxs : 0,
28776     
28777     getAutoCreate : function()
28778     {   
28779         var managerWidget = {
28780             tag : 'div',
28781             cls : 'roo-document-manager',
28782             cn : [
28783                 {
28784                     tag : 'input',
28785                     cls : 'roo-document-manager-selector',
28786                     type : 'file'
28787                 },
28788                 {
28789                     tag : 'div',
28790                     cls : 'roo-document-manager-uploader',
28791                     cn : [
28792                         {
28793                             tag : 'div',
28794                             cls : 'roo-document-manager-upload-btn',
28795                             html : '<i class="fa fa-plus"></i>'
28796                         }
28797                     ]
28798                     
28799                 }
28800             ]
28801         };
28802         
28803         var content = [
28804             {
28805                 tag : 'div',
28806                 cls : 'column col-md-12',
28807                 cn : managerWidget
28808             }
28809         ];
28810         
28811         if(this.fieldLabel.length){
28812             
28813             content = [
28814                 {
28815                     tag : 'div',
28816                     cls : 'column col-md-12',
28817                     html : this.fieldLabel
28818                 },
28819                 {
28820                     tag : 'div',
28821                     cls : 'column col-md-12',
28822                     cn : managerWidget
28823                 }
28824             ];
28825
28826             if(this.labelAlign == 'left'){
28827                 content = [
28828                     {
28829                         tag : 'div',
28830                         cls : 'column',
28831                         html : this.fieldLabel
28832                     },
28833                     {
28834                         tag : 'div',
28835                         cls : 'column',
28836                         cn : managerWidget
28837                     }
28838                 ];
28839                 
28840                 if(this.labelWidth > 12){
28841                     content[0].style = "width: " + this.labelWidth + 'px';
28842                 }
28843
28844                 if(this.labelWidth < 13 && this.labelmd == 0){
28845                     this.labelmd = this.labelWidth;
28846                 }
28847
28848                 if(this.labellg > 0){
28849                     content[0].cls += ' col-lg-' + this.labellg;
28850                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28851                 }
28852
28853                 if(this.labelmd > 0){
28854                     content[0].cls += ' col-md-' + this.labelmd;
28855                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28856                 }
28857
28858                 if(this.labelsm > 0){
28859                     content[0].cls += ' col-sm-' + this.labelsm;
28860                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28861                 }
28862
28863                 if(this.labelxs > 0){
28864                     content[0].cls += ' col-xs-' + this.labelxs;
28865                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28866                 }
28867                 
28868             }
28869         }
28870         
28871         var cfg = {
28872             tag : 'div',
28873             cls : 'row clearfix',
28874             cn : content
28875         };
28876         
28877         return cfg;
28878         
28879     },
28880     
28881     initEvents : function()
28882     {
28883         this.managerEl = this.el.select('.roo-document-manager', true).first();
28884         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28885         
28886         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28887         this.selectorEl.hide();
28888         
28889         if(this.multiple){
28890             this.selectorEl.attr('multiple', 'multiple');
28891         }
28892         
28893         this.selectorEl.on('change', this.onFileSelected, this);
28894         
28895         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28896         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28897         
28898         this.uploader.on('click', this.onUploaderClick, this);
28899         
28900         this.renderProgressDialog();
28901         
28902         var _this = this;
28903         
28904         window.addEventListener("resize", function() { _this.refresh(); } );
28905         
28906         this.fireEvent('initial', this);
28907     },
28908     
28909     renderProgressDialog : function()
28910     {
28911         var _this = this;
28912         
28913         this.progressDialog = new Roo.bootstrap.Modal({
28914             cls : 'roo-document-manager-progress-dialog',
28915             allow_close : false,
28916             title : '',
28917             buttons : [
28918                 {
28919                     name  :'cancel',
28920                     weight : 'danger',
28921                     html : 'Cancel'
28922                 }
28923             ], 
28924             listeners : { 
28925                 btnclick : function() {
28926                     _this.uploadCancel();
28927                     this.hide();
28928                 }
28929             }
28930         });
28931          
28932         this.progressDialog.render(Roo.get(document.body));
28933          
28934         this.progress = new Roo.bootstrap.Progress({
28935             cls : 'roo-document-manager-progress',
28936             active : true,
28937             striped : true
28938         });
28939         
28940         this.progress.render(this.progressDialog.getChildContainer());
28941         
28942         this.progressBar = new Roo.bootstrap.ProgressBar({
28943             cls : 'roo-document-manager-progress-bar',
28944             aria_valuenow : 0,
28945             aria_valuemin : 0,
28946             aria_valuemax : 12,
28947             panel : 'success'
28948         });
28949         
28950         this.progressBar.render(this.progress.getChildContainer());
28951     },
28952     
28953     onUploaderClick : function(e)
28954     {
28955         e.preventDefault();
28956      
28957         if(this.fireEvent('beforeselectfile', this) != false){
28958             this.selectorEl.dom.click();
28959         }
28960         
28961     },
28962     
28963     onFileSelected : function(e)
28964     {
28965         e.preventDefault();
28966         
28967         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28968             return;
28969         }
28970         
28971         Roo.each(this.selectorEl.dom.files, function(file){
28972             if(this.fireEvent('inspect', this, file) != false){
28973                 this.files.push(file);
28974             }
28975         }, this);
28976         
28977         this.queue();
28978         
28979     },
28980     
28981     queue : function()
28982     {
28983         this.selectorEl.dom.value = '';
28984         
28985         if(!this.files || !this.files.length){
28986             return;
28987         }
28988         
28989         if(this.boxes > 0 && this.files.length > this.boxes){
28990             this.files = this.files.slice(0, this.boxes);
28991         }
28992         
28993         this.uploader.show();
28994         
28995         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28996             this.uploader.hide();
28997         }
28998         
28999         var _this = this;
29000         
29001         var files = [];
29002         
29003         var docs = [];
29004         
29005         Roo.each(this.files, function(file){
29006             
29007             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29008                 var f = this.renderPreview(file);
29009                 files.push(f);
29010                 return;
29011             }
29012             
29013             if(file.type.indexOf('image') != -1){
29014                 this.delegates.push(
29015                     (function(){
29016                         _this.process(file);
29017                     }).createDelegate(this)
29018                 );
29019         
29020                 return;
29021             }
29022             
29023             docs.push(
29024                 (function(){
29025                     _this.process(file);
29026                 }).createDelegate(this)
29027             );
29028             
29029         }, this);
29030         
29031         this.files = files;
29032         
29033         this.delegates = this.delegates.concat(docs);
29034         
29035         if(!this.delegates.length){
29036             this.refresh();
29037             return;
29038         }
29039         
29040         this.progressBar.aria_valuemax = this.delegates.length;
29041         
29042         this.arrange();
29043         
29044         return;
29045     },
29046     
29047     arrange : function()
29048     {
29049         if(!this.delegates.length){
29050             this.progressDialog.hide();
29051             this.refresh();
29052             return;
29053         }
29054         
29055         var delegate = this.delegates.shift();
29056         
29057         this.progressDialog.show();
29058         
29059         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29060         
29061         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29062         
29063         delegate();
29064     },
29065     
29066     refresh : function()
29067     {
29068         this.uploader.show();
29069         
29070         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29071             this.uploader.hide();
29072         }
29073         
29074         Roo.isTouch ? this.closable(false) : this.closable(true);
29075         
29076         this.fireEvent('refresh', this);
29077     },
29078     
29079     onRemove : function(e, el, o)
29080     {
29081         e.preventDefault();
29082         
29083         this.fireEvent('remove', this, o);
29084         
29085     },
29086     
29087     remove : function(o)
29088     {
29089         var files = [];
29090         
29091         Roo.each(this.files, function(file){
29092             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29093                 files.push(file);
29094                 return;
29095             }
29096
29097             o.target.remove();
29098
29099         }, this);
29100         
29101         this.files = files;
29102         
29103         this.refresh();
29104     },
29105     
29106     clear : function()
29107     {
29108         Roo.each(this.files, function(file){
29109             if(!file.target){
29110                 return;
29111             }
29112             
29113             file.target.remove();
29114
29115         }, this);
29116         
29117         this.files = [];
29118         
29119         this.refresh();
29120     },
29121     
29122     onClick : function(e, el, o)
29123     {
29124         e.preventDefault();
29125         
29126         this.fireEvent('click', this, o);
29127         
29128     },
29129     
29130     closable : function(closable)
29131     {
29132         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29133             
29134             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29135             
29136             if(closable){
29137                 el.show();
29138                 return;
29139             }
29140             
29141             el.hide();
29142             
29143         }, this);
29144     },
29145     
29146     xhrOnLoad : function(xhr)
29147     {
29148         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29149             el.remove();
29150         }, this);
29151         
29152         if (xhr.readyState !== 4) {
29153             this.arrange();
29154             this.fireEvent('exception', this, xhr);
29155             return;
29156         }
29157
29158         var response = Roo.decode(xhr.responseText);
29159         
29160         if(!response.success){
29161             this.arrange();
29162             this.fireEvent('exception', this, xhr);
29163             return;
29164         }
29165         
29166         var file = this.renderPreview(response.data);
29167         
29168         this.files.push(file);
29169         
29170         this.arrange();
29171         
29172         this.fireEvent('afterupload', this, xhr);
29173         
29174     },
29175     
29176     xhrOnError : function(xhr)
29177     {
29178         Roo.log('xhr on error');
29179         
29180         var response = Roo.decode(xhr.responseText);
29181           
29182         Roo.log(response);
29183         
29184         this.arrange();
29185     },
29186     
29187     process : function(file)
29188     {
29189         if(this.fireEvent('process', this, file) !== false){
29190             if(this.editable && file.type.indexOf('image') != -1){
29191                 this.fireEvent('edit', this, file);
29192                 return;
29193             }
29194
29195             this.uploadStart(file, false);
29196
29197             return;
29198         }
29199         
29200     },
29201     
29202     uploadStart : function(file, crop)
29203     {
29204         this.xhr = new XMLHttpRequest();
29205         
29206         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29207             this.arrange();
29208             return;
29209         }
29210         
29211         file.xhr = this.xhr;
29212             
29213         this.managerEl.createChild({
29214             tag : 'div',
29215             cls : 'roo-document-manager-loading',
29216             cn : [
29217                 {
29218                     tag : 'div',
29219                     tooltip : file.name,
29220                     cls : 'roo-document-manager-thumb',
29221                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29222                 }
29223             ]
29224
29225         });
29226
29227         this.xhr.open(this.method, this.url, true);
29228         
29229         var headers = {
29230             "Accept": "application/json",
29231             "Cache-Control": "no-cache",
29232             "X-Requested-With": "XMLHttpRequest"
29233         };
29234         
29235         for (var headerName in headers) {
29236             var headerValue = headers[headerName];
29237             if (headerValue) {
29238                 this.xhr.setRequestHeader(headerName, headerValue);
29239             }
29240         }
29241         
29242         var _this = this;
29243         
29244         this.xhr.onload = function()
29245         {
29246             _this.xhrOnLoad(_this.xhr);
29247         }
29248         
29249         this.xhr.onerror = function()
29250         {
29251             _this.xhrOnError(_this.xhr);
29252         }
29253         
29254         var formData = new FormData();
29255
29256         formData.append('returnHTML', 'NO');
29257         
29258         if(crop){
29259             formData.append('crop', crop);
29260         }
29261         
29262         formData.append(this.paramName, file, file.name);
29263         
29264         var options = {
29265             file : file, 
29266             manually : false
29267         };
29268         
29269         if(this.fireEvent('prepare', this, formData, options) != false){
29270             
29271             if(options.manually){
29272                 return;
29273             }
29274             
29275             this.xhr.send(formData);
29276             return;
29277         };
29278         
29279         this.uploadCancel();
29280     },
29281     
29282     uploadCancel : function()
29283     {
29284         if (this.xhr) {
29285             this.xhr.abort();
29286         }
29287         
29288         this.delegates = [];
29289         
29290         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29291             el.remove();
29292         }, this);
29293         
29294         this.arrange();
29295     },
29296     
29297     renderPreview : function(file)
29298     {
29299         if(typeof(file.target) != 'undefined' && file.target){
29300             return file;
29301         }
29302         
29303         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29304         
29305         var previewEl = this.managerEl.createChild({
29306             tag : 'div',
29307             cls : 'roo-document-manager-preview',
29308             cn : [
29309                 {
29310                     tag : 'div',
29311                     tooltip : file[this.toolTipName],
29312                     cls : 'roo-document-manager-thumb',
29313                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29314                 },
29315                 {
29316                     tag : 'button',
29317                     cls : 'close',
29318                     html : '<i class="fa fa-times-circle"></i>'
29319                 }
29320             ]
29321         });
29322
29323         var close = previewEl.select('button.close', true).first();
29324
29325         close.on('click', this.onRemove, this, file);
29326
29327         file.target = previewEl;
29328
29329         var image = previewEl.select('img', true).first();
29330         
29331         var _this = this;
29332         
29333         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29334         
29335         image.on('click', this.onClick, this, file);
29336         
29337         this.fireEvent('previewrendered', this, file);
29338         
29339         return file;
29340         
29341     },
29342     
29343     onPreviewLoad : function(file, image)
29344     {
29345         if(typeof(file.target) == 'undefined' || !file.target){
29346             return;
29347         }
29348         
29349         var width = image.dom.naturalWidth || image.dom.width;
29350         var height = image.dom.naturalHeight || image.dom.height;
29351         
29352         if(!this.previewResize) {
29353             return;
29354         }
29355         
29356         if(width > height){
29357             file.target.addClass('wide');
29358             return;
29359         }
29360         
29361         file.target.addClass('tall');
29362         return;
29363         
29364     },
29365     
29366     uploadFromSource : function(file, crop)
29367     {
29368         this.xhr = new XMLHttpRequest();
29369         
29370         this.managerEl.createChild({
29371             tag : 'div',
29372             cls : 'roo-document-manager-loading',
29373             cn : [
29374                 {
29375                     tag : 'div',
29376                     tooltip : file.name,
29377                     cls : 'roo-document-manager-thumb',
29378                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29379                 }
29380             ]
29381
29382         });
29383
29384         this.xhr.open(this.method, this.url, true);
29385         
29386         var headers = {
29387             "Accept": "application/json",
29388             "Cache-Control": "no-cache",
29389             "X-Requested-With": "XMLHttpRequest"
29390         };
29391         
29392         for (var headerName in headers) {
29393             var headerValue = headers[headerName];
29394             if (headerValue) {
29395                 this.xhr.setRequestHeader(headerName, headerValue);
29396             }
29397         }
29398         
29399         var _this = this;
29400         
29401         this.xhr.onload = function()
29402         {
29403             _this.xhrOnLoad(_this.xhr);
29404         }
29405         
29406         this.xhr.onerror = function()
29407         {
29408             _this.xhrOnError(_this.xhr);
29409         }
29410         
29411         var formData = new FormData();
29412
29413         formData.append('returnHTML', 'NO');
29414         
29415         formData.append('crop', crop);
29416         
29417         if(typeof(file.filename) != 'undefined'){
29418             formData.append('filename', file.filename);
29419         }
29420         
29421         if(typeof(file.mimetype) != 'undefined'){
29422             formData.append('mimetype', file.mimetype);
29423         }
29424         
29425         Roo.log(formData);
29426         
29427         if(this.fireEvent('prepare', this, formData) != false){
29428             this.xhr.send(formData);
29429         };
29430     }
29431 });
29432
29433 /*
29434 * Licence: LGPL
29435 */
29436
29437 /**
29438  * @class Roo.bootstrap.DocumentViewer
29439  * @extends Roo.bootstrap.Component
29440  * Bootstrap DocumentViewer class
29441  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29442  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29443  * 
29444  * @constructor
29445  * Create a new DocumentViewer
29446  * @param {Object} config The config object
29447  */
29448
29449 Roo.bootstrap.DocumentViewer = function(config){
29450     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29451     
29452     this.addEvents({
29453         /**
29454          * @event initial
29455          * Fire after initEvent
29456          * @param {Roo.bootstrap.DocumentViewer} this
29457          */
29458         "initial" : true,
29459         /**
29460          * @event click
29461          * Fire after click
29462          * @param {Roo.bootstrap.DocumentViewer} this
29463          */
29464         "click" : true,
29465         /**
29466          * @event download
29467          * Fire after download button
29468          * @param {Roo.bootstrap.DocumentViewer} this
29469          */
29470         "download" : true,
29471         /**
29472          * @event trash
29473          * Fire after trash button
29474          * @param {Roo.bootstrap.DocumentViewer} this
29475          */
29476         "trash" : true
29477         
29478     });
29479 };
29480
29481 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29482     
29483     showDownload : true,
29484     
29485     showTrash : true,
29486     
29487     getAutoCreate : function()
29488     {
29489         var cfg = {
29490             tag : 'div',
29491             cls : 'roo-document-viewer',
29492             cn : [
29493                 {
29494                     tag : 'div',
29495                     cls : 'roo-document-viewer-body',
29496                     cn : [
29497                         {
29498                             tag : 'div',
29499                             cls : 'roo-document-viewer-thumb',
29500                             cn : [
29501                                 {
29502                                     tag : 'img',
29503                                     cls : 'roo-document-viewer-image'
29504                                 }
29505                             ]
29506                         }
29507                     ]
29508                 },
29509                 {
29510                     tag : 'div',
29511                     cls : 'roo-document-viewer-footer',
29512                     cn : {
29513                         tag : 'div',
29514                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29515                         cn : [
29516                             {
29517                                 tag : 'div',
29518                                 cls : 'btn-group roo-document-viewer-download',
29519                                 cn : [
29520                                     {
29521                                         tag : 'button',
29522                                         cls : 'btn btn-default',
29523                                         html : '<i class="fa fa-download"></i>'
29524                                     }
29525                                 ]
29526                             },
29527                             {
29528                                 tag : 'div',
29529                                 cls : 'btn-group roo-document-viewer-trash',
29530                                 cn : [
29531                                     {
29532                                         tag : 'button',
29533                                         cls : 'btn btn-default',
29534                                         html : '<i class="fa fa-trash"></i>'
29535                                     }
29536                                 ]
29537                             }
29538                         ]
29539                     }
29540                 }
29541             ]
29542         };
29543         
29544         return cfg;
29545     },
29546     
29547     initEvents : function()
29548     {
29549         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29550         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29551         
29552         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29553         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29554         
29555         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29556         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29557         
29558         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29559         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29560         
29561         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29562         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29563         
29564         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29565         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29566         
29567         this.bodyEl.on('click', this.onClick, this);
29568         this.downloadBtn.on('click', this.onDownload, this);
29569         this.trashBtn.on('click', this.onTrash, this);
29570         
29571         this.downloadBtn.hide();
29572         this.trashBtn.hide();
29573         
29574         if(this.showDownload){
29575             this.downloadBtn.show();
29576         }
29577         
29578         if(this.showTrash){
29579             this.trashBtn.show();
29580         }
29581         
29582         if(!this.showDownload && !this.showTrash) {
29583             this.footerEl.hide();
29584         }
29585         
29586     },
29587     
29588     initial : function()
29589     {
29590         this.fireEvent('initial', this);
29591         
29592     },
29593     
29594     onClick : function(e)
29595     {
29596         e.preventDefault();
29597         
29598         this.fireEvent('click', this);
29599     },
29600     
29601     onDownload : function(e)
29602     {
29603         e.preventDefault();
29604         
29605         this.fireEvent('download', this);
29606     },
29607     
29608     onTrash : function(e)
29609     {
29610         e.preventDefault();
29611         
29612         this.fireEvent('trash', this);
29613     }
29614     
29615 });
29616 /*
29617  * - LGPL
29618  *
29619  * nav progress bar
29620  * 
29621  */
29622
29623 /**
29624  * @class Roo.bootstrap.NavProgressBar
29625  * @extends Roo.bootstrap.Component
29626  * Bootstrap NavProgressBar class
29627  * 
29628  * @constructor
29629  * Create a new nav progress bar
29630  * @param {Object} config The config object
29631  */
29632
29633 Roo.bootstrap.NavProgressBar = function(config){
29634     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29635
29636     this.bullets = this.bullets || [];
29637    
29638 //    Roo.bootstrap.NavProgressBar.register(this);
29639      this.addEvents({
29640         /**
29641              * @event changed
29642              * Fires when the active item changes
29643              * @param {Roo.bootstrap.NavProgressBar} this
29644              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29645              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29646          */
29647         'changed': true
29648      });
29649     
29650 };
29651
29652 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29653     
29654     bullets : [],
29655     barItems : [],
29656     
29657     getAutoCreate : function()
29658     {
29659         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29660         
29661         cfg = {
29662             tag : 'div',
29663             cls : 'roo-navigation-bar-group',
29664             cn : [
29665                 {
29666                     tag : 'div',
29667                     cls : 'roo-navigation-top-bar'
29668                 },
29669                 {
29670                     tag : 'div',
29671                     cls : 'roo-navigation-bullets-bar',
29672                     cn : [
29673                         {
29674                             tag : 'ul',
29675                             cls : 'roo-navigation-bar'
29676                         }
29677                     ]
29678                 },
29679                 
29680                 {
29681                     tag : 'div',
29682                     cls : 'roo-navigation-bottom-bar'
29683                 }
29684             ]
29685             
29686         };
29687         
29688         return cfg;
29689         
29690     },
29691     
29692     initEvents: function() 
29693     {
29694         
29695     },
29696     
29697     onRender : function(ct, position) 
29698     {
29699         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29700         
29701         if(this.bullets.length){
29702             Roo.each(this.bullets, function(b){
29703                this.addItem(b);
29704             }, this);
29705         }
29706         
29707         this.format();
29708         
29709     },
29710     
29711     addItem : function(cfg)
29712     {
29713         var item = new Roo.bootstrap.NavProgressItem(cfg);
29714         
29715         item.parentId = this.id;
29716         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29717         
29718         if(cfg.html){
29719             var top = new Roo.bootstrap.Element({
29720                 tag : 'div',
29721                 cls : 'roo-navigation-bar-text'
29722             });
29723             
29724             var bottom = new Roo.bootstrap.Element({
29725                 tag : 'div',
29726                 cls : 'roo-navigation-bar-text'
29727             });
29728             
29729             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29730             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29731             
29732             var topText = new Roo.bootstrap.Element({
29733                 tag : 'span',
29734                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29735             });
29736             
29737             var bottomText = new Roo.bootstrap.Element({
29738                 tag : 'span',
29739                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29740             });
29741             
29742             topText.onRender(top.el, null);
29743             bottomText.onRender(bottom.el, null);
29744             
29745             item.topEl = top;
29746             item.bottomEl = bottom;
29747         }
29748         
29749         this.barItems.push(item);
29750         
29751         return item;
29752     },
29753     
29754     getActive : function()
29755     {
29756         var active = false;
29757         
29758         Roo.each(this.barItems, function(v){
29759             
29760             if (!v.isActive()) {
29761                 return;
29762             }
29763             
29764             active = v;
29765             return false;
29766             
29767         });
29768         
29769         return active;
29770     },
29771     
29772     setActiveItem : function(item)
29773     {
29774         var prev = false;
29775         
29776         Roo.each(this.barItems, function(v){
29777             if (v.rid == item.rid) {
29778                 return ;
29779             }
29780             
29781             if (v.isActive()) {
29782                 v.setActive(false);
29783                 prev = v;
29784             }
29785         });
29786
29787         item.setActive(true);
29788         
29789         this.fireEvent('changed', this, item, prev);
29790     },
29791     
29792     getBarItem: function(rid)
29793     {
29794         var ret = false;
29795         
29796         Roo.each(this.barItems, function(e) {
29797             if (e.rid != rid) {
29798                 return;
29799             }
29800             
29801             ret =  e;
29802             return false;
29803         });
29804         
29805         return ret;
29806     },
29807     
29808     indexOfItem : function(item)
29809     {
29810         var index = false;
29811         
29812         Roo.each(this.barItems, function(v, i){
29813             
29814             if (v.rid != item.rid) {
29815                 return;
29816             }
29817             
29818             index = i;
29819             return false
29820         });
29821         
29822         return index;
29823     },
29824     
29825     setActiveNext : function()
29826     {
29827         var i = this.indexOfItem(this.getActive());
29828         
29829         if (i > this.barItems.length) {
29830             return;
29831         }
29832         
29833         this.setActiveItem(this.barItems[i+1]);
29834     },
29835     
29836     setActivePrev : function()
29837     {
29838         var i = this.indexOfItem(this.getActive());
29839         
29840         if (i  < 1) {
29841             return;
29842         }
29843         
29844         this.setActiveItem(this.barItems[i-1]);
29845     },
29846     
29847     format : function()
29848     {
29849         if(!this.barItems.length){
29850             return;
29851         }
29852      
29853         var width = 100 / this.barItems.length;
29854         
29855         Roo.each(this.barItems, function(i){
29856             i.el.setStyle('width', width + '%');
29857             i.topEl.el.setStyle('width', width + '%');
29858             i.bottomEl.el.setStyle('width', width + '%');
29859         }, this);
29860         
29861     }
29862     
29863 });
29864 /*
29865  * - LGPL
29866  *
29867  * Nav Progress Item
29868  * 
29869  */
29870
29871 /**
29872  * @class Roo.bootstrap.NavProgressItem
29873  * @extends Roo.bootstrap.Component
29874  * Bootstrap NavProgressItem class
29875  * @cfg {String} rid the reference id
29876  * @cfg {Boolean} active (true|false) Is item active default false
29877  * @cfg {Boolean} disabled (true|false) Is item active default false
29878  * @cfg {String} html
29879  * @cfg {String} position (top|bottom) text position default bottom
29880  * @cfg {String} icon show icon instead of number
29881  * 
29882  * @constructor
29883  * Create a new NavProgressItem
29884  * @param {Object} config The config object
29885  */
29886 Roo.bootstrap.NavProgressItem = function(config){
29887     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29888     this.addEvents({
29889         // raw events
29890         /**
29891          * @event click
29892          * The raw click event for the entire grid.
29893          * @param {Roo.bootstrap.NavProgressItem} this
29894          * @param {Roo.EventObject} e
29895          */
29896         "click" : true
29897     });
29898    
29899 };
29900
29901 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29902     
29903     rid : '',
29904     active : false,
29905     disabled : false,
29906     html : '',
29907     position : 'bottom',
29908     icon : false,
29909     
29910     getAutoCreate : function()
29911     {
29912         var iconCls = 'roo-navigation-bar-item-icon';
29913         
29914         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29915         
29916         var cfg = {
29917             tag: 'li',
29918             cls: 'roo-navigation-bar-item',
29919             cn : [
29920                 {
29921                     tag : 'i',
29922                     cls : iconCls
29923                 }
29924             ]
29925         };
29926         
29927         if(this.active){
29928             cfg.cls += ' active';
29929         }
29930         if(this.disabled){
29931             cfg.cls += ' disabled';
29932         }
29933         
29934         return cfg;
29935     },
29936     
29937     disable : function()
29938     {
29939         this.setDisabled(true);
29940     },
29941     
29942     enable : function()
29943     {
29944         this.setDisabled(false);
29945     },
29946     
29947     initEvents: function() 
29948     {
29949         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29950         
29951         this.iconEl.on('click', this.onClick, this);
29952     },
29953     
29954     onClick : function(e)
29955     {
29956         e.preventDefault();
29957         
29958         if(this.disabled){
29959             return;
29960         }
29961         
29962         if(this.fireEvent('click', this, e) === false){
29963             return;
29964         };
29965         
29966         this.parent().setActiveItem(this);
29967     },
29968     
29969     isActive: function () 
29970     {
29971         return this.active;
29972     },
29973     
29974     setActive : function(state)
29975     {
29976         if(this.active == state){
29977             return;
29978         }
29979         
29980         this.active = state;
29981         
29982         if (state) {
29983             this.el.addClass('active');
29984             return;
29985         }
29986         
29987         this.el.removeClass('active');
29988         
29989         return;
29990     },
29991     
29992     setDisabled : function(state)
29993     {
29994         if(this.disabled == state){
29995             return;
29996         }
29997         
29998         this.disabled = state;
29999         
30000         if (state) {
30001             this.el.addClass('disabled');
30002             return;
30003         }
30004         
30005         this.el.removeClass('disabled');
30006     },
30007     
30008     tooltipEl : function()
30009     {
30010         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30011     }
30012 });
30013  
30014
30015  /*
30016  * - LGPL
30017  *
30018  * FieldLabel
30019  * 
30020  */
30021
30022 /**
30023  * @class Roo.bootstrap.FieldLabel
30024  * @extends Roo.bootstrap.Component
30025  * Bootstrap FieldLabel class
30026  * @cfg {String} html contents of the element
30027  * @cfg {String} tag tag of the element default label
30028  * @cfg {String} cls class of the element
30029  * @cfg {String} target label target 
30030  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30031  * @cfg {String} invalidClass default "text-warning"
30032  * @cfg {String} validClass default "text-success"
30033  * @cfg {String} iconTooltip default "This field is required"
30034  * @cfg {String} indicatorpos (left|right) default left
30035  * 
30036  * @constructor
30037  * Create a new FieldLabel
30038  * @param {Object} config The config object
30039  */
30040
30041 Roo.bootstrap.FieldLabel = function(config){
30042     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30043     
30044     this.addEvents({
30045             /**
30046              * @event invalid
30047              * Fires after the field has been marked as invalid.
30048              * @param {Roo.form.FieldLabel} this
30049              * @param {String} msg The validation message
30050              */
30051             invalid : true,
30052             /**
30053              * @event valid
30054              * Fires after the field has been validated with no errors.
30055              * @param {Roo.form.FieldLabel} this
30056              */
30057             valid : true
30058         });
30059 };
30060
30061 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30062     
30063     tag: 'label',
30064     cls: '',
30065     html: '',
30066     target: '',
30067     allowBlank : true,
30068     invalidClass : 'has-warning',
30069     validClass : 'has-success',
30070     iconTooltip : 'This field is required',
30071     indicatorpos : 'left',
30072     
30073     getAutoCreate : function(){
30074         
30075         var cls = "";
30076         if (!this.allowBlank) {
30077             cls  = "visible";
30078         }
30079         
30080         var cfg = {
30081             tag : this.tag,
30082             cls : 'roo-bootstrap-field-label ' + this.cls,
30083             for : this.target,
30084             cn : [
30085                 {
30086                     tag : 'i',
30087                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30088                     tooltip : this.iconTooltip
30089                 },
30090                 {
30091                     tag : 'span',
30092                     html : this.html
30093                 }
30094             ] 
30095         };
30096         
30097         if(this.indicatorpos == 'right'){
30098             var cfg = {
30099                 tag : this.tag,
30100                 cls : 'roo-bootstrap-field-label ' + this.cls,
30101                 for : this.target,
30102                 cn : [
30103                     {
30104                         tag : 'span',
30105                         html : this.html
30106                     },
30107                     {
30108                         tag : 'i',
30109                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30110                         tooltip : this.iconTooltip
30111                     }
30112                 ] 
30113             };
30114         }
30115         
30116         return cfg;
30117     },
30118     
30119     initEvents: function() 
30120     {
30121         Roo.bootstrap.Element.superclass.initEvents.call(this);
30122         
30123         this.indicator = this.indicatorEl();
30124         
30125         if(this.indicator){
30126             this.indicator.removeClass('visible');
30127             this.indicator.addClass('invisible');
30128         }
30129         
30130         Roo.bootstrap.FieldLabel.register(this);
30131     },
30132     
30133     indicatorEl : function()
30134     {
30135         var indicator = this.el.select('i.roo-required-indicator',true).first();
30136         
30137         if(!indicator){
30138             return false;
30139         }
30140         
30141         return indicator;
30142         
30143     },
30144     
30145     /**
30146      * Mark this field as valid
30147      */
30148     markValid : function()
30149     {
30150         if(this.indicator){
30151             this.indicator.removeClass('visible');
30152             this.indicator.addClass('invisible');
30153         }
30154         
30155         this.el.removeClass(this.invalidClass);
30156         
30157         this.el.addClass(this.validClass);
30158         
30159         this.fireEvent('valid', this);
30160     },
30161     
30162     /**
30163      * Mark this field as invalid
30164      * @param {String} msg The validation message
30165      */
30166     markInvalid : function(msg)
30167     {
30168         if(this.indicator){
30169             this.indicator.removeClass('invisible');
30170             this.indicator.addClass('visible');
30171         }
30172         
30173         this.el.removeClass(this.validClass);
30174         
30175         this.el.addClass(this.invalidClass);
30176         
30177         this.fireEvent('invalid', this, msg);
30178     }
30179     
30180    
30181 });
30182
30183 Roo.apply(Roo.bootstrap.FieldLabel, {
30184     
30185     groups: {},
30186     
30187      /**
30188     * register a FieldLabel Group
30189     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30190     */
30191     register : function(label)
30192     {
30193         if(this.groups.hasOwnProperty(label.target)){
30194             return;
30195         }
30196      
30197         this.groups[label.target] = label;
30198         
30199     },
30200     /**
30201     * fetch a FieldLabel Group based on the target
30202     * @param {string} target
30203     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30204     */
30205     get: function(target) {
30206         if (typeof(this.groups[target]) == 'undefined') {
30207             return false;
30208         }
30209         
30210         return this.groups[target] ;
30211     }
30212 });
30213
30214  
30215
30216  /*
30217  * - LGPL
30218  *
30219  * page DateSplitField.
30220  * 
30221  */
30222
30223
30224 /**
30225  * @class Roo.bootstrap.DateSplitField
30226  * @extends Roo.bootstrap.Component
30227  * Bootstrap DateSplitField class
30228  * @cfg {string} fieldLabel - the label associated
30229  * @cfg {Number} labelWidth set the width of label (0-12)
30230  * @cfg {String} labelAlign (top|left)
30231  * @cfg {Boolean} dayAllowBlank (true|false) default false
30232  * @cfg {Boolean} monthAllowBlank (true|false) default false
30233  * @cfg {Boolean} yearAllowBlank (true|false) default false
30234  * @cfg {string} dayPlaceholder 
30235  * @cfg {string} monthPlaceholder
30236  * @cfg {string} yearPlaceholder
30237  * @cfg {string} dayFormat default 'd'
30238  * @cfg {string} monthFormat default 'm'
30239  * @cfg {string} yearFormat default 'Y'
30240  * @cfg {Number} labellg set the width of label (1-12)
30241  * @cfg {Number} labelmd set the width of label (1-12)
30242  * @cfg {Number} labelsm set the width of label (1-12)
30243  * @cfg {Number} labelxs set the width of label (1-12)
30244
30245  *     
30246  * @constructor
30247  * Create a new DateSplitField
30248  * @param {Object} config The config object
30249  */
30250
30251 Roo.bootstrap.DateSplitField = function(config){
30252     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30253     
30254     this.addEvents({
30255         // raw events
30256          /**
30257          * @event years
30258          * getting the data of years
30259          * @param {Roo.bootstrap.DateSplitField} this
30260          * @param {Object} years
30261          */
30262         "years" : true,
30263         /**
30264          * @event days
30265          * getting the data of days
30266          * @param {Roo.bootstrap.DateSplitField} this
30267          * @param {Object} days
30268          */
30269         "days" : true,
30270         /**
30271          * @event invalid
30272          * Fires after the field has been marked as invalid.
30273          * @param {Roo.form.Field} this
30274          * @param {String} msg The validation message
30275          */
30276         invalid : true,
30277        /**
30278          * @event valid
30279          * Fires after the field has been validated with no errors.
30280          * @param {Roo.form.Field} this
30281          */
30282         valid : true
30283     });
30284 };
30285
30286 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30287     
30288     fieldLabel : '',
30289     labelAlign : 'top',
30290     labelWidth : 3,
30291     dayAllowBlank : false,
30292     monthAllowBlank : false,
30293     yearAllowBlank : false,
30294     dayPlaceholder : '',
30295     monthPlaceholder : '',
30296     yearPlaceholder : '',
30297     dayFormat : 'd',
30298     monthFormat : 'm',
30299     yearFormat : 'Y',
30300     isFormField : true,
30301     labellg : 0,
30302     labelmd : 0,
30303     labelsm : 0,
30304     labelxs : 0,
30305     
30306     getAutoCreate : function()
30307     {
30308         var cfg = {
30309             tag : 'div',
30310             cls : 'row roo-date-split-field-group',
30311             cn : [
30312                 {
30313                     tag : 'input',
30314                     type : 'hidden',
30315                     cls : 'form-hidden-field roo-date-split-field-group-value',
30316                     name : this.name
30317                 }
30318             ]
30319         };
30320         
30321         var labelCls = 'col-md-12';
30322         var contentCls = 'col-md-4';
30323         
30324         if(this.fieldLabel){
30325             
30326             var label = {
30327                 tag : 'div',
30328                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30329                 cn : [
30330                     {
30331                         tag : 'label',
30332                         html : this.fieldLabel
30333                     }
30334                 ]
30335             };
30336             
30337             if(this.labelAlign == 'left'){
30338             
30339                 if(this.labelWidth > 12){
30340                     label.style = "width: " + this.labelWidth + 'px';
30341                 }
30342
30343                 if(this.labelWidth < 13 && this.labelmd == 0){
30344                     this.labelmd = this.labelWidth;
30345                 }
30346
30347                 if(this.labellg > 0){
30348                     labelCls = ' col-lg-' + this.labellg;
30349                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30350                 }
30351
30352                 if(this.labelmd > 0){
30353                     labelCls = ' col-md-' + this.labelmd;
30354                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30355                 }
30356
30357                 if(this.labelsm > 0){
30358                     labelCls = ' col-sm-' + this.labelsm;
30359                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30360                 }
30361
30362                 if(this.labelxs > 0){
30363                     labelCls = ' col-xs-' + this.labelxs;
30364                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30365                 }
30366             }
30367             
30368             label.cls += ' ' + labelCls;
30369             
30370             cfg.cn.push(label);
30371         }
30372         
30373         Roo.each(['day', 'month', 'year'], function(t){
30374             cfg.cn.push({
30375                 tag : 'div',
30376                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30377             });
30378         }, this);
30379         
30380         return cfg;
30381     },
30382     
30383     inputEl: function ()
30384     {
30385         return this.el.select('.roo-date-split-field-group-value', true).first();
30386     },
30387     
30388     onRender : function(ct, position) 
30389     {
30390         var _this = this;
30391         
30392         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30393         
30394         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30395         
30396         this.dayField = new Roo.bootstrap.ComboBox({
30397             allowBlank : this.dayAllowBlank,
30398             alwaysQuery : true,
30399             displayField : 'value',
30400             editable : false,
30401             fieldLabel : '',
30402             forceSelection : true,
30403             mode : 'local',
30404             placeholder : this.dayPlaceholder,
30405             selectOnFocus : true,
30406             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30407             triggerAction : 'all',
30408             typeAhead : true,
30409             valueField : 'value',
30410             store : new Roo.data.SimpleStore({
30411                 data : (function() {    
30412                     var days = [];
30413                     _this.fireEvent('days', _this, days);
30414                     return days;
30415                 })(),
30416                 fields : [ 'value' ]
30417             }),
30418             listeners : {
30419                 select : function (_self, record, index)
30420                 {
30421                     _this.setValue(_this.getValue());
30422                 }
30423             }
30424         });
30425
30426         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30427         
30428         this.monthField = new Roo.bootstrap.MonthField({
30429             after : '<i class=\"fa fa-calendar\"></i>',
30430             allowBlank : this.monthAllowBlank,
30431             placeholder : this.monthPlaceholder,
30432             readOnly : true,
30433             listeners : {
30434                 render : function (_self)
30435                 {
30436                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30437                         e.preventDefault();
30438                         _self.focus();
30439                     });
30440                 },
30441                 select : function (_self, oldvalue, newvalue)
30442                 {
30443                     _this.setValue(_this.getValue());
30444                 }
30445             }
30446         });
30447         
30448         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30449         
30450         this.yearField = new Roo.bootstrap.ComboBox({
30451             allowBlank : this.yearAllowBlank,
30452             alwaysQuery : true,
30453             displayField : 'value',
30454             editable : false,
30455             fieldLabel : '',
30456             forceSelection : true,
30457             mode : 'local',
30458             placeholder : this.yearPlaceholder,
30459             selectOnFocus : true,
30460             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30461             triggerAction : 'all',
30462             typeAhead : true,
30463             valueField : 'value',
30464             store : new Roo.data.SimpleStore({
30465                 data : (function() {
30466                     var years = [];
30467                     _this.fireEvent('years', _this, years);
30468                     return years;
30469                 })(),
30470                 fields : [ 'value' ]
30471             }),
30472             listeners : {
30473                 select : function (_self, record, index)
30474                 {
30475                     _this.setValue(_this.getValue());
30476                 }
30477             }
30478         });
30479
30480         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30481     },
30482     
30483     setValue : function(v, format)
30484     {
30485         this.inputEl.dom.value = v;
30486         
30487         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30488         
30489         var d = Date.parseDate(v, f);
30490         
30491         if(!d){
30492             this.validate();
30493             return;
30494         }
30495         
30496         this.setDay(d.format(this.dayFormat));
30497         this.setMonth(d.format(this.monthFormat));
30498         this.setYear(d.format(this.yearFormat));
30499         
30500         this.validate();
30501         
30502         return;
30503     },
30504     
30505     setDay : function(v)
30506     {
30507         this.dayField.setValue(v);
30508         this.inputEl.dom.value = this.getValue();
30509         this.validate();
30510         return;
30511     },
30512     
30513     setMonth : function(v)
30514     {
30515         this.monthField.setValue(v, true);
30516         this.inputEl.dom.value = this.getValue();
30517         this.validate();
30518         return;
30519     },
30520     
30521     setYear : function(v)
30522     {
30523         this.yearField.setValue(v);
30524         this.inputEl.dom.value = this.getValue();
30525         this.validate();
30526         return;
30527     },
30528     
30529     getDay : function()
30530     {
30531         return this.dayField.getValue();
30532     },
30533     
30534     getMonth : function()
30535     {
30536         return this.monthField.getValue();
30537     },
30538     
30539     getYear : function()
30540     {
30541         return this.yearField.getValue();
30542     },
30543     
30544     getValue : function()
30545     {
30546         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30547         
30548         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30549         
30550         return date;
30551     },
30552     
30553     reset : function()
30554     {
30555         this.setDay('');
30556         this.setMonth('');
30557         this.setYear('');
30558         this.inputEl.dom.value = '';
30559         this.validate();
30560         return;
30561     },
30562     
30563     validate : function()
30564     {
30565         var d = this.dayField.validate();
30566         var m = this.monthField.validate();
30567         var y = this.yearField.validate();
30568         
30569         var valid = true;
30570         
30571         if(
30572                 (!this.dayAllowBlank && !d) ||
30573                 (!this.monthAllowBlank && !m) ||
30574                 (!this.yearAllowBlank && !y)
30575         ){
30576             valid = false;
30577         }
30578         
30579         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30580             return valid;
30581         }
30582         
30583         if(valid){
30584             this.markValid();
30585             return valid;
30586         }
30587         
30588         this.markInvalid();
30589         
30590         return valid;
30591     },
30592     
30593     markValid : function()
30594     {
30595         
30596         var label = this.el.select('label', true).first();
30597         var icon = this.el.select('i.fa-star', true).first();
30598
30599         if(label && icon){
30600             icon.remove();
30601         }
30602         
30603         this.fireEvent('valid', this);
30604     },
30605     
30606      /**
30607      * Mark this field as invalid
30608      * @param {String} msg The validation message
30609      */
30610     markInvalid : function(msg)
30611     {
30612         
30613         var label = this.el.select('label', true).first();
30614         var icon = this.el.select('i.fa-star', true).first();
30615
30616         if(label && !icon){
30617             this.el.select('.roo-date-split-field-label', true).createChild({
30618                 tag : 'i',
30619                 cls : 'text-danger fa fa-lg fa-star',
30620                 tooltip : 'This field is required',
30621                 style : 'margin-right:5px;'
30622             }, label, true);
30623         }
30624         
30625         this.fireEvent('invalid', this, msg);
30626     },
30627     
30628     clearInvalid : function()
30629     {
30630         var label = this.el.select('label', true).first();
30631         var icon = this.el.select('i.fa-star', true).first();
30632
30633         if(label && icon){
30634             icon.remove();
30635         }
30636         
30637         this.fireEvent('valid', this);
30638     },
30639     
30640     getName: function()
30641     {
30642         return this.name;
30643     }
30644     
30645 });
30646
30647  /**
30648  *
30649  * This is based on 
30650  * http://masonry.desandro.com
30651  *
30652  * The idea is to render all the bricks based on vertical width...
30653  *
30654  * The original code extends 'outlayer' - we might need to use that....
30655  * 
30656  */
30657
30658
30659 /**
30660  * @class Roo.bootstrap.LayoutMasonry
30661  * @extends Roo.bootstrap.Component
30662  * Bootstrap Layout Masonry class
30663  * 
30664  * @constructor
30665  * Create a new Element
30666  * @param {Object} config The config object
30667  */
30668
30669 Roo.bootstrap.LayoutMasonry = function(config){
30670     
30671     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30672     
30673     this.bricks = [];
30674     
30675     Roo.bootstrap.LayoutMasonry.register(this);
30676     
30677     this.addEvents({
30678         // raw events
30679         /**
30680          * @event layout
30681          * Fire after layout the items
30682          * @param {Roo.bootstrap.LayoutMasonry} this
30683          * @param {Roo.EventObject} e
30684          */
30685         "layout" : true
30686     });
30687     
30688 };
30689
30690 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30691     
30692     /**
30693      * @cfg {Boolean} isLayoutInstant = no animation?
30694      */   
30695     isLayoutInstant : false, // needed?
30696    
30697     /**
30698      * @cfg {Number} boxWidth  width of the columns
30699      */   
30700     boxWidth : 450,
30701     
30702       /**
30703      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30704      */   
30705     boxHeight : 0,
30706     
30707     /**
30708      * @cfg {Number} padWidth padding below box..
30709      */   
30710     padWidth : 10, 
30711     
30712     /**
30713      * @cfg {Number} gutter gutter width..
30714      */   
30715     gutter : 10,
30716     
30717      /**
30718      * @cfg {Number} maxCols maximum number of columns
30719      */   
30720     
30721     maxCols: 0,
30722     
30723     /**
30724      * @cfg {Boolean} isAutoInitial defalut true
30725      */   
30726     isAutoInitial : true, 
30727     
30728     containerWidth: 0,
30729     
30730     /**
30731      * @cfg {Boolean} isHorizontal defalut false
30732      */   
30733     isHorizontal : false, 
30734
30735     currentSize : null,
30736     
30737     tag: 'div',
30738     
30739     cls: '',
30740     
30741     bricks: null, //CompositeElement
30742     
30743     cols : 1,
30744     
30745     _isLayoutInited : false,
30746     
30747 //    isAlternative : false, // only use for vertical layout...
30748     
30749     /**
30750      * @cfg {Number} alternativePadWidth padding below box..
30751      */   
30752     alternativePadWidth : 50,
30753     
30754     selectedBrick : [],
30755     
30756     getAutoCreate : function(){
30757         
30758         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30759         
30760         var cfg = {
30761             tag: this.tag,
30762             cls: 'blog-masonary-wrapper ' + this.cls,
30763             cn : {
30764                 cls : 'mas-boxes masonary'
30765             }
30766         };
30767         
30768         return cfg;
30769     },
30770     
30771     getChildContainer: function( )
30772     {
30773         if (this.boxesEl) {
30774             return this.boxesEl;
30775         }
30776         
30777         this.boxesEl = this.el.select('.mas-boxes').first();
30778         
30779         return this.boxesEl;
30780     },
30781     
30782     
30783     initEvents : function()
30784     {
30785         var _this = this;
30786         
30787         if(this.isAutoInitial){
30788             Roo.log('hook children rendered');
30789             this.on('childrenrendered', function() {
30790                 Roo.log('children rendered');
30791                 _this.initial();
30792             } ,this);
30793         }
30794     },
30795     
30796     initial : function()
30797     {
30798         this.selectedBrick = [];
30799         
30800         this.currentSize = this.el.getBox(true);
30801         
30802         Roo.EventManager.onWindowResize(this.resize, this); 
30803
30804         if(!this.isAutoInitial){
30805             this.layout();
30806             return;
30807         }
30808         
30809         this.layout();
30810         
30811         return;
30812         //this.layout.defer(500,this);
30813         
30814     },
30815     
30816     resize : function()
30817     {
30818         var cs = this.el.getBox(true);
30819         
30820         if (
30821                 this.currentSize.width == cs.width && 
30822                 this.currentSize.x == cs.x && 
30823                 this.currentSize.height == cs.height && 
30824                 this.currentSize.y == cs.y 
30825         ) {
30826             Roo.log("no change in with or X or Y");
30827             return;
30828         }
30829         
30830         this.currentSize = cs;
30831         
30832         this.layout();
30833         
30834     },
30835     
30836     layout : function()
30837     {   
30838         this._resetLayout();
30839         
30840         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30841         
30842         this.layoutItems( isInstant );
30843       
30844         this._isLayoutInited = true;
30845         
30846         this.fireEvent('layout', this);
30847         
30848     },
30849     
30850     _resetLayout : function()
30851     {
30852         if(this.isHorizontal){
30853             this.horizontalMeasureColumns();
30854             return;
30855         }
30856         
30857         this.verticalMeasureColumns();
30858         
30859     },
30860     
30861     verticalMeasureColumns : function()
30862     {
30863         this.getContainerWidth();
30864         
30865 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30866 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30867 //            return;
30868 //        }
30869         
30870         var boxWidth = this.boxWidth + this.padWidth;
30871         
30872         if(this.containerWidth < this.boxWidth){
30873             boxWidth = this.containerWidth
30874         }
30875         
30876         var containerWidth = this.containerWidth;
30877         
30878         var cols = Math.floor(containerWidth / boxWidth);
30879         
30880         this.cols = Math.max( cols, 1 );
30881         
30882         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30883         
30884         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30885         
30886         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30887         
30888         this.colWidth = boxWidth + avail - this.padWidth;
30889         
30890         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30891         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30892     },
30893     
30894     horizontalMeasureColumns : function()
30895     {
30896         this.getContainerWidth();
30897         
30898         var boxWidth = this.boxWidth;
30899         
30900         if(this.containerWidth < boxWidth){
30901             boxWidth = this.containerWidth;
30902         }
30903         
30904         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30905         
30906         this.el.setHeight(boxWidth);
30907         
30908     },
30909     
30910     getContainerWidth : function()
30911     {
30912         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30913     },
30914     
30915     layoutItems : function( isInstant )
30916     {
30917         Roo.log(this.bricks);
30918         
30919         var items = Roo.apply([], this.bricks);
30920         
30921         if(this.isHorizontal){
30922             this._horizontalLayoutItems( items , isInstant );
30923             return;
30924         }
30925         
30926 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30927 //            this._verticalAlternativeLayoutItems( items , isInstant );
30928 //            return;
30929 //        }
30930         
30931         this._verticalLayoutItems( items , isInstant );
30932         
30933     },
30934     
30935     _verticalLayoutItems : function ( items , isInstant)
30936     {
30937         if ( !items || !items.length ) {
30938             return;
30939         }
30940         
30941         var standard = [
30942             ['xs', 'xs', 'xs', 'tall'],
30943             ['xs', 'xs', 'tall'],
30944             ['xs', 'xs', 'sm'],
30945             ['xs', 'xs', 'xs'],
30946             ['xs', 'tall'],
30947             ['xs', 'sm'],
30948             ['xs', 'xs'],
30949             ['xs'],
30950             
30951             ['sm', 'xs', 'xs'],
30952             ['sm', 'xs'],
30953             ['sm'],
30954             
30955             ['tall', 'xs', 'xs', 'xs'],
30956             ['tall', 'xs', 'xs'],
30957             ['tall', 'xs'],
30958             ['tall']
30959             
30960         ];
30961         
30962         var queue = [];
30963         
30964         var boxes = [];
30965         
30966         var box = [];
30967         
30968         Roo.each(items, function(item, k){
30969             
30970             switch (item.size) {
30971                 // these layouts take up a full box,
30972                 case 'md' :
30973                 case 'md-left' :
30974                 case 'md-right' :
30975                 case 'wide' :
30976                     
30977                     if(box.length){
30978                         boxes.push(box);
30979                         box = [];
30980                     }
30981                     
30982                     boxes.push([item]);
30983                     
30984                     break;
30985                     
30986                 case 'xs' :
30987                 case 'sm' :
30988                 case 'tall' :
30989                     
30990                     box.push(item);
30991                     
30992                     break;
30993                 default :
30994                     break;
30995                     
30996             }
30997             
30998         }, this);
30999         
31000         if(box.length){
31001             boxes.push(box);
31002             box = [];
31003         }
31004         
31005         var filterPattern = function(box, length)
31006         {
31007             if(!box.length){
31008                 return;
31009             }
31010             
31011             var match = false;
31012             
31013             var pattern = box.slice(0, length);
31014             
31015             var format = [];
31016             
31017             Roo.each(pattern, function(i){
31018                 format.push(i.size);
31019             }, this);
31020             
31021             Roo.each(standard, function(s){
31022                 
31023                 if(String(s) != String(format)){
31024                     return;
31025                 }
31026                 
31027                 match = true;
31028                 return false;
31029                 
31030             }, this);
31031             
31032             if(!match && length == 1){
31033                 return;
31034             }
31035             
31036             if(!match){
31037                 filterPattern(box, length - 1);
31038                 return;
31039             }
31040                 
31041             queue.push(pattern);
31042
31043             box = box.slice(length, box.length);
31044
31045             filterPattern(box, 4);
31046
31047             return;
31048             
31049         }
31050         
31051         Roo.each(boxes, function(box, k){
31052             
31053             if(!box.length){
31054                 return;
31055             }
31056             
31057             if(box.length == 1){
31058                 queue.push(box);
31059                 return;
31060             }
31061             
31062             filterPattern(box, 4);
31063             
31064         }, this);
31065         
31066         this._processVerticalLayoutQueue( queue, isInstant );
31067         
31068     },
31069     
31070 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31071 //    {
31072 //        if ( !items || !items.length ) {
31073 //            return;
31074 //        }
31075 //
31076 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31077 //        
31078 //    },
31079     
31080     _horizontalLayoutItems : function ( items , isInstant)
31081     {
31082         if ( !items || !items.length || items.length < 3) {
31083             return;
31084         }
31085         
31086         items.reverse();
31087         
31088         var eItems = items.slice(0, 3);
31089         
31090         items = items.slice(3, items.length);
31091         
31092         var standard = [
31093             ['xs', 'xs', 'xs', 'wide'],
31094             ['xs', 'xs', 'wide'],
31095             ['xs', 'xs', 'sm'],
31096             ['xs', 'xs', 'xs'],
31097             ['xs', 'wide'],
31098             ['xs', 'sm'],
31099             ['xs', 'xs'],
31100             ['xs'],
31101             
31102             ['sm', 'xs', 'xs'],
31103             ['sm', 'xs'],
31104             ['sm'],
31105             
31106             ['wide', 'xs', 'xs', 'xs'],
31107             ['wide', 'xs', 'xs'],
31108             ['wide', 'xs'],
31109             ['wide'],
31110             
31111             ['wide-thin']
31112         ];
31113         
31114         var queue = [];
31115         
31116         var boxes = [];
31117         
31118         var box = [];
31119         
31120         Roo.each(items, function(item, k){
31121             
31122             switch (item.size) {
31123                 case 'md' :
31124                 case 'md-left' :
31125                 case 'md-right' :
31126                 case 'tall' :
31127                     
31128                     if(box.length){
31129                         boxes.push(box);
31130                         box = [];
31131                     }
31132                     
31133                     boxes.push([item]);
31134                     
31135                     break;
31136                     
31137                 case 'xs' :
31138                 case 'sm' :
31139                 case 'wide' :
31140                 case 'wide-thin' :
31141                     
31142                     box.push(item);
31143                     
31144                     break;
31145                 default :
31146                     break;
31147                     
31148             }
31149             
31150         }, this);
31151         
31152         if(box.length){
31153             boxes.push(box);
31154             box = [];
31155         }
31156         
31157         var filterPattern = function(box, length)
31158         {
31159             if(!box.length){
31160                 return;
31161             }
31162             
31163             var match = false;
31164             
31165             var pattern = box.slice(0, length);
31166             
31167             var format = [];
31168             
31169             Roo.each(pattern, function(i){
31170                 format.push(i.size);
31171             }, this);
31172             
31173             Roo.each(standard, function(s){
31174                 
31175                 if(String(s) != String(format)){
31176                     return;
31177                 }
31178                 
31179                 match = true;
31180                 return false;
31181                 
31182             }, this);
31183             
31184             if(!match && length == 1){
31185                 return;
31186             }
31187             
31188             if(!match){
31189                 filterPattern(box, length - 1);
31190                 return;
31191             }
31192                 
31193             queue.push(pattern);
31194
31195             box = box.slice(length, box.length);
31196
31197             filterPattern(box, 4);
31198
31199             return;
31200             
31201         }
31202         
31203         Roo.each(boxes, function(box, k){
31204             
31205             if(!box.length){
31206                 return;
31207             }
31208             
31209             if(box.length == 1){
31210                 queue.push(box);
31211                 return;
31212             }
31213             
31214             filterPattern(box, 4);
31215             
31216         }, this);
31217         
31218         
31219         var prune = [];
31220         
31221         var pos = this.el.getBox(true);
31222         
31223         var minX = pos.x;
31224         
31225         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31226         
31227         var hit_end = false;
31228         
31229         Roo.each(queue, function(box){
31230             
31231             if(hit_end){
31232                 
31233                 Roo.each(box, function(b){
31234                 
31235                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31236                     b.el.hide();
31237
31238                 }, this);
31239
31240                 return;
31241             }
31242             
31243             var mx = 0;
31244             
31245             Roo.each(box, function(b){
31246                 
31247                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31248                 b.el.show();
31249
31250                 mx = Math.max(mx, b.x);
31251                 
31252             }, this);
31253             
31254             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31255             
31256             if(maxX < minX){
31257                 
31258                 Roo.each(box, function(b){
31259                 
31260                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31261                     b.el.hide();
31262                     
31263                 }, this);
31264                 
31265                 hit_end = true;
31266                 
31267                 return;
31268             }
31269             
31270             prune.push(box);
31271             
31272         }, this);
31273         
31274         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31275     },
31276     
31277     /** Sets position of item in DOM
31278     * @param {Element} item
31279     * @param {Number} x - horizontal position
31280     * @param {Number} y - vertical position
31281     * @param {Boolean} isInstant - disables transitions
31282     */
31283     _processVerticalLayoutQueue : function( queue, isInstant )
31284     {
31285         var pos = this.el.getBox(true);
31286         var x = pos.x;
31287         var y = pos.y;
31288         var maxY = [];
31289         
31290         for (var i = 0; i < this.cols; i++){
31291             maxY[i] = pos.y;
31292         }
31293         
31294         Roo.each(queue, function(box, k){
31295             
31296             var col = k % this.cols;
31297             
31298             Roo.each(box, function(b,kk){
31299                 
31300                 b.el.position('absolute');
31301                 
31302                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31303                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31304                 
31305                 if(b.size == 'md-left' || b.size == 'md-right'){
31306                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31307                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31308                 }
31309                 
31310                 b.el.setWidth(width);
31311                 b.el.setHeight(height);
31312                 // iframe?
31313                 b.el.select('iframe',true).setSize(width,height);
31314                 
31315             }, this);
31316             
31317             for (var i = 0; i < this.cols; i++){
31318                 
31319                 if(maxY[i] < maxY[col]){
31320                     col = i;
31321                     continue;
31322                 }
31323                 
31324                 col = Math.min(col, i);
31325                 
31326             }
31327             
31328             x = pos.x + col * (this.colWidth + this.padWidth);
31329             
31330             y = maxY[col];
31331             
31332             var positions = [];
31333             
31334             switch (box.length){
31335                 case 1 :
31336                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31337                     break;
31338                 case 2 :
31339                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31340                     break;
31341                 case 3 :
31342                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31343                     break;
31344                 case 4 :
31345                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31346                     break;
31347                 default :
31348                     break;
31349             }
31350             
31351             Roo.each(box, function(b,kk){
31352                 
31353                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31354                 
31355                 var sz = b.el.getSize();
31356                 
31357                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31358                 
31359             }, this);
31360             
31361         }, this);
31362         
31363         var mY = 0;
31364         
31365         for (var i = 0; i < this.cols; i++){
31366             mY = Math.max(mY, maxY[i]);
31367         }
31368         
31369         this.el.setHeight(mY - pos.y);
31370         
31371     },
31372     
31373 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31374 //    {
31375 //        var pos = this.el.getBox(true);
31376 //        var x = pos.x;
31377 //        var y = pos.y;
31378 //        var maxX = pos.right;
31379 //        
31380 //        var maxHeight = 0;
31381 //        
31382 //        Roo.each(items, function(item, k){
31383 //            
31384 //            var c = k % 2;
31385 //            
31386 //            item.el.position('absolute');
31387 //                
31388 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31389 //
31390 //            item.el.setWidth(width);
31391 //
31392 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31393 //
31394 //            item.el.setHeight(height);
31395 //            
31396 //            if(c == 0){
31397 //                item.el.setXY([x, y], isInstant ? false : true);
31398 //            } else {
31399 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31400 //            }
31401 //            
31402 //            y = y + height + this.alternativePadWidth;
31403 //            
31404 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31405 //            
31406 //        }, this);
31407 //        
31408 //        this.el.setHeight(maxHeight);
31409 //        
31410 //    },
31411     
31412     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31413     {
31414         var pos = this.el.getBox(true);
31415         
31416         var minX = pos.x;
31417         var minY = pos.y;
31418         
31419         var maxX = pos.right;
31420         
31421         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31422         
31423         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31424         
31425         Roo.each(queue, function(box, k){
31426             
31427             Roo.each(box, function(b, kk){
31428                 
31429                 b.el.position('absolute');
31430                 
31431                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31432                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31433                 
31434                 if(b.size == 'md-left' || b.size == 'md-right'){
31435                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31436                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31437                 }
31438                 
31439                 b.el.setWidth(width);
31440                 b.el.setHeight(height);
31441                 
31442             }, this);
31443             
31444             if(!box.length){
31445                 return;
31446             }
31447             
31448             var positions = [];
31449             
31450             switch (box.length){
31451                 case 1 :
31452                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31453                     break;
31454                 case 2 :
31455                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31456                     break;
31457                 case 3 :
31458                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31459                     break;
31460                 case 4 :
31461                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31462                     break;
31463                 default :
31464                     break;
31465             }
31466             
31467             Roo.each(box, function(b,kk){
31468                 
31469                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31470                 
31471                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31472                 
31473             }, this);
31474             
31475         }, this);
31476         
31477     },
31478     
31479     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31480     {
31481         Roo.each(eItems, function(b,k){
31482             
31483             b.size = (k == 0) ? 'sm' : 'xs';
31484             b.x = (k == 0) ? 2 : 1;
31485             b.y = (k == 0) ? 2 : 1;
31486             
31487             b.el.position('absolute');
31488             
31489             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31490                 
31491             b.el.setWidth(width);
31492             
31493             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31494             
31495             b.el.setHeight(height);
31496             
31497         }, this);
31498
31499         var positions = [];
31500         
31501         positions.push({
31502             x : maxX - this.unitWidth * 2 - this.gutter,
31503             y : minY
31504         });
31505         
31506         positions.push({
31507             x : maxX - this.unitWidth,
31508             y : minY + (this.unitWidth + this.gutter) * 2
31509         });
31510         
31511         positions.push({
31512             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31513             y : minY
31514         });
31515         
31516         Roo.each(eItems, function(b,k){
31517             
31518             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31519
31520         }, this);
31521         
31522     },
31523     
31524     getVerticalOneBoxColPositions : function(x, y, box)
31525     {
31526         var pos = [];
31527         
31528         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31529         
31530         if(box[0].size == 'md-left'){
31531             rand = 0;
31532         }
31533         
31534         if(box[0].size == 'md-right'){
31535             rand = 1;
31536         }
31537         
31538         pos.push({
31539             x : x + (this.unitWidth + this.gutter) * rand,
31540             y : y
31541         });
31542         
31543         return pos;
31544     },
31545     
31546     getVerticalTwoBoxColPositions : function(x, y, box)
31547     {
31548         var pos = [];
31549         
31550         if(box[0].size == 'xs'){
31551             
31552             pos.push({
31553                 x : x,
31554                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31555             });
31556
31557             pos.push({
31558                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31559                 y : y
31560             });
31561             
31562             return pos;
31563             
31564         }
31565         
31566         pos.push({
31567             x : x,
31568             y : y
31569         });
31570
31571         pos.push({
31572             x : x + (this.unitWidth + this.gutter) * 2,
31573             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31574         });
31575         
31576         return pos;
31577         
31578     },
31579     
31580     getVerticalThreeBoxColPositions : function(x, y, box)
31581     {
31582         var pos = [];
31583         
31584         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31585             
31586             pos.push({
31587                 x : x,
31588                 y : y
31589             });
31590
31591             pos.push({
31592                 x : x + (this.unitWidth + this.gutter) * 1,
31593                 y : y
31594             });
31595             
31596             pos.push({
31597                 x : x + (this.unitWidth + this.gutter) * 2,
31598                 y : y
31599             });
31600             
31601             return pos;
31602             
31603         }
31604         
31605         if(box[0].size == 'xs' && box[1].size == 'xs'){
31606             
31607             pos.push({
31608                 x : x,
31609                 y : y
31610             });
31611
31612             pos.push({
31613                 x : x,
31614                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31615             });
31616             
31617             pos.push({
31618                 x : x + (this.unitWidth + this.gutter) * 1,
31619                 y : y
31620             });
31621             
31622             return pos;
31623             
31624         }
31625         
31626         pos.push({
31627             x : x,
31628             y : y
31629         });
31630
31631         pos.push({
31632             x : x + (this.unitWidth + this.gutter) * 2,
31633             y : y
31634         });
31635
31636         pos.push({
31637             x : x + (this.unitWidth + this.gutter) * 2,
31638             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31639         });
31640             
31641         return pos;
31642         
31643     },
31644     
31645     getVerticalFourBoxColPositions : function(x, y, box)
31646     {
31647         var pos = [];
31648         
31649         if(box[0].size == 'xs'){
31650             
31651             pos.push({
31652                 x : x,
31653                 y : y
31654             });
31655
31656             pos.push({
31657                 x : x,
31658                 y : y + (this.unitHeight + this.gutter) * 1
31659             });
31660             
31661             pos.push({
31662                 x : x,
31663                 y : y + (this.unitHeight + this.gutter) * 2
31664             });
31665             
31666             pos.push({
31667                 x : x + (this.unitWidth + this.gutter) * 1,
31668                 y : y
31669             });
31670             
31671             return pos;
31672             
31673         }
31674         
31675         pos.push({
31676             x : x,
31677             y : y
31678         });
31679
31680         pos.push({
31681             x : x + (this.unitWidth + this.gutter) * 2,
31682             y : y
31683         });
31684
31685         pos.push({
31686             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31687             y : y + (this.unitHeight + this.gutter) * 1
31688         });
31689
31690         pos.push({
31691             x : x + (this.unitWidth + this.gutter) * 2,
31692             y : y + (this.unitWidth + this.gutter) * 2
31693         });
31694
31695         return pos;
31696         
31697     },
31698     
31699     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31700     {
31701         var pos = [];
31702         
31703         if(box[0].size == 'md-left'){
31704             pos.push({
31705                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31706                 y : minY
31707             });
31708             
31709             return pos;
31710         }
31711         
31712         if(box[0].size == 'md-right'){
31713             pos.push({
31714                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31715                 y : minY + (this.unitWidth + this.gutter) * 1
31716             });
31717             
31718             return pos;
31719         }
31720         
31721         var rand = Math.floor(Math.random() * (4 - box[0].y));
31722         
31723         pos.push({
31724             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31725             y : minY + (this.unitWidth + this.gutter) * rand
31726         });
31727         
31728         return pos;
31729         
31730     },
31731     
31732     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31733     {
31734         var pos = [];
31735         
31736         if(box[0].size == 'xs'){
31737             
31738             pos.push({
31739                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31740                 y : minY
31741             });
31742
31743             pos.push({
31744                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31745                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31746             });
31747             
31748             return pos;
31749             
31750         }
31751         
31752         pos.push({
31753             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31754             y : minY
31755         });
31756
31757         pos.push({
31758             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31759             y : minY + (this.unitWidth + this.gutter) * 2
31760         });
31761         
31762         return pos;
31763         
31764     },
31765     
31766     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31767     {
31768         var pos = [];
31769         
31770         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31771             
31772             pos.push({
31773                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31774                 y : minY
31775             });
31776
31777             pos.push({
31778                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31779                 y : minY + (this.unitWidth + this.gutter) * 1
31780             });
31781             
31782             pos.push({
31783                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31784                 y : minY + (this.unitWidth + this.gutter) * 2
31785             });
31786             
31787             return pos;
31788             
31789         }
31790         
31791         if(box[0].size == 'xs' && box[1].size == 'xs'){
31792             
31793             pos.push({
31794                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31795                 y : minY
31796             });
31797
31798             pos.push({
31799                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31800                 y : minY
31801             });
31802             
31803             pos.push({
31804                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31805                 y : minY + (this.unitWidth + this.gutter) * 1
31806             });
31807             
31808             return pos;
31809             
31810         }
31811         
31812         pos.push({
31813             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31814             y : minY
31815         });
31816
31817         pos.push({
31818             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31819             y : minY + (this.unitWidth + this.gutter) * 2
31820         });
31821
31822         pos.push({
31823             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31824             y : minY + (this.unitWidth + this.gutter) * 2
31825         });
31826             
31827         return pos;
31828         
31829     },
31830     
31831     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31832     {
31833         var pos = [];
31834         
31835         if(box[0].size == 'xs'){
31836             
31837             pos.push({
31838                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31839                 y : minY
31840             });
31841
31842             pos.push({
31843                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31844                 y : minY
31845             });
31846             
31847             pos.push({
31848                 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),
31849                 y : minY
31850             });
31851             
31852             pos.push({
31853                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31854                 y : minY + (this.unitWidth + this.gutter) * 1
31855             });
31856             
31857             return pos;
31858             
31859         }
31860         
31861         pos.push({
31862             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31863             y : minY
31864         });
31865         
31866         pos.push({
31867             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31868             y : minY + (this.unitWidth + this.gutter) * 2
31869         });
31870         
31871         pos.push({
31872             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31873             y : minY + (this.unitWidth + this.gutter) * 2
31874         });
31875         
31876         pos.push({
31877             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),
31878             y : minY + (this.unitWidth + this.gutter) * 2
31879         });
31880
31881         return pos;
31882         
31883     },
31884     
31885     /**
31886     * remove a Masonry Brick
31887     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31888     */
31889     removeBrick : function(brick_id)
31890     {
31891         if (!brick_id) {
31892             return;
31893         }
31894         
31895         for (var i = 0; i<this.bricks.length; i++) {
31896             if (this.bricks[i].id == brick_id) {
31897                 this.bricks.splice(i,1);
31898                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31899                 this.initial();
31900             }
31901         }
31902     },
31903     
31904     /**
31905     * adds a Masonry Brick
31906     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31907     */
31908     addBrick : function(cfg)
31909     {
31910         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31911         //this.register(cn);
31912         cn.parentId = this.id;
31913         cn.onRender(this.el, null);
31914         return cn;
31915     },
31916     
31917     /**
31918     * register a Masonry Brick
31919     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31920     */
31921     
31922     register : function(brick)
31923     {
31924         this.bricks.push(brick);
31925         brick.masonryId = this.id;
31926     },
31927     
31928     /**
31929     * clear all the Masonry Brick
31930     */
31931     clearAll : function()
31932     {
31933         this.bricks = [];
31934         //this.getChildContainer().dom.innerHTML = "";
31935         this.el.dom.innerHTML = '';
31936     },
31937     
31938     getSelected : function()
31939     {
31940         if (!this.selectedBrick) {
31941             return false;
31942         }
31943         
31944         return this.selectedBrick;
31945     }
31946 });
31947
31948 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31949     
31950     groups: {},
31951      /**
31952     * register a Masonry Layout
31953     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31954     */
31955     
31956     register : function(layout)
31957     {
31958         this.groups[layout.id] = layout;
31959     },
31960     /**
31961     * fetch a  Masonry Layout based on the masonry layout ID
31962     * @param {string} the masonry layout to add
31963     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31964     */
31965     
31966     get: function(layout_id) {
31967         if (typeof(this.groups[layout_id]) == 'undefined') {
31968             return false;
31969         }
31970         return this.groups[layout_id] ;
31971     }
31972     
31973     
31974     
31975 });
31976
31977  
31978
31979  /**
31980  *
31981  * This is based on 
31982  * http://masonry.desandro.com
31983  *
31984  * The idea is to render all the bricks based on vertical width...
31985  *
31986  * The original code extends 'outlayer' - we might need to use that....
31987  * 
31988  */
31989
31990
31991 /**
31992  * @class Roo.bootstrap.LayoutMasonryAuto
31993  * @extends Roo.bootstrap.Component
31994  * Bootstrap Layout Masonry class
31995  * 
31996  * @constructor
31997  * Create a new Element
31998  * @param {Object} config The config object
31999  */
32000
32001 Roo.bootstrap.LayoutMasonryAuto = function(config){
32002     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32003 };
32004
32005 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32006     
32007       /**
32008      * @cfg {Boolean} isFitWidth  - resize the width..
32009      */   
32010     isFitWidth : false,  // options..
32011     /**
32012      * @cfg {Boolean} isOriginLeft = left align?
32013      */   
32014     isOriginLeft : true,
32015     /**
32016      * @cfg {Boolean} isOriginTop = top align?
32017      */   
32018     isOriginTop : false,
32019     /**
32020      * @cfg {Boolean} isLayoutInstant = no animation?
32021      */   
32022     isLayoutInstant : false, // needed?
32023     /**
32024      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32025      */   
32026     isResizingContainer : true,
32027     /**
32028      * @cfg {Number} columnWidth  width of the columns 
32029      */   
32030     
32031     columnWidth : 0,
32032     
32033     /**
32034      * @cfg {Number} maxCols maximum number of columns
32035      */   
32036     
32037     maxCols: 0,
32038     /**
32039      * @cfg {Number} padHeight padding below box..
32040      */   
32041     
32042     padHeight : 10, 
32043     
32044     /**
32045      * @cfg {Boolean} isAutoInitial defalut true
32046      */   
32047     
32048     isAutoInitial : true, 
32049     
32050     // private?
32051     gutter : 0,
32052     
32053     containerWidth: 0,
32054     initialColumnWidth : 0,
32055     currentSize : null,
32056     
32057     colYs : null, // array.
32058     maxY : 0,
32059     padWidth: 10,
32060     
32061     
32062     tag: 'div',
32063     cls: '',
32064     bricks: null, //CompositeElement
32065     cols : 0, // array?
32066     // element : null, // wrapped now this.el
32067     _isLayoutInited : null, 
32068     
32069     
32070     getAutoCreate : function(){
32071         
32072         var cfg = {
32073             tag: this.tag,
32074             cls: 'blog-masonary-wrapper ' + this.cls,
32075             cn : {
32076                 cls : 'mas-boxes masonary'
32077             }
32078         };
32079         
32080         return cfg;
32081     },
32082     
32083     getChildContainer: function( )
32084     {
32085         if (this.boxesEl) {
32086             return this.boxesEl;
32087         }
32088         
32089         this.boxesEl = this.el.select('.mas-boxes').first();
32090         
32091         return this.boxesEl;
32092     },
32093     
32094     
32095     initEvents : function()
32096     {
32097         var _this = this;
32098         
32099         if(this.isAutoInitial){
32100             Roo.log('hook children rendered');
32101             this.on('childrenrendered', function() {
32102                 Roo.log('children rendered');
32103                 _this.initial();
32104             } ,this);
32105         }
32106         
32107     },
32108     
32109     initial : function()
32110     {
32111         this.reloadItems();
32112
32113         this.currentSize = this.el.getBox(true);
32114
32115         /// was window resize... - let's see if this works..
32116         Roo.EventManager.onWindowResize(this.resize, this); 
32117
32118         if(!this.isAutoInitial){
32119             this.layout();
32120             return;
32121         }
32122         
32123         this.layout.defer(500,this);
32124     },
32125     
32126     reloadItems: function()
32127     {
32128         this.bricks = this.el.select('.masonry-brick', true);
32129         
32130         this.bricks.each(function(b) {
32131             //Roo.log(b.getSize());
32132             if (!b.attr('originalwidth')) {
32133                 b.attr('originalwidth',  b.getSize().width);
32134             }
32135             
32136         });
32137         
32138         Roo.log(this.bricks.elements.length);
32139     },
32140     
32141     resize : function()
32142     {
32143         Roo.log('resize');
32144         var cs = this.el.getBox(true);
32145         
32146         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32147             Roo.log("no change in with or X");
32148             return;
32149         }
32150         this.currentSize = cs;
32151         this.layout();
32152     },
32153     
32154     layout : function()
32155     {
32156          Roo.log('layout');
32157         this._resetLayout();
32158         //this._manageStamps();
32159       
32160         // don't animate first layout
32161         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32162         this.layoutItems( isInstant );
32163       
32164         // flag for initalized
32165         this._isLayoutInited = true;
32166     },
32167     
32168     layoutItems : function( isInstant )
32169     {
32170         //var items = this._getItemsForLayout( this.items );
32171         // original code supports filtering layout items.. we just ignore it..
32172         
32173         this._layoutItems( this.bricks , isInstant );
32174       
32175         this._postLayout();
32176     },
32177     _layoutItems : function ( items , isInstant)
32178     {
32179        //this.fireEvent( 'layout', this, items );
32180     
32181
32182         if ( !items || !items.elements.length ) {
32183           // no items, emit event with empty array
32184             return;
32185         }
32186
32187         var queue = [];
32188         items.each(function(item) {
32189             Roo.log("layout item");
32190             Roo.log(item);
32191             // get x/y object from method
32192             var position = this._getItemLayoutPosition( item );
32193             // enqueue
32194             position.item = item;
32195             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32196             queue.push( position );
32197         }, this);
32198       
32199         this._processLayoutQueue( queue );
32200     },
32201     /** Sets position of item in DOM
32202     * @param {Element} item
32203     * @param {Number} x - horizontal position
32204     * @param {Number} y - vertical position
32205     * @param {Boolean} isInstant - disables transitions
32206     */
32207     _processLayoutQueue : function( queue )
32208     {
32209         for ( var i=0, len = queue.length; i < len; i++ ) {
32210             var obj = queue[i];
32211             obj.item.position('absolute');
32212             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32213         }
32214     },
32215       
32216     
32217     /**
32218     * Any logic you want to do after each layout,
32219     * i.e. size the container
32220     */
32221     _postLayout : function()
32222     {
32223         this.resizeContainer();
32224     },
32225     
32226     resizeContainer : function()
32227     {
32228         if ( !this.isResizingContainer ) {
32229             return;
32230         }
32231         var size = this._getContainerSize();
32232         if ( size ) {
32233             this.el.setSize(size.width,size.height);
32234             this.boxesEl.setSize(size.width,size.height);
32235         }
32236     },
32237     
32238     
32239     
32240     _resetLayout : function()
32241     {
32242         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32243         this.colWidth = this.el.getWidth();
32244         //this.gutter = this.el.getWidth(); 
32245         
32246         this.measureColumns();
32247
32248         // reset column Y
32249         var i = this.cols;
32250         this.colYs = [];
32251         while (i--) {
32252             this.colYs.push( 0 );
32253         }
32254     
32255         this.maxY = 0;
32256     },
32257
32258     measureColumns : function()
32259     {
32260         this.getContainerWidth();
32261       // if columnWidth is 0, default to outerWidth of first item
32262         if ( !this.columnWidth ) {
32263             var firstItem = this.bricks.first();
32264             Roo.log(firstItem);
32265             this.columnWidth  = this.containerWidth;
32266             if (firstItem && firstItem.attr('originalwidth') ) {
32267                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32268             }
32269             // columnWidth fall back to item of first element
32270             Roo.log("set column width?");
32271                         this.initialColumnWidth = this.columnWidth  ;
32272
32273             // if first elem has no width, default to size of container
32274             
32275         }
32276         
32277         
32278         if (this.initialColumnWidth) {
32279             this.columnWidth = this.initialColumnWidth;
32280         }
32281         
32282         
32283             
32284         // column width is fixed at the top - however if container width get's smaller we should
32285         // reduce it...
32286         
32287         // this bit calcs how man columns..
32288             
32289         var columnWidth = this.columnWidth += this.gutter;
32290       
32291         // calculate columns
32292         var containerWidth = this.containerWidth + this.gutter;
32293         
32294         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32295         // fix rounding errors, typically with gutters
32296         var excess = columnWidth - containerWidth % columnWidth;
32297         
32298         
32299         // if overshoot is less than a pixel, round up, otherwise floor it
32300         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32301         cols = Math[ mathMethod ]( cols );
32302         this.cols = Math.max( cols, 1 );
32303         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32304         
32305          // padding positioning..
32306         var totalColWidth = this.cols * this.columnWidth;
32307         var padavail = this.containerWidth - totalColWidth;
32308         // so for 2 columns - we need 3 'pads'
32309         
32310         var padNeeded = (1+this.cols) * this.padWidth;
32311         
32312         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32313         
32314         this.columnWidth += padExtra
32315         //this.padWidth = Math.floor(padavail /  ( this.cols));
32316         
32317         // adjust colum width so that padding is fixed??
32318         
32319         // we have 3 columns ... total = width * 3
32320         // we have X left over... that should be used by 
32321         
32322         //if (this.expandC) {
32323             
32324         //}
32325         
32326         
32327         
32328     },
32329     
32330     getContainerWidth : function()
32331     {
32332        /* // container is parent if fit width
32333         var container = this.isFitWidth ? this.element.parentNode : this.element;
32334         // check that this.size and size are there
32335         // IE8 triggers resize on body size change, so they might not be
32336         
32337         var size = getSize( container );  //FIXME
32338         this.containerWidth = size && size.innerWidth; //FIXME
32339         */
32340          
32341         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32342         
32343     },
32344     
32345     _getItemLayoutPosition : function( item )  // what is item?
32346     {
32347         // we resize the item to our columnWidth..
32348       
32349         item.setWidth(this.columnWidth);
32350         item.autoBoxAdjust  = false;
32351         
32352         var sz = item.getSize();
32353  
32354         // how many columns does this brick span
32355         var remainder = this.containerWidth % this.columnWidth;
32356         
32357         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32358         // round if off by 1 pixel, otherwise use ceil
32359         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32360         colSpan = Math.min( colSpan, this.cols );
32361         
32362         // normally this should be '1' as we dont' currently allow multi width columns..
32363         
32364         var colGroup = this._getColGroup( colSpan );
32365         // get the minimum Y value from the columns
32366         var minimumY = Math.min.apply( Math, colGroup );
32367         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32368         
32369         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32370          
32371         // position the brick
32372         var position = {
32373             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32374             y: this.currentSize.y + minimumY + this.padHeight
32375         };
32376         
32377         Roo.log(position);
32378         // apply setHeight to necessary columns
32379         var setHeight = minimumY + sz.height + this.padHeight;
32380         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32381         
32382         var setSpan = this.cols + 1 - colGroup.length;
32383         for ( var i = 0; i < setSpan; i++ ) {
32384           this.colYs[ shortColIndex + i ] = setHeight ;
32385         }
32386       
32387         return position;
32388     },
32389     
32390     /**
32391      * @param {Number} colSpan - number of columns the element spans
32392      * @returns {Array} colGroup
32393      */
32394     _getColGroup : function( colSpan )
32395     {
32396         if ( colSpan < 2 ) {
32397           // if brick spans only one column, use all the column Ys
32398           return this.colYs;
32399         }
32400       
32401         var colGroup = [];
32402         // how many different places could this brick fit horizontally
32403         var groupCount = this.cols + 1 - colSpan;
32404         // for each group potential horizontal position
32405         for ( var i = 0; i < groupCount; i++ ) {
32406           // make an array of colY values for that one group
32407           var groupColYs = this.colYs.slice( i, i + colSpan );
32408           // and get the max value of the array
32409           colGroup[i] = Math.max.apply( Math, groupColYs );
32410         }
32411         return colGroup;
32412     },
32413     /*
32414     _manageStamp : function( stamp )
32415     {
32416         var stampSize =  stamp.getSize();
32417         var offset = stamp.getBox();
32418         // get the columns that this stamp affects
32419         var firstX = this.isOriginLeft ? offset.x : offset.right;
32420         var lastX = firstX + stampSize.width;
32421         var firstCol = Math.floor( firstX / this.columnWidth );
32422         firstCol = Math.max( 0, firstCol );
32423         
32424         var lastCol = Math.floor( lastX / this.columnWidth );
32425         // lastCol should not go over if multiple of columnWidth #425
32426         lastCol -= lastX % this.columnWidth ? 0 : 1;
32427         lastCol = Math.min( this.cols - 1, lastCol );
32428         
32429         // set colYs to bottom of the stamp
32430         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32431             stampSize.height;
32432             
32433         for ( var i = firstCol; i <= lastCol; i++ ) {
32434           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32435         }
32436     },
32437     */
32438     
32439     _getContainerSize : function()
32440     {
32441         this.maxY = Math.max.apply( Math, this.colYs );
32442         var size = {
32443             height: this.maxY
32444         };
32445       
32446         if ( this.isFitWidth ) {
32447             size.width = this._getContainerFitWidth();
32448         }
32449       
32450         return size;
32451     },
32452     
32453     _getContainerFitWidth : function()
32454     {
32455         var unusedCols = 0;
32456         // count unused columns
32457         var i = this.cols;
32458         while ( --i ) {
32459           if ( this.colYs[i] !== 0 ) {
32460             break;
32461           }
32462           unusedCols++;
32463         }
32464         // fit container to columns that have been used
32465         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32466     },
32467     
32468     needsResizeLayout : function()
32469     {
32470         var previousWidth = this.containerWidth;
32471         this.getContainerWidth();
32472         return previousWidth !== this.containerWidth;
32473     }
32474  
32475 });
32476
32477  
32478
32479  /*
32480  * - LGPL
32481  *
32482  * element
32483  * 
32484  */
32485
32486 /**
32487  * @class Roo.bootstrap.MasonryBrick
32488  * @extends Roo.bootstrap.Component
32489  * Bootstrap MasonryBrick class
32490  * 
32491  * @constructor
32492  * Create a new MasonryBrick
32493  * @param {Object} config The config object
32494  */
32495
32496 Roo.bootstrap.MasonryBrick = function(config){
32497     
32498     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32499     
32500     Roo.bootstrap.MasonryBrick.register(this);
32501     
32502     this.addEvents({
32503         // raw events
32504         /**
32505          * @event click
32506          * When a MasonryBrick is clcik
32507          * @param {Roo.bootstrap.MasonryBrick} this
32508          * @param {Roo.EventObject} e
32509          */
32510         "click" : true
32511     });
32512 };
32513
32514 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32515     
32516     /**
32517      * @cfg {String} title
32518      */   
32519     title : '',
32520     /**
32521      * @cfg {String} html
32522      */   
32523     html : '',
32524     /**
32525      * @cfg {String} bgimage
32526      */   
32527     bgimage : '',
32528     /**
32529      * @cfg {String} videourl
32530      */   
32531     videourl : '',
32532     /**
32533      * @cfg {String} cls
32534      */   
32535     cls : '',
32536     /**
32537      * @cfg {String} href
32538      */   
32539     href : '',
32540     /**
32541      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32542      */   
32543     size : 'xs',
32544     
32545     /**
32546      * @cfg {String} placetitle (center|bottom)
32547      */   
32548     placetitle : '',
32549     
32550     /**
32551      * @cfg {Boolean} isFitContainer defalut true
32552      */   
32553     isFitContainer : true, 
32554     
32555     /**
32556      * @cfg {Boolean} preventDefault defalut false
32557      */   
32558     preventDefault : false, 
32559     
32560     /**
32561      * @cfg {Boolean} inverse defalut false
32562      */   
32563     maskInverse : false, 
32564     
32565     getAutoCreate : function()
32566     {
32567         if(!this.isFitContainer){
32568             return this.getSplitAutoCreate();
32569         }
32570         
32571         var cls = 'masonry-brick masonry-brick-full';
32572         
32573         if(this.href.length){
32574             cls += ' masonry-brick-link';
32575         }
32576         
32577         if(this.bgimage.length){
32578             cls += ' masonry-brick-image';
32579         }
32580         
32581         if(this.maskInverse){
32582             cls += ' mask-inverse';
32583         }
32584         
32585         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32586             cls += ' enable-mask';
32587         }
32588         
32589         if(this.size){
32590             cls += ' masonry-' + this.size + '-brick';
32591         }
32592         
32593         if(this.placetitle.length){
32594             
32595             switch (this.placetitle) {
32596                 case 'center' :
32597                     cls += ' masonry-center-title';
32598                     break;
32599                 case 'bottom' :
32600                     cls += ' masonry-bottom-title';
32601                     break;
32602                 default:
32603                     break;
32604             }
32605             
32606         } else {
32607             if(!this.html.length && !this.bgimage.length){
32608                 cls += ' masonry-center-title';
32609             }
32610
32611             if(!this.html.length && this.bgimage.length){
32612                 cls += ' masonry-bottom-title';
32613             }
32614         }
32615         
32616         if(this.cls){
32617             cls += ' ' + this.cls;
32618         }
32619         
32620         var cfg = {
32621             tag: (this.href.length) ? 'a' : 'div',
32622             cls: cls,
32623             cn: [
32624                 {
32625                     tag: 'div',
32626                     cls: 'masonry-brick-mask'
32627                 },
32628                 {
32629                     tag: 'div',
32630                     cls: 'masonry-brick-paragraph',
32631                     cn: []
32632                 }
32633             ]
32634         };
32635         
32636         if(this.href.length){
32637             cfg.href = this.href;
32638         }
32639         
32640         var cn = cfg.cn[1].cn;
32641         
32642         if(this.title.length){
32643             cn.push({
32644                 tag: 'h4',
32645                 cls: 'masonry-brick-title',
32646                 html: this.title
32647             });
32648         }
32649         
32650         if(this.html.length){
32651             cn.push({
32652                 tag: 'p',
32653                 cls: 'masonry-brick-text',
32654                 html: this.html
32655             });
32656         }
32657         
32658         if (!this.title.length && !this.html.length) {
32659             cfg.cn[1].cls += ' hide';
32660         }
32661         
32662         if(this.bgimage.length){
32663             cfg.cn.push({
32664                 tag: 'img',
32665                 cls: 'masonry-brick-image-view',
32666                 src: this.bgimage
32667             });
32668         }
32669         
32670         if(this.videourl.length){
32671             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32672             // youtube support only?
32673             cfg.cn.push({
32674                 tag: 'iframe',
32675                 cls: 'masonry-brick-image-view',
32676                 src: vurl,
32677                 frameborder : 0,
32678                 allowfullscreen : true
32679             });
32680         }
32681         
32682         return cfg;
32683         
32684     },
32685     
32686     getSplitAutoCreate : function()
32687     {
32688         var cls = 'masonry-brick masonry-brick-split';
32689         
32690         if(this.href.length){
32691             cls += ' masonry-brick-link';
32692         }
32693         
32694         if(this.bgimage.length){
32695             cls += ' masonry-brick-image';
32696         }
32697         
32698         if(this.size){
32699             cls += ' masonry-' + this.size + '-brick';
32700         }
32701         
32702         switch (this.placetitle) {
32703             case 'center' :
32704                 cls += ' masonry-center-title';
32705                 break;
32706             case 'bottom' :
32707                 cls += ' masonry-bottom-title';
32708                 break;
32709             default:
32710                 if(!this.bgimage.length){
32711                     cls += ' masonry-center-title';
32712                 }
32713
32714                 if(this.bgimage.length){
32715                     cls += ' masonry-bottom-title';
32716                 }
32717                 break;
32718         }
32719         
32720         if(this.cls){
32721             cls += ' ' + this.cls;
32722         }
32723         
32724         var cfg = {
32725             tag: (this.href.length) ? 'a' : 'div',
32726             cls: cls,
32727             cn: [
32728                 {
32729                     tag: 'div',
32730                     cls: 'masonry-brick-split-head',
32731                     cn: [
32732                         {
32733                             tag: 'div',
32734                             cls: 'masonry-brick-paragraph',
32735                             cn: []
32736                         }
32737                     ]
32738                 },
32739                 {
32740                     tag: 'div',
32741                     cls: 'masonry-brick-split-body',
32742                     cn: []
32743                 }
32744             ]
32745         };
32746         
32747         if(this.href.length){
32748             cfg.href = this.href;
32749         }
32750         
32751         if(this.title.length){
32752             cfg.cn[0].cn[0].cn.push({
32753                 tag: 'h4',
32754                 cls: 'masonry-brick-title',
32755                 html: this.title
32756             });
32757         }
32758         
32759         if(this.html.length){
32760             cfg.cn[1].cn.push({
32761                 tag: 'p',
32762                 cls: 'masonry-brick-text',
32763                 html: this.html
32764             });
32765         }
32766
32767         if(this.bgimage.length){
32768             cfg.cn[0].cn.push({
32769                 tag: 'img',
32770                 cls: 'masonry-brick-image-view',
32771                 src: this.bgimage
32772             });
32773         }
32774         
32775         if(this.videourl.length){
32776             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32777             // youtube support only?
32778             cfg.cn[0].cn.cn.push({
32779                 tag: 'iframe',
32780                 cls: 'masonry-brick-image-view',
32781                 src: vurl,
32782                 frameborder : 0,
32783                 allowfullscreen : true
32784             });
32785         }
32786         
32787         return cfg;
32788     },
32789     
32790     initEvents: function() 
32791     {
32792         switch (this.size) {
32793             case 'xs' :
32794                 this.x = 1;
32795                 this.y = 1;
32796                 break;
32797             case 'sm' :
32798                 this.x = 2;
32799                 this.y = 2;
32800                 break;
32801             case 'md' :
32802             case 'md-left' :
32803             case 'md-right' :
32804                 this.x = 3;
32805                 this.y = 3;
32806                 break;
32807             case 'tall' :
32808                 this.x = 2;
32809                 this.y = 3;
32810                 break;
32811             case 'wide' :
32812                 this.x = 3;
32813                 this.y = 2;
32814                 break;
32815             case 'wide-thin' :
32816                 this.x = 3;
32817                 this.y = 1;
32818                 break;
32819                         
32820             default :
32821                 break;
32822         }
32823         
32824         if(Roo.isTouch){
32825             this.el.on('touchstart', this.onTouchStart, this);
32826             this.el.on('touchmove', this.onTouchMove, this);
32827             this.el.on('touchend', this.onTouchEnd, this);
32828             this.el.on('contextmenu', this.onContextMenu, this);
32829         } else {
32830             this.el.on('mouseenter'  ,this.enter, this);
32831             this.el.on('mouseleave', this.leave, this);
32832             this.el.on('click', this.onClick, this);
32833         }
32834         
32835         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32836             this.parent().bricks.push(this);   
32837         }
32838         
32839     },
32840     
32841     onClick: function(e, el)
32842     {
32843         var time = this.endTimer - this.startTimer;
32844         // Roo.log(e.preventDefault());
32845         if(Roo.isTouch){
32846             if(time > 1000){
32847                 e.preventDefault();
32848                 return;
32849             }
32850         }
32851         
32852         if(!this.preventDefault){
32853             return;
32854         }
32855         
32856         e.preventDefault();
32857         
32858         if (this.activeClass != '') {
32859             this.selectBrick();
32860         }
32861         
32862         this.fireEvent('click', this, e);
32863     },
32864     
32865     enter: function(e, el)
32866     {
32867         e.preventDefault();
32868         
32869         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32870             return;
32871         }
32872         
32873         if(this.bgimage.length && this.html.length){
32874             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32875         }
32876     },
32877     
32878     leave: function(e, el)
32879     {
32880         e.preventDefault();
32881         
32882         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32883             return;
32884         }
32885         
32886         if(this.bgimage.length && this.html.length){
32887             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32888         }
32889     },
32890     
32891     onTouchStart: function(e, el)
32892     {
32893 //        e.preventDefault();
32894         
32895         this.touchmoved = false;
32896         
32897         if(!this.isFitContainer){
32898             return;
32899         }
32900         
32901         if(!this.bgimage.length || !this.html.length){
32902             return;
32903         }
32904         
32905         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32906         
32907         this.timer = new Date().getTime();
32908         
32909     },
32910     
32911     onTouchMove: function(e, el)
32912     {
32913         this.touchmoved = true;
32914     },
32915     
32916     onContextMenu : function(e,el)
32917     {
32918         e.preventDefault();
32919         e.stopPropagation();
32920         return false;
32921     },
32922     
32923     onTouchEnd: function(e, el)
32924     {
32925 //        e.preventDefault();
32926         
32927         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32928         
32929             this.leave(e,el);
32930             
32931             return;
32932         }
32933         
32934         if(!this.bgimage.length || !this.html.length){
32935             
32936             if(this.href.length){
32937                 window.location.href = this.href;
32938             }
32939             
32940             return;
32941         }
32942         
32943         if(!this.isFitContainer){
32944             return;
32945         }
32946         
32947         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32948         
32949         window.location.href = this.href;
32950     },
32951     
32952     //selection on single brick only
32953     selectBrick : function() {
32954         
32955         if (!this.parentId) {
32956             return;
32957         }
32958         
32959         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32960         var index = m.selectedBrick.indexOf(this.id);
32961         
32962         if ( index > -1) {
32963             m.selectedBrick.splice(index,1);
32964             this.el.removeClass(this.activeClass);
32965             return;
32966         }
32967         
32968         for(var i = 0; i < m.selectedBrick.length; i++) {
32969             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32970             b.el.removeClass(b.activeClass);
32971         }
32972         
32973         m.selectedBrick = [];
32974         
32975         m.selectedBrick.push(this.id);
32976         this.el.addClass(this.activeClass);
32977         return;
32978     },
32979     
32980     isSelected : function(){
32981         return this.el.hasClass(this.activeClass);
32982         
32983     }
32984 });
32985
32986 Roo.apply(Roo.bootstrap.MasonryBrick, {
32987     
32988     //groups: {},
32989     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32990      /**
32991     * register a Masonry Brick
32992     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32993     */
32994     
32995     register : function(brick)
32996     {
32997         //this.groups[brick.id] = brick;
32998         this.groups.add(brick.id, brick);
32999     },
33000     /**
33001     * fetch a  masonry brick based on the masonry brick ID
33002     * @param {string} the masonry brick to add
33003     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33004     */
33005     
33006     get: function(brick_id) 
33007     {
33008         // if (typeof(this.groups[brick_id]) == 'undefined') {
33009         //     return false;
33010         // }
33011         // return this.groups[brick_id] ;
33012         
33013         if(this.groups.key(brick_id)) {
33014             return this.groups.key(brick_id);
33015         }
33016         
33017         return false;
33018     }
33019     
33020     
33021     
33022 });
33023
33024  /*
33025  * - LGPL
33026  *
33027  * element
33028  * 
33029  */
33030
33031 /**
33032  * @class Roo.bootstrap.Brick
33033  * @extends Roo.bootstrap.Component
33034  * Bootstrap Brick class
33035  * 
33036  * @constructor
33037  * Create a new Brick
33038  * @param {Object} config The config object
33039  */
33040
33041 Roo.bootstrap.Brick = function(config){
33042     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33043     
33044     this.addEvents({
33045         // raw events
33046         /**
33047          * @event click
33048          * When a Brick is click
33049          * @param {Roo.bootstrap.Brick} this
33050          * @param {Roo.EventObject} e
33051          */
33052         "click" : true
33053     });
33054 };
33055
33056 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33057     
33058     /**
33059      * @cfg {String} title
33060      */   
33061     title : '',
33062     /**
33063      * @cfg {String} html
33064      */   
33065     html : '',
33066     /**
33067      * @cfg {String} bgimage
33068      */   
33069     bgimage : '',
33070     /**
33071      * @cfg {String} cls
33072      */   
33073     cls : '',
33074     /**
33075      * @cfg {String} href
33076      */   
33077     href : '',
33078     /**
33079      * @cfg {String} video
33080      */   
33081     video : '',
33082     /**
33083      * @cfg {Boolean} square
33084      */   
33085     square : true,
33086     
33087     getAutoCreate : function()
33088     {
33089         var cls = 'roo-brick';
33090         
33091         if(this.href.length){
33092             cls += ' roo-brick-link';
33093         }
33094         
33095         if(this.bgimage.length){
33096             cls += ' roo-brick-image';
33097         }
33098         
33099         if(!this.html.length && !this.bgimage.length){
33100             cls += ' roo-brick-center-title';
33101         }
33102         
33103         if(!this.html.length && this.bgimage.length){
33104             cls += ' roo-brick-bottom-title';
33105         }
33106         
33107         if(this.cls){
33108             cls += ' ' + this.cls;
33109         }
33110         
33111         var cfg = {
33112             tag: (this.href.length) ? 'a' : 'div',
33113             cls: cls,
33114             cn: [
33115                 {
33116                     tag: 'div',
33117                     cls: 'roo-brick-paragraph',
33118                     cn: []
33119                 }
33120             ]
33121         };
33122         
33123         if(this.href.length){
33124             cfg.href = this.href;
33125         }
33126         
33127         var cn = cfg.cn[0].cn;
33128         
33129         if(this.title.length){
33130             cn.push({
33131                 tag: 'h4',
33132                 cls: 'roo-brick-title',
33133                 html: this.title
33134             });
33135         }
33136         
33137         if(this.html.length){
33138             cn.push({
33139                 tag: 'p',
33140                 cls: 'roo-brick-text',
33141                 html: this.html
33142             });
33143         } else {
33144             cn.cls += ' hide';
33145         }
33146         
33147         if(this.bgimage.length){
33148             cfg.cn.push({
33149                 tag: 'img',
33150                 cls: 'roo-brick-image-view',
33151                 src: this.bgimage
33152             });
33153         }
33154         
33155         return cfg;
33156     },
33157     
33158     initEvents: function() 
33159     {
33160         if(this.title.length || this.html.length){
33161             this.el.on('mouseenter'  ,this.enter, this);
33162             this.el.on('mouseleave', this.leave, this);
33163         }
33164         
33165         Roo.EventManager.onWindowResize(this.resize, this); 
33166         
33167         if(this.bgimage.length){
33168             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33169             this.imageEl.on('load', this.onImageLoad, this);
33170             return;
33171         }
33172         
33173         this.resize();
33174     },
33175     
33176     onImageLoad : function()
33177     {
33178         this.resize();
33179     },
33180     
33181     resize : function()
33182     {
33183         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33184         
33185         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33186         
33187         if(this.bgimage.length){
33188             var image = this.el.select('.roo-brick-image-view', true).first();
33189             
33190             image.setWidth(paragraph.getWidth());
33191             
33192             if(this.square){
33193                 image.setHeight(paragraph.getWidth());
33194             }
33195             
33196             this.el.setHeight(image.getHeight());
33197             paragraph.setHeight(image.getHeight());
33198             
33199         }
33200         
33201     },
33202     
33203     enter: function(e, el)
33204     {
33205         e.preventDefault();
33206         
33207         if(this.bgimage.length){
33208             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33209             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33210         }
33211     },
33212     
33213     leave: function(e, el)
33214     {
33215         e.preventDefault();
33216         
33217         if(this.bgimage.length){
33218             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33219             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33220         }
33221     }
33222     
33223 });
33224
33225  
33226
33227  /*
33228  * - LGPL
33229  *
33230  * Number field 
33231  */
33232
33233 /**
33234  * @class Roo.bootstrap.NumberField
33235  * @extends Roo.bootstrap.Input
33236  * Bootstrap NumberField class
33237  * 
33238  * 
33239  * 
33240  * 
33241  * @constructor
33242  * Create a new NumberField
33243  * @param {Object} config The config object
33244  */
33245
33246 Roo.bootstrap.NumberField = function(config){
33247     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33248 };
33249
33250 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33251     
33252     /**
33253      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33254      */
33255     allowDecimals : true,
33256     /**
33257      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33258      */
33259     decimalSeparator : ".",
33260     /**
33261      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33262      */
33263     decimalPrecision : 2,
33264     /**
33265      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33266      */
33267     allowNegative : true,
33268     
33269     /**
33270      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33271      */
33272     allowZero: true,
33273     /**
33274      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33275      */
33276     minValue : Number.NEGATIVE_INFINITY,
33277     /**
33278      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33279      */
33280     maxValue : Number.MAX_VALUE,
33281     /**
33282      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33283      */
33284     minText : "The minimum value for this field is {0}",
33285     /**
33286      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33287      */
33288     maxText : "The maximum value for this field is {0}",
33289     /**
33290      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33291      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33292      */
33293     nanText : "{0} is not a valid number",
33294     /**
33295      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33296      */
33297     thousandsDelimiter : false,
33298     /**
33299      * @cfg {String} valueAlign alignment of value
33300      */
33301     valueAlign : "left",
33302
33303     getAutoCreate : function()
33304     {
33305         var hiddenInput = {
33306             tag: 'input',
33307             type: 'hidden',
33308             id: Roo.id(),
33309             cls: 'hidden-number-input'
33310         };
33311         
33312         if (this.name) {
33313             hiddenInput.name = this.name;
33314         }
33315         
33316         this.name = '';
33317         
33318         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33319         
33320         this.name = hiddenInput.name;
33321         
33322         if(cfg.cn.length > 0) {
33323             cfg.cn.push(hiddenInput);
33324         }
33325         
33326         return cfg;
33327     },
33328
33329     // private
33330     initEvents : function()
33331     {   
33332         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33333         
33334         var allowed = "0123456789";
33335         
33336         if(this.allowDecimals){
33337             allowed += this.decimalSeparator;
33338         }
33339         
33340         if(this.allowNegative){
33341             allowed += "-";
33342         }
33343         
33344         if(this.thousandsDelimiter) {
33345             allowed += ",";
33346         }
33347         
33348         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33349         
33350         var keyPress = function(e){
33351             
33352             var k = e.getKey();
33353             
33354             var c = e.getCharCode();
33355             
33356             if(
33357                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33358                     allowed.indexOf(String.fromCharCode(c)) === -1
33359             ){
33360                 e.stopEvent();
33361                 return;
33362             }
33363             
33364             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33365                 return;
33366             }
33367             
33368             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33369                 e.stopEvent();
33370             }
33371         };
33372         
33373         this.el.on("keypress", keyPress, this);
33374     },
33375     
33376     validateValue : function(value)
33377     {
33378         
33379         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33380             return false;
33381         }
33382         
33383         var num = this.parseValue(value);
33384         
33385         if(isNaN(num)){
33386             this.markInvalid(String.format(this.nanText, value));
33387             return false;
33388         }
33389         
33390         if(num < this.minValue){
33391             this.markInvalid(String.format(this.minText, this.minValue));
33392             return false;
33393         }
33394         
33395         if(num > this.maxValue){
33396             this.markInvalid(String.format(this.maxText, this.maxValue));
33397             return false;
33398         }
33399         
33400         return true;
33401     },
33402
33403     getValue : function()
33404     {
33405         var v = this.hiddenEl().getValue();
33406         
33407         return this.fixPrecision(this.parseValue(v));
33408     },
33409
33410     parseValue : function(value)
33411     {
33412         if(this.thousandsDelimiter) {
33413             value += "";
33414             r = new RegExp(",", "g");
33415             value = value.replace(r, "");
33416         }
33417         
33418         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33419         return isNaN(value) ? '' : value;
33420     },
33421
33422     fixPrecision : function(value)
33423     {
33424         if(this.thousandsDelimiter) {
33425             value += "";
33426             r = new RegExp(",", "g");
33427             value = value.replace(r, "");
33428         }
33429         
33430         var nan = isNaN(value);
33431         
33432         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33433             return nan ? '' : value;
33434         }
33435         return parseFloat(value).toFixed(this.decimalPrecision);
33436     },
33437
33438     setValue : function(v)
33439     {
33440         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33441         
33442         this.value = v;
33443         
33444         if(this.rendered){
33445             
33446             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33447             
33448             this.inputEl().dom.value = (v == '') ? '' :
33449                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33450             
33451             if(!this.allowZero && v === '0') {
33452                 this.hiddenEl().dom.value = '';
33453                 this.inputEl().dom.value = '';
33454             }
33455             
33456             this.validate();
33457         }
33458     },
33459
33460     decimalPrecisionFcn : function(v)
33461     {
33462         return Math.floor(v);
33463     },
33464
33465     beforeBlur : function()
33466     {
33467         var v = this.parseValue(this.getRawValue());
33468         
33469         if(v || v === 0 || v === ''){
33470             this.setValue(v);
33471         }
33472     },
33473     
33474     hiddenEl : function()
33475     {
33476         return this.el.select('input.hidden-number-input',true).first();
33477     }
33478     
33479 });
33480
33481  
33482
33483 /*
33484 * Licence: LGPL
33485 */
33486
33487 /**
33488  * @class Roo.bootstrap.DocumentSlider
33489  * @extends Roo.bootstrap.Component
33490  * Bootstrap DocumentSlider class
33491  * 
33492  * @constructor
33493  * Create a new DocumentViewer
33494  * @param {Object} config The config object
33495  */
33496
33497 Roo.bootstrap.DocumentSlider = function(config){
33498     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33499     
33500     this.files = [];
33501     
33502     this.addEvents({
33503         /**
33504          * @event initial
33505          * Fire after initEvent
33506          * @param {Roo.bootstrap.DocumentSlider} this
33507          */
33508         "initial" : true,
33509         /**
33510          * @event update
33511          * Fire after update
33512          * @param {Roo.bootstrap.DocumentSlider} this
33513          */
33514         "update" : true,
33515         /**
33516          * @event click
33517          * Fire after click
33518          * @param {Roo.bootstrap.DocumentSlider} this
33519          */
33520         "click" : true
33521     });
33522 };
33523
33524 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33525     
33526     files : false,
33527     
33528     indicator : 0,
33529     
33530     getAutoCreate : function()
33531     {
33532         var cfg = {
33533             tag : 'div',
33534             cls : 'roo-document-slider',
33535             cn : [
33536                 {
33537                     tag : 'div',
33538                     cls : 'roo-document-slider-header',
33539                     cn : [
33540                         {
33541                             tag : 'div',
33542                             cls : 'roo-document-slider-header-title'
33543                         }
33544                     ]
33545                 },
33546                 {
33547                     tag : 'div',
33548                     cls : 'roo-document-slider-body',
33549                     cn : [
33550                         {
33551                             tag : 'div',
33552                             cls : 'roo-document-slider-prev',
33553                             cn : [
33554                                 {
33555                                     tag : 'i',
33556                                     cls : 'fa fa-chevron-left'
33557                                 }
33558                             ]
33559                         },
33560                         {
33561                             tag : 'div',
33562                             cls : 'roo-document-slider-thumb',
33563                             cn : [
33564                                 {
33565                                     tag : 'img',
33566                                     cls : 'roo-document-slider-image'
33567                                 }
33568                             ]
33569                         },
33570                         {
33571                             tag : 'div',
33572                             cls : 'roo-document-slider-next',
33573                             cn : [
33574                                 {
33575                                     tag : 'i',
33576                                     cls : 'fa fa-chevron-right'
33577                                 }
33578                             ]
33579                         }
33580                     ]
33581                 }
33582             ]
33583         };
33584         
33585         return cfg;
33586     },
33587     
33588     initEvents : function()
33589     {
33590         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33591         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33592         
33593         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33594         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33595         
33596         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33597         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33598         
33599         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33600         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33601         
33602         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33603         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33604         
33605         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33606         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33607         
33608         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33609         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33610         
33611         this.thumbEl.on('click', this.onClick, this);
33612         
33613         this.prevIndicator.on('click', this.prev, this);
33614         
33615         this.nextIndicator.on('click', this.next, this);
33616         
33617     },
33618     
33619     initial : function()
33620     {
33621         if(this.files.length){
33622             this.indicator = 1;
33623             this.update()
33624         }
33625         
33626         this.fireEvent('initial', this);
33627     },
33628     
33629     update : function()
33630     {
33631         this.imageEl.attr('src', this.files[this.indicator - 1]);
33632         
33633         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33634         
33635         this.prevIndicator.show();
33636         
33637         if(this.indicator == 1){
33638             this.prevIndicator.hide();
33639         }
33640         
33641         this.nextIndicator.show();
33642         
33643         if(this.indicator == this.files.length){
33644             this.nextIndicator.hide();
33645         }
33646         
33647         this.thumbEl.scrollTo('top');
33648         
33649         this.fireEvent('update', this);
33650     },
33651     
33652     onClick : function(e)
33653     {
33654         e.preventDefault();
33655         
33656         this.fireEvent('click', this);
33657     },
33658     
33659     prev : function(e)
33660     {
33661         e.preventDefault();
33662         
33663         this.indicator = Math.max(1, this.indicator - 1);
33664         
33665         this.update();
33666     },
33667     
33668     next : function(e)
33669     {
33670         e.preventDefault();
33671         
33672         this.indicator = Math.min(this.files.length, this.indicator + 1);
33673         
33674         this.update();
33675     }
33676 });
33677 /*
33678  * - LGPL
33679  *
33680  * RadioSet
33681  *
33682  *
33683  */
33684
33685 /**
33686  * @class Roo.bootstrap.RadioSet
33687  * @extends Roo.bootstrap.Input
33688  * Bootstrap RadioSet class
33689  * @cfg {String} indicatorpos (left|right) default left
33690  * @cfg {Boolean} inline (true|false) inline the element (default true)
33691  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33692  * @constructor
33693  * Create a new RadioSet
33694  * @param {Object} config The config object
33695  */
33696
33697 Roo.bootstrap.RadioSet = function(config){
33698     
33699     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33700     
33701     this.radioes = [];
33702     
33703     Roo.bootstrap.RadioSet.register(this);
33704     
33705     this.addEvents({
33706         /**
33707         * @event check
33708         * Fires when the element is checked or unchecked.
33709         * @param {Roo.bootstrap.RadioSet} this This radio
33710         * @param {Roo.bootstrap.Radio} item The checked item
33711         */
33712        check : true,
33713        /**
33714         * @event click
33715         * Fires when the element is click.
33716         * @param {Roo.bootstrap.RadioSet} this This radio set
33717         * @param {Roo.bootstrap.Radio} item The checked item
33718         * @param {Roo.EventObject} e The event object
33719         */
33720        click : true
33721     });
33722     
33723 };
33724
33725 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33726
33727     radioes : false,
33728     
33729     inline : true,
33730     
33731     weight : '',
33732     
33733     indicatorpos : 'left',
33734     
33735     getAutoCreate : function()
33736     {
33737         var label = {
33738             tag : 'label',
33739             cls : 'roo-radio-set-label',
33740             cn : [
33741                 {
33742                     tag : 'span',
33743                     html : this.fieldLabel
33744                 }
33745             ]
33746         };
33747         
33748         if(this.indicatorpos == 'left'){
33749             label.cn.unshift({
33750                 tag : 'i',
33751                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33752                 tooltip : 'This field is required'
33753             });
33754         } else {
33755             label.cn.push({
33756                 tag : 'i',
33757                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33758                 tooltip : 'This field is required'
33759             });
33760         }
33761         
33762         var items = {
33763             tag : 'div',
33764             cls : 'roo-radio-set-items'
33765         };
33766         
33767         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33768         
33769         if (align === 'left' && this.fieldLabel.length) {
33770             
33771             items = {
33772                 cls : "roo-radio-set-right", 
33773                 cn: [
33774                     items
33775                 ]
33776             };
33777             
33778             if(this.labelWidth > 12){
33779                 label.style = "width: " + this.labelWidth + 'px';
33780             }
33781             
33782             if(this.labelWidth < 13 && this.labelmd == 0){
33783                 this.labelmd = this.labelWidth;
33784             }
33785             
33786             if(this.labellg > 0){
33787                 label.cls += ' col-lg-' + this.labellg;
33788                 items.cls += ' col-lg-' + (12 - this.labellg);
33789             }
33790             
33791             if(this.labelmd > 0){
33792                 label.cls += ' col-md-' + this.labelmd;
33793                 items.cls += ' col-md-' + (12 - this.labelmd);
33794             }
33795             
33796             if(this.labelsm > 0){
33797                 label.cls += ' col-sm-' + this.labelsm;
33798                 items.cls += ' col-sm-' + (12 - this.labelsm);
33799             }
33800             
33801             if(this.labelxs > 0){
33802                 label.cls += ' col-xs-' + this.labelxs;
33803                 items.cls += ' col-xs-' + (12 - this.labelxs);
33804             }
33805         }
33806         
33807         var cfg = {
33808             tag : 'div',
33809             cls : 'roo-radio-set',
33810             cn : [
33811                 {
33812                     tag : 'input',
33813                     cls : 'roo-radio-set-input',
33814                     type : 'hidden',
33815                     name : this.name,
33816                     value : this.value ? this.value :  ''
33817                 },
33818                 label,
33819                 items
33820             ]
33821         };
33822         
33823         if(this.weight.length){
33824             cfg.cls += ' roo-radio-' + this.weight;
33825         }
33826         
33827         if(this.inline) {
33828             cfg.cls += ' roo-radio-set-inline';
33829         }
33830         
33831         var settings=this;
33832         ['xs','sm','md','lg'].map(function(size){
33833             if (settings[size]) {
33834                 cfg.cls += ' col-' + size + '-' + settings[size];
33835             }
33836         });
33837         
33838         return cfg;
33839         
33840     },
33841
33842     initEvents : function()
33843     {
33844         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33845         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33846         
33847         if(!this.fieldLabel.length){
33848             this.labelEl.hide();
33849         }
33850         
33851         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33852         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33853         
33854         this.indicator = this.indicatorEl();
33855         
33856         if(this.indicator){
33857             this.indicator.addClass('invisible');
33858         }
33859         
33860         this.originalValue = this.getValue();
33861         
33862     },
33863     
33864     inputEl: function ()
33865     {
33866         return this.el.select('.roo-radio-set-input', true).first();
33867     },
33868     
33869     getChildContainer : function()
33870     {
33871         return this.itemsEl;
33872     },
33873     
33874     register : function(item)
33875     {
33876         this.radioes.push(item);
33877         
33878     },
33879     
33880     validate : function()
33881     {   
33882         if(this.getVisibilityEl().hasClass('hidden')){
33883             return true;
33884         }
33885         
33886         var valid = false;
33887         
33888         Roo.each(this.radioes, function(i){
33889             if(!i.checked){
33890                 return;
33891             }
33892             
33893             valid = true;
33894             return false;
33895         });
33896         
33897         if(this.allowBlank) {
33898             return true;
33899         }
33900         
33901         if(this.disabled || valid){
33902             this.markValid();
33903             return true;
33904         }
33905         
33906         this.markInvalid();
33907         return false;
33908         
33909     },
33910     
33911     markValid : function()
33912     {
33913         if(this.labelEl.isVisible(true)){
33914             this.indicatorEl().removeClass('visible');
33915             this.indicatorEl().addClass('invisible');
33916         }
33917         
33918         this.el.removeClass([this.invalidClass, this.validClass]);
33919         this.el.addClass(this.validClass);
33920         
33921         this.fireEvent('valid', this);
33922     },
33923     
33924     markInvalid : function(msg)
33925     {
33926         if(this.allowBlank || this.disabled){
33927             return;
33928         }
33929         
33930         if(this.labelEl.isVisible(true)){
33931             this.indicatorEl().removeClass('invisible');
33932             this.indicatorEl().addClass('visible');
33933         }
33934         
33935         this.el.removeClass([this.invalidClass, this.validClass]);
33936         this.el.addClass(this.invalidClass);
33937         
33938         this.fireEvent('invalid', this, msg);
33939         
33940     },
33941     
33942     setValue : function(v, suppressEvent)
33943     {   
33944         if(this.value === v){
33945             return;
33946         }
33947         
33948         this.value = v;
33949         
33950         if(this.rendered){
33951             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33952         }
33953         
33954         Roo.each(this.radioes, function(i){
33955             i.checked = false;
33956             i.el.removeClass('checked');
33957         });
33958         
33959         Roo.each(this.radioes, function(i){
33960             
33961             if(i.value === v || i.value.toString() === v.toString()){
33962                 i.checked = true;
33963                 i.el.addClass('checked');
33964                 
33965                 if(suppressEvent !== true){
33966                     this.fireEvent('check', this, i);
33967                 }
33968                 
33969                 return false;
33970             }
33971             
33972         }, this);
33973         
33974         this.validate();
33975     },
33976     
33977     clearInvalid : function(){
33978         
33979         if(!this.el || this.preventMark){
33980             return;
33981         }
33982         
33983         this.el.removeClass([this.invalidClass]);
33984         
33985         this.fireEvent('valid', this);
33986     }
33987     
33988 });
33989
33990 Roo.apply(Roo.bootstrap.RadioSet, {
33991     
33992     groups: {},
33993     
33994     register : function(set)
33995     {
33996         this.groups[set.name] = set;
33997     },
33998     
33999     get: function(name) 
34000     {
34001         if (typeof(this.groups[name]) == 'undefined') {
34002             return false;
34003         }
34004         
34005         return this.groups[name] ;
34006     }
34007     
34008 });
34009 /*
34010  * Based on:
34011  * Ext JS Library 1.1.1
34012  * Copyright(c) 2006-2007, Ext JS, LLC.
34013  *
34014  * Originally Released Under LGPL - original licence link has changed is not relivant.
34015  *
34016  * Fork - LGPL
34017  * <script type="text/javascript">
34018  */
34019
34020
34021 /**
34022  * @class Roo.bootstrap.SplitBar
34023  * @extends Roo.util.Observable
34024  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34025  * <br><br>
34026  * Usage:
34027  * <pre><code>
34028 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34029                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34030 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34031 split.minSize = 100;
34032 split.maxSize = 600;
34033 split.animate = true;
34034 split.on('moved', splitterMoved);
34035 </code></pre>
34036  * @constructor
34037  * Create a new SplitBar
34038  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34039  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34040  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34041  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34042                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34043                         position of the SplitBar).
34044  */
34045 Roo.bootstrap.SplitBar = function(cfg){
34046     
34047     /** @private */
34048     
34049     //{
34050     //  dragElement : elm
34051     //  resizingElement: el,
34052         // optional..
34053     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34054     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34055         // existingProxy ???
34056     //}
34057     
34058     this.el = Roo.get(cfg.dragElement, true);
34059     this.el.dom.unselectable = "on";
34060     /** @private */
34061     this.resizingEl = Roo.get(cfg.resizingElement, true);
34062
34063     /**
34064      * @private
34065      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34066      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34067      * @type Number
34068      */
34069     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34070     
34071     /**
34072      * The minimum size of the resizing element. (Defaults to 0)
34073      * @type Number
34074      */
34075     this.minSize = 0;
34076     
34077     /**
34078      * The maximum size of the resizing element. (Defaults to 2000)
34079      * @type Number
34080      */
34081     this.maxSize = 2000;
34082     
34083     /**
34084      * Whether to animate the transition to the new size
34085      * @type Boolean
34086      */
34087     this.animate = false;
34088     
34089     /**
34090      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34091      * @type Boolean
34092      */
34093     this.useShim = false;
34094     
34095     /** @private */
34096     this.shim = null;
34097     
34098     if(!cfg.existingProxy){
34099         /** @private */
34100         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34101     }else{
34102         this.proxy = Roo.get(cfg.existingProxy).dom;
34103     }
34104     /** @private */
34105     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34106     
34107     /** @private */
34108     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34109     
34110     /** @private */
34111     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34112     
34113     /** @private */
34114     this.dragSpecs = {};
34115     
34116     /**
34117      * @private The adapter to use to positon and resize elements
34118      */
34119     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34120     this.adapter.init(this);
34121     
34122     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34123         /** @private */
34124         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34125         this.el.addClass("roo-splitbar-h");
34126     }else{
34127         /** @private */
34128         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34129         this.el.addClass("roo-splitbar-v");
34130     }
34131     
34132     this.addEvents({
34133         /**
34134          * @event resize
34135          * Fires when the splitter is moved (alias for {@link #event-moved})
34136          * @param {Roo.bootstrap.SplitBar} this
34137          * @param {Number} newSize the new width or height
34138          */
34139         "resize" : true,
34140         /**
34141          * @event moved
34142          * Fires when the splitter is moved
34143          * @param {Roo.bootstrap.SplitBar} this
34144          * @param {Number} newSize the new width or height
34145          */
34146         "moved" : true,
34147         /**
34148          * @event beforeresize
34149          * Fires before the splitter is dragged
34150          * @param {Roo.bootstrap.SplitBar} this
34151          */
34152         "beforeresize" : true,
34153
34154         "beforeapply" : true
34155     });
34156
34157     Roo.util.Observable.call(this);
34158 };
34159
34160 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34161     onStartProxyDrag : function(x, y){
34162         this.fireEvent("beforeresize", this);
34163         if(!this.overlay){
34164             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34165             o.unselectable();
34166             o.enableDisplayMode("block");
34167             // all splitbars share the same overlay
34168             Roo.bootstrap.SplitBar.prototype.overlay = o;
34169         }
34170         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34171         this.overlay.show();
34172         Roo.get(this.proxy).setDisplayed("block");
34173         var size = this.adapter.getElementSize(this);
34174         this.activeMinSize = this.getMinimumSize();;
34175         this.activeMaxSize = this.getMaximumSize();;
34176         var c1 = size - this.activeMinSize;
34177         var c2 = Math.max(this.activeMaxSize - size, 0);
34178         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34179             this.dd.resetConstraints();
34180             this.dd.setXConstraint(
34181                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34182                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34183             );
34184             this.dd.setYConstraint(0, 0);
34185         }else{
34186             this.dd.resetConstraints();
34187             this.dd.setXConstraint(0, 0);
34188             this.dd.setYConstraint(
34189                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34190                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34191             );
34192          }
34193         this.dragSpecs.startSize = size;
34194         this.dragSpecs.startPoint = [x, y];
34195         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34196     },
34197     
34198     /** 
34199      * @private Called after the drag operation by the DDProxy
34200      */
34201     onEndProxyDrag : function(e){
34202         Roo.get(this.proxy).setDisplayed(false);
34203         var endPoint = Roo.lib.Event.getXY(e);
34204         if(this.overlay){
34205             this.overlay.hide();
34206         }
34207         var newSize;
34208         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34209             newSize = this.dragSpecs.startSize + 
34210                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34211                     endPoint[0] - this.dragSpecs.startPoint[0] :
34212                     this.dragSpecs.startPoint[0] - endPoint[0]
34213                 );
34214         }else{
34215             newSize = this.dragSpecs.startSize + 
34216                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34217                     endPoint[1] - this.dragSpecs.startPoint[1] :
34218                     this.dragSpecs.startPoint[1] - endPoint[1]
34219                 );
34220         }
34221         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34222         if(newSize != this.dragSpecs.startSize){
34223             if(this.fireEvent('beforeapply', this, newSize) !== false){
34224                 this.adapter.setElementSize(this, newSize);
34225                 this.fireEvent("moved", this, newSize);
34226                 this.fireEvent("resize", this, newSize);
34227             }
34228         }
34229     },
34230     
34231     /**
34232      * Get the adapter this SplitBar uses
34233      * @return The adapter object
34234      */
34235     getAdapter : function(){
34236         return this.adapter;
34237     },
34238     
34239     /**
34240      * Set the adapter this SplitBar uses
34241      * @param {Object} adapter A SplitBar adapter object
34242      */
34243     setAdapter : function(adapter){
34244         this.adapter = adapter;
34245         this.adapter.init(this);
34246     },
34247     
34248     /**
34249      * Gets the minimum size for the resizing element
34250      * @return {Number} The minimum size
34251      */
34252     getMinimumSize : function(){
34253         return this.minSize;
34254     },
34255     
34256     /**
34257      * Sets the minimum size for the resizing element
34258      * @param {Number} minSize The minimum size
34259      */
34260     setMinimumSize : function(minSize){
34261         this.minSize = minSize;
34262     },
34263     
34264     /**
34265      * Gets the maximum size for the resizing element
34266      * @return {Number} The maximum size
34267      */
34268     getMaximumSize : function(){
34269         return this.maxSize;
34270     },
34271     
34272     /**
34273      * Sets the maximum size for the resizing element
34274      * @param {Number} maxSize The maximum size
34275      */
34276     setMaximumSize : function(maxSize){
34277         this.maxSize = maxSize;
34278     },
34279     
34280     /**
34281      * Sets the initialize size for the resizing element
34282      * @param {Number} size The initial size
34283      */
34284     setCurrentSize : function(size){
34285         var oldAnimate = this.animate;
34286         this.animate = false;
34287         this.adapter.setElementSize(this, size);
34288         this.animate = oldAnimate;
34289     },
34290     
34291     /**
34292      * Destroy this splitbar. 
34293      * @param {Boolean} removeEl True to remove the element
34294      */
34295     destroy : function(removeEl){
34296         if(this.shim){
34297             this.shim.remove();
34298         }
34299         this.dd.unreg();
34300         this.proxy.parentNode.removeChild(this.proxy);
34301         if(removeEl){
34302             this.el.remove();
34303         }
34304     }
34305 });
34306
34307 /**
34308  * @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.
34309  */
34310 Roo.bootstrap.SplitBar.createProxy = function(dir){
34311     var proxy = new Roo.Element(document.createElement("div"));
34312     proxy.unselectable();
34313     var cls = 'roo-splitbar-proxy';
34314     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34315     document.body.appendChild(proxy.dom);
34316     return proxy.dom;
34317 };
34318
34319 /** 
34320  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34321  * Default Adapter. It assumes the splitter and resizing element are not positioned
34322  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34323  */
34324 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34325 };
34326
34327 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34328     // do nothing for now
34329     init : function(s){
34330     
34331     },
34332     /**
34333      * Called before drag operations to get the current size of the resizing element. 
34334      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34335      */
34336      getElementSize : function(s){
34337         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34338             return s.resizingEl.getWidth();
34339         }else{
34340             return s.resizingEl.getHeight();
34341         }
34342     },
34343     
34344     /**
34345      * Called after drag operations to set the size of the resizing element.
34346      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34347      * @param {Number} newSize The new size to set
34348      * @param {Function} onComplete A function to be invoked when resizing is complete
34349      */
34350     setElementSize : function(s, newSize, onComplete){
34351         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34352             if(!s.animate){
34353                 s.resizingEl.setWidth(newSize);
34354                 if(onComplete){
34355                     onComplete(s, newSize);
34356                 }
34357             }else{
34358                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34359             }
34360         }else{
34361             
34362             if(!s.animate){
34363                 s.resizingEl.setHeight(newSize);
34364                 if(onComplete){
34365                     onComplete(s, newSize);
34366                 }
34367             }else{
34368                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34369             }
34370         }
34371     }
34372 };
34373
34374 /** 
34375  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34376  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34377  * Adapter that  moves the splitter element to align with the resized sizing element. 
34378  * Used with an absolute positioned SplitBar.
34379  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34380  * document.body, make sure you assign an id to the body element.
34381  */
34382 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34383     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34384     this.container = Roo.get(container);
34385 };
34386
34387 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34388     init : function(s){
34389         this.basic.init(s);
34390     },
34391     
34392     getElementSize : function(s){
34393         return this.basic.getElementSize(s);
34394     },
34395     
34396     setElementSize : function(s, newSize, onComplete){
34397         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34398     },
34399     
34400     moveSplitter : function(s){
34401         var yes = Roo.bootstrap.SplitBar;
34402         switch(s.placement){
34403             case yes.LEFT:
34404                 s.el.setX(s.resizingEl.getRight());
34405                 break;
34406             case yes.RIGHT:
34407                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34408                 break;
34409             case yes.TOP:
34410                 s.el.setY(s.resizingEl.getBottom());
34411                 break;
34412             case yes.BOTTOM:
34413                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34414                 break;
34415         }
34416     }
34417 };
34418
34419 /**
34420  * Orientation constant - Create a vertical SplitBar
34421  * @static
34422  * @type Number
34423  */
34424 Roo.bootstrap.SplitBar.VERTICAL = 1;
34425
34426 /**
34427  * Orientation constant - Create a horizontal SplitBar
34428  * @static
34429  * @type Number
34430  */
34431 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34432
34433 /**
34434  * Placement constant - The resizing element is to the left of the splitter element
34435  * @static
34436  * @type Number
34437  */
34438 Roo.bootstrap.SplitBar.LEFT = 1;
34439
34440 /**
34441  * Placement constant - The resizing element is to the right of the splitter element
34442  * @static
34443  * @type Number
34444  */
34445 Roo.bootstrap.SplitBar.RIGHT = 2;
34446
34447 /**
34448  * Placement constant - The resizing element is positioned above the splitter element
34449  * @static
34450  * @type Number
34451  */
34452 Roo.bootstrap.SplitBar.TOP = 3;
34453
34454 /**
34455  * Placement constant - The resizing element is positioned under splitter element
34456  * @static
34457  * @type Number
34458  */
34459 Roo.bootstrap.SplitBar.BOTTOM = 4;
34460 Roo.namespace("Roo.bootstrap.layout");/*
34461  * Based on:
34462  * Ext JS Library 1.1.1
34463  * Copyright(c) 2006-2007, Ext JS, LLC.
34464  *
34465  * Originally Released Under LGPL - original licence link has changed is not relivant.
34466  *
34467  * Fork - LGPL
34468  * <script type="text/javascript">
34469  */
34470
34471 /**
34472  * @class Roo.bootstrap.layout.Manager
34473  * @extends Roo.bootstrap.Component
34474  * Base class for layout managers.
34475  */
34476 Roo.bootstrap.layout.Manager = function(config)
34477 {
34478     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34479
34480
34481
34482
34483
34484     /** false to disable window resize monitoring @type Boolean */
34485     this.monitorWindowResize = true;
34486     this.regions = {};
34487     this.addEvents({
34488         /**
34489          * @event layout
34490          * Fires when a layout is performed.
34491          * @param {Roo.LayoutManager} this
34492          */
34493         "layout" : true,
34494         /**
34495          * @event regionresized
34496          * Fires when the user resizes a region.
34497          * @param {Roo.LayoutRegion} region The resized region
34498          * @param {Number} newSize The new size (width for east/west, height for north/south)
34499          */
34500         "regionresized" : true,
34501         /**
34502          * @event regioncollapsed
34503          * Fires when a region is collapsed.
34504          * @param {Roo.LayoutRegion} region The collapsed region
34505          */
34506         "regioncollapsed" : true,
34507         /**
34508          * @event regionexpanded
34509          * Fires when a region is expanded.
34510          * @param {Roo.LayoutRegion} region The expanded region
34511          */
34512         "regionexpanded" : true
34513     });
34514     this.updating = false;
34515
34516     if (config.el) {
34517         this.el = Roo.get(config.el);
34518         this.initEvents();
34519     }
34520
34521 };
34522
34523 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34524
34525
34526     regions : null,
34527
34528     monitorWindowResize : true,
34529
34530
34531     updating : false,
34532
34533
34534     onRender : function(ct, position)
34535     {
34536         if(!this.el){
34537             this.el = Roo.get(ct);
34538             this.initEvents();
34539         }
34540         //this.fireEvent('render',this);
34541     },
34542
34543
34544     initEvents: function()
34545     {
34546
34547
34548         // ie scrollbar fix
34549         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34550             document.body.scroll = "no";
34551         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34552             this.el.position('relative');
34553         }
34554         this.id = this.el.id;
34555         this.el.addClass("roo-layout-container");
34556         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34557         if(this.el.dom != document.body ) {
34558             this.el.on('resize', this.layout,this);
34559             this.el.on('show', this.layout,this);
34560         }
34561
34562     },
34563
34564     /**
34565      * Returns true if this layout is currently being updated
34566      * @return {Boolean}
34567      */
34568     isUpdating : function(){
34569         return this.updating;
34570     },
34571
34572     /**
34573      * Suspend the LayoutManager from doing auto-layouts while
34574      * making multiple add or remove calls
34575      */
34576     beginUpdate : function(){
34577         this.updating = true;
34578     },
34579
34580     /**
34581      * Restore auto-layouts and optionally disable the manager from performing a layout
34582      * @param {Boolean} noLayout true to disable a layout update
34583      */
34584     endUpdate : function(noLayout){
34585         this.updating = false;
34586         if(!noLayout){
34587             this.layout();
34588         }
34589     },
34590
34591     layout: function(){
34592         // abstract...
34593     },
34594
34595     onRegionResized : function(region, newSize){
34596         this.fireEvent("regionresized", region, newSize);
34597         this.layout();
34598     },
34599
34600     onRegionCollapsed : function(region){
34601         this.fireEvent("regioncollapsed", region);
34602     },
34603
34604     onRegionExpanded : function(region){
34605         this.fireEvent("regionexpanded", region);
34606     },
34607
34608     /**
34609      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34610      * performs box-model adjustments.
34611      * @return {Object} The size as an object {width: (the width), height: (the height)}
34612      */
34613     getViewSize : function()
34614     {
34615         var size;
34616         if(this.el.dom != document.body){
34617             size = this.el.getSize();
34618         }else{
34619             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34620         }
34621         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34622         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34623         return size;
34624     },
34625
34626     /**
34627      * Returns the Element this layout is bound to.
34628      * @return {Roo.Element}
34629      */
34630     getEl : function(){
34631         return this.el;
34632     },
34633
34634     /**
34635      * Returns the specified region.
34636      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34637      * @return {Roo.LayoutRegion}
34638      */
34639     getRegion : function(target){
34640         return this.regions[target.toLowerCase()];
34641     },
34642
34643     onWindowResize : function(){
34644         if(this.monitorWindowResize){
34645             this.layout();
34646         }
34647     }
34648 });
34649 /*
34650  * Based on:
34651  * Ext JS Library 1.1.1
34652  * Copyright(c) 2006-2007, Ext JS, LLC.
34653  *
34654  * Originally Released Under LGPL - original licence link has changed is not relivant.
34655  *
34656  * Fork - LGPL
34657  * <script type="text/javascript">
34658  */
34659 /**
34660  * @class Roo.bootstrap.layout.Border
34661  * @extends Roo.bootstrap.layout.Manager
34662  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34663  * please see: examples/bootstrap/nested.html<br><br>
34664  
34665 <b>The container the layout is rendered into can be either the body element or any other element.
34666 If it is not the body element, the container needs to either be an absolute positioned element,
34667 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34668 the container size if it is not the body element.</b>
34669
34670 * @constructor
34671 * Create a new Border
34672 * @param {Object} config Configuration options
34673  */
34674 Roo.bootstrap.layout.Border = function(config){
34675     config = config || {};
34676     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34677     
34678     
34679     
34680     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34681         if(config[region]){
34682             config[region].region = region;
34683             this.addRegion(config[region]);
34684         }
34685     },this);
34686     
34687 };
34688
34689 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34690
34691 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34692     /**
34693      * Creates and adds a new region if it doesn't already exist.
34694      * @param {String} target The target region key (north, south, east, west or center).
34695      * @param {Object} config The regions config object
34696      * @return {BorderLayoutRegion} The new region
34697      */
34698     addRegion : function(config)
34699     {
34700         if(!this.regions[config.region]){
34701             var r = this.factory(config);
34702             this.bindRegion(r);
34703         }
34704         return this.regions[config.region];
34705     },
34706
34707     // private (kinda)
34708     bindRegion : function(r){
34709         this.regions[r.config.region] = r;
34710         
34711         r.on("visibilitychange",    this.layout, this);
34712         r.on("paneladded",          this.layout, this);
34713         r.on("panelremoved",        this.layout, this);
34714         r.on("invalidated",         this.layout, this);
34715         r.on("resized",             this.onRegionResized, this);
34716         r.on("collapsed",           this.onRegionCollapsed, this);
34717         r.on("expanded",            this.onRegionExpanded, this);
34718     },
34719
34720     /**
34721      * Performs a layout update.
34722      */
34723     layout : function()
34724     {
34725         if(this.updating) {
34726             return;
34727         }
34728         
34729         // render all the rebions if they have not been done alreayd?
34730         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34731             if(this.regions[region] && !this.regions[region].bodyEl){
34732                 this.regions[region].onRender(this.el)
34733             }
34734         },this);
34735         
34736         var size = this.getViewSize();
34737         var w = size.width;
34738         var h = size.height;
34739         var centerW = w;
34740         var centerH = h;
34741         var centerY = 0;
34742         var centerX = 0;
34743         //var x = 0, y = 0;
34744
34745         var rs = this.regions;
34746         var north = rs["north"];
34747         var south = rs["south"]; 
34748         var west = rs["west"];
34749         var east = rs["east"];
34750         var center = rs["center"];
34751         //if(this.hideOnLayout){ // not supported anymore
34752             //c.el.setStyle("display", "none");
34753         //}
34754         if(north && north.isVisible()){
34755             var b = north.getBox();
34756             var m = north.getMargins();
34757             b.width = w - (m.left+m.right);
34758             b.x = m.left;
34759             b.y = m.top;
34760             centerY = b.height + b.y + m.bottom;
34761             centerH -= centerY;
34762             north.updateBox(this.safeBox(b));
34763         }
34764         if(south && south.isVisible()){
34765             var b = south.getBox();
34766             var m = south.getMargins();
34767             b.width = w - (m.left+m.right);
34768             b.x = m.left;
34769             var totalHeight = (b.height + m.top + m.bottom);
34770             b.y = h - totalHeight + m.top;
34771             centerH -= totalHeight;
34772             south.updateBox(this.safeBox(b));
34773         }
34774         if(west && west.isVisible()){
34775             var b = west.getBox();
34776             var m = west.getMargins();
34777             b.height = centerH - (m.top+m.bottom);
34778             b.x = m.left;
34779             b.y = centerY + m.top;
34780             var totalWidth = (b.width + m.left + m.right);
34781             centerX += totalWidth;
34782             centerW -= totalWidth;
34783             west.updateBox(this.safeBox(b));
34784         }
34785         if(east && east.isVisible()){
34786             var b = east.getBox();
34787             var m = east.getMargins();
34788             b.height = centerH - (m.top+m.bottom);
34789             var totalWidth = (b.width + m.left + m.right);
34790             b.x = w - totalWidth + m.left;
34791             b.y = centerY + m.top;
34792             centerW -= totalWidth;
34793             east.updateBox(this.safeBox(b));
34794         }
34795         if(center){
34796             var m = center.getMargins();
34797             var centerBox = {
34798                 x: centerX + m.left,
34799                 y: centerY + m.top,
34800                 width: centerW - (m.left+m.right),
34801                 height: centerH - (m.top+m.bottom)
34802             };
34803             //if(this.hideOnLayout){
34804                 //center.el.setStyle("display", "block");
34805             //}
34806             center.updateBox(this.safeBox(centerBox));
34807         }
34808         this.el.repaint();
34809         this.fireEvent("layout", this);
34810     },
34811
34812     // private
34813     safeBox : function(box){
34814         box.width = Math.max(0, box.width);
34815         box.height = Math.max(0, box.height);
34816         return box;
34817     },
34818
34819     /**
34820      * Adds a ContentPanel (or subclass) to this layout.
34821      * @param {String} target The target region key (north, south, east, west or center).
34822      * @param {Roo.ContentPanel} panel The panel to add
34823      * @return {Roo.ContentPanel} The added panel
34824      */
34825     add : function(target, panel){
34826          
34827         target = target.toLowerCase();
34828         return this.regions[target].add(panel);
34829     },
34830
34831     /**
34832      * Remove a ContentPanel (or subclass) to this layout.
34833      * @param {String} target The target region key (north, south, east, west or center).
34834      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34835      * @return {Roo.ContentPanel} The removed panel
34836      */
34837     remove : function(target, panel){
34838         target = target.toLowerCase();
34839         return this.regions[target].remove(panel);
34840     },
34841
34842     /**
34843      * Searches all regions for a panel with the specified id
34844      * @param {String} panelId
34845      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34846      */
34847     findPanel : function(panelId){
34848         var rs = this.regions;
34849         for(var target in rs){
34850             if(typeof rs[target] != "function"){
34851                 var p = rs[target].getPanel(panelId);
34852                 if(p){
34853                     return p;
34854                 }
34855             }
34856         }
34857         return null;
34858     },
34859
34860     /**
34861      * Searches all regions for a panel with the specified id and activates (shows) it.
34862      * @param {String/ContentPanel} panelId The panels id or the panel itself
34863      * @return {Roo.ContentPanel} The shown panel or null
34864      */
34865     showPanel : function(panelId) {
34866       var rs = this.regions;
34867       for(var target in rs){
34868          var r = rs[target];
34869          if(typeof r != "function"){
34870             if(r.hasPanel(panelId)){
34871                return r.showPanel(panelId);
34872             }
34873          }
34874       }
34875       return null;
34876    },
34877
34878    /**
34879      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34880      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34881      */
34882    /*
34883     restoreState : function(provider){
34884         if(!provider){
34885             provider = Roo.state.Manager;
34886         }
34887         var sm = new Roo.LayoutStateManager();
34888         sm.init(this, provider);
34889     },
34890 */
34891  
34892  
34893     /**
34894      * Adds a xtype elements to the layout.
34895      * <pre><code>
34896
34897 layout.addxtype({
34898        xtype : 'ContentPanel',
34899        region: 'west',
34900        items: [ .... ]
34901    }
34902 );
34903
34904 layout.addxtype({
34905         xtype : 'NestedLayoutPanel',
34906         region: 'west',
34907         layout: {
34908            center: { },
34909            west: { }   
34910         },
34911         items : [ ... list of content panels or nested layout panels.. ]
34912    }
34913 );
34914 </code></pre>
34915      * @param {Object} cfg Xtype definition of item to add.
34916      */
34917     addxtype : function(cfg)
34918     {
34919         // basically accepts a pannel...
34920         // can accept a layout region..!?!?
34921         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34922         
34923         
34924         // theory?  children can only be panels??
34925         
34926         //if (!cfg.xtype.match(/Panel$/)) {
34927         //    return false;
34928         //}
34929         var ret = false;
34930         
34931         if (typeof(cfg.region) == 'undefined') {
34932             Roo.log("Failed to add Panel, region was not set");
34933             Roo.log(cfg);
34934             return false;
34935         }
34936         var region = cfg.region;
34937         delete cfg.region;
34938         
34939           
34940         var xitems = [];
34941         if (cfg.items) {
34942             xitems = cfg.items;
34943             delete cfg.items;
34944         }
34945         var nb = false;
34946         
34947         switch(cfg.xtype) 
34948         {
34949             case 'Content':  // ContentPanel (el, cfg)
34950             case 'Scroll':  // ContentPanel (el, cfg)
34951             case 'View': 
34952                 cfg.autoCreate = true;
34953                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34954                 //} else {
34955                 //    var el = this.el.createChild();
34956                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34957                 //}
34958                 
34959                 this.add(region, ret);
34960                 break;
34961             
34962             /*
34963             case 'TreePanel': // our new panel!
34964                 cfg.el = this.el.createChild();
34965                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34966                 this.add(region, ret);
34967                 break;
34968             */
34969             
34970             case 'Nest': 
34971                 // create a new Layout (which is  a Border Layout...
34972                 
34973                 var clayout = cfg.layout;
34974                 clayout.el  = this.el.createChild();
34975                 clayout.items   = clayout.items  || [];
34976                 
34977                 delete cfg.layout;
34978                 
34979                 // replace this exitems with the clayout ones..
34980                 xitems = clayout.items;
34981                  
34982                 // force background off if it's in center...
34983                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34984                     cfg.background = false;
34985                 }
34986                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34987                 
34988                 
34989                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34990                 //console.log('adding nested layout panel '  + cfg.toSource());
34991                 this.add(region, ret);
34992                 nb = {}; /// find first...
34993                 break;
34994             
34995             case 'Grid':
34996                 
34997                 // needs grid and region
34998                 
34999                 //var el = this.getRegion(region).el.createChild();
35000                 /*
35001                  *var el = this.el.createChild();
35002                 // create the grid first...
35003                 cfg.grid.container = el;
35004                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35005                 */
35006                 
35007                 if (region == 'center' && this.active ) {
35008                     cfg.background = false;
35009                 }
35010                 
35011                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35012                 
35013                 this.add(region, ret);
35014                 /*
35015                 if (cfg.background) {
35016                     // render grid on panel activation (if panel background)
35017                     ret.on('activate', function(gp) {
35018                         if (!gp.grid.rendered) {
35019                     //        gp.grid.render(el);
35020                         }
35021                     });
35022                 } else {
35023                   //  cfg.grid.render(el);
35024                 }
35025                 */
35026                 break;
35027            
35028            
35029             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35030                 // it was the old xcomponent building that caused this before.
35031                 // espeically if border is the top element in the tree.
35032                 ret = this;
35033                 break; 
35034                 
35035                     
35036                 
35037                 
35038                 
35039             default:
35040                 /*
35041                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35042                     
35043                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35044                     this.add(region, ret);
35045                 } else {
35046                 */
35047                     Roo.log(cfg);
35048                     throw "Can not add '" + cfg.xtype + "' to Border";
35049                     return null;
35050              
35051                                 
35052              
35053         }
35054         this.beginUpdate();
35055         // add children..
35056         var region = '';
35057         var abn = {};
35058         Roo.each(xitems, function(i)  {
35059             region = nb && i.region ? i.region : false;
35060             
35061             var add = ret.addxtype(i);
35062            
35063             if (region) {
35064                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35065                 if (!i.background) {
35066                     abn[region] = nb[region] ;
35067                 }
35068             }
35069             
35070         });
35071         this.endUpdate();
35072
35073         // make the last non-background panel active..
35074         //if (nb) { Roo.log(abn); }
35075         if (nb) {
35076             
35077             for(var r in abn) {
35078                 region = this.getRegion(r);
35079                 if (region) {
35080                     // tried using nb[r], but it does not work..
35081                      
35082                     region.showPanel(abn[r]);
35083                    
35084                 }
35085             }
35086         }
35087         return ret;
35088         
35089     },
35090     
35091     
35092 // private
35093     factory : function(cfg)
35094     {
35095         
35096         var validRegions = Roo.bootstrap.layout.Border.regions;
35097
35098         var target = cfg.region;
35099         cfg.mgr = this;
35100         
35101         var r = Roo.bootstrap.layout;
35102         Roo.log(target);
35103         switch(target){
35104             case "north":
35105                 return new r.North(cfg);
35106             case "south":
35107                 return new r.South(cfg);
35108             case "east":
35109                 return new r.East(cfg);
35110             case "west":
35111                 return new r.West(cfg);
35112             case "center":
35113                 return new r.Center(cfg);
35114         }
35115         throw 'Layout region "'+target+'" not supported.';
35116     }
35117     
35118     
35119 });
35120  /*
35121  * Based on:
35122  * Ext JS Library 1.1.1
35123  * Copyright(c) 2006-2007, Ext JS, LLC.
35124  *
35125  * Originally Released Under LGPL - original licence link has changed is not relivant.
35126  *
35127  * Fork - LGPL
35128  * <script type="text/javascript">
35129  */
35130  
35131 /**
35132  * @class Roo.bootstrap.layout.Basic
35133  * @extends Roo.util.Observable
35134  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35135  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35136  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35137  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35138  * @cfg {string}   region  the region that it inhabits..
35139  * @cfg {bool}   skipConfig skip config?
35140  * 
35141
35142  */
35143 Roo.bootstrap.layout.Basic = function(config){
35144     
35145     this.mgr = config.mgr;
35146     
35147     this.position = config.region;
35148     
35149     var skipConfig = config.skipConfig;
35150     
35151     this.events = {
35152         /**
35153          * @scope Roo.BasicLayoutRegion
35154          */
35155         
35156         /**
35157          * @event beforeremove
35158          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35159          * @param {Roo.LayoutRegion} this
35160          * @param {Roo.ContentPanel} panel The panel
35161          * @param {Object} e The cancel event object
35162          */
35163         "beforeremove" : true,
35164         /**
35165          * @event invalidated
35166          * Fires when the layout for this region is changed.
35167          * @param {Roo.LayoutRegion} this
35168          */
35169         "invalidated" : true,
35170         /**
35171          * @event visibilitychange
35172          * Fires when this region is shown or hidden 
35173          * @param {Roo.LayoutRegion} this
35174          * @param {Boolean} visibility true or false
35175          */
35176         "visibilitychange" : true,
35177         /**
35178          * @event paneladded
35179          * Fires when a panel is added. 
35180          * @param {Roo.LayoutRegion} this
35181          * @param {Roo.ContentPanel} panel The panel
35182          */
35183         "paneladded" : true,
35184         /**
35185          * @event panelremoved
35186          * Fires when a panel is removed. 
35187          * @param {Roo.LayoutRegion} this
35188          * @param {Roo.ContentPanel} panel The panel
35189          */
35190         "panelremoved" : true,
35191         /**
35192          * @event beforecollapse
35193          * Fires when this region before collapse.
35194          * @param {Roo.LayoutRegion} this
35195          */
35196         "beforecollapse" : true,
35197         /**
35198          * @event collapsed
35199          * Fires when this region is collapsed.
35200          * @param {Roo.LayoutRegion} this
35201          */
35202         "collapsed" : true,
35203         /**
35204          * @event expanded
35205          * Fires when this region is expanded.
35206          * @param {Roo.LayoutRegion} this
35207          */
35208         "expanded" : true,
35209         /**
35210          * @event slideshow
35211          * Fires when this region is slid into view.
35212          * @param {Roo.LayoutRegion} this
35213          */
35214         "slideshow" : true,
35215         /**
35216          * @event slidehide
35217          * Fires when this region slides out of view. 
35218          * @param {Roo.LayoutRegion} this
35219          */
35220         "slidehide" : true,
35221         /**
35222          * @event panelactivated
35223          * Fires when a panel is activated. 
35224          * @param {Roo.LayoutRegion} this
35225          * @param {Roo.ContentPanel} panel The activated panel
35226          */
35227         "panelactivated" : true,
35228         /**
35229          * @event resized
35230          * Fires when the user resizes this region. 
35231          * @param {Roo.LayoutRegion} this
35232          * @param {Number} newSize The new size (width for east/west, height for north/south)
35233          */
35234         "resized" : true
35235     };
35236     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35237     this.panels = new Roo.util.MixedCollection();
35238     this.panels.getKey = this.getPanelId.createDelegate(this);
35239     this.box = null;
35240     this.activePanel = null;
35241     // ensure listeners are added...
35242     
35243     if (config.listeners || config.events) {
35244         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35245             listeners : config.listeners || {},
35246             events : config.events || {}
35247         });
35248     }
35249     
35250     if(skipConfig !== true){
35251         this.applyConfig(config);
35252     }
35253 };
35254
35255 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35256 {
35257     getPanelId : function(p){
35258         return p.getId();
35259     },
35260     
35261     applyConfig : function(config){
35262         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35263         this.config = config;
35264         
35265     },
35266     
35267     /**
35268      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35269      * the width, for horizontal (north, south) the height.
35270      * @param {Number} newSize The new width or height
35271      */
35272     resizeTo : function(newSize){
35273         var el = this.el ? this.el :
35274                  (this.activePanel ? this.activePanel.getEl() : null);
35275         if(el){
35276             switch(this.position){
35277                 case "east":
35278                 case "west":
35279                     el.setWidth(newSize);
35280                     this.fireEvent("resized", this, newSize);
35281                 break;
35282                 case "north":
35283                 case "south":
35284                     el.setHeight(newSize);
35285                     this.fireEvent("resized", this, newSize);
35286                 break;                
35287             }
35288         }
35289     },
35290     
35291     getBox : function(){
35292         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35293     },
35294     
35295     getMargins : function(){
35296         return this.margins;
35297     },
35298     
35299     updateBox : function(box){
35300         this.box = box;
35301         var el = this.activePanel.getEl();
35302         el.dom.style.left = box.x + "px";
35303         el.dom.style.top = box.y + "px";
35304         this.activePanel.setSize(box.width, box.height);
35305     },
35306     
35307     /**
35308      * Returns the container element for this region.
35309      * @return {Roo.Element}
35310      */
35311     getEl : function(){
35312         return this.activePanel;
35313     },
35314     
35315     /**
35316      * Returns true if this region is currently visible.
35317      * @return {Boolean}
35318      */
35319     isVisible : function(){
35320         return this.activePanel ? true : false;
35321     },
35322     
35323     setActivePanel : function(panel){
35324         panel = this.getPanel(panel);
35325         if(this.activePanel && this.activePanel != panel){
35326             this.activePanel.setActiveState(false);
35327             this.activePanel.getEl().setLeftTop(-10000,-10000);
35328         }
35329         this.activePanel = panel;
35330         panel.setActiveState(true);
35331         if(this.box){
35332             panel.setSize(this.box.width, this.box.height);
35333         }
35334         this.fireEvent("panelactivated", this, panel);
35335         this.fireEvent("invalidated");
35336     },
35337     
35338     /**
35339      * Show the specified panel.
35340      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35341      * @return {Roo.ContentPanel} The shown panel or null
35342      */
35343     showPanel : function(panel){
35344         panel = this.getPanel(panel);
35345         if(panel){
35346             this.setActivePanel(panel);
35347         }
35348         return panel;
35349     },
35350     
35351     /**
35352      * Get the active panel for this region.
35353      * @return {Roo.ContentPanel} The active panel or null
35354      */
35355     getActivePanel : function(){
35356         return this.activePanel;
35357     },
35358     
35359     /**
35360      * Add the passed ContentPanel(s)
35361      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35362      * @return {Roo.ContentPanel} The panel added (if only one was added)
35363      */
35364     add : function(panel){
35365         if(arguments.length > 1){
35366             for(var i = 0, len = arguments.length; i < len; i++) {
35367                 this.add(arguments[i]);
35368             }
35369             return null;
35370         }
35371         if(this.hasPanel(panel)){
35372             this.showPanel(panel);
35373             return panel;
35374         }
35375         var el = panel.getEl();
35376         if(el.dom.parentNode != this.mgr.el.dom){
35377             this.mgr.el.dom.appendChild(el.dom);
35378         }
35379         if(panel.setRegion){
35380             panel.setRegion(this);
35381         }
35382         this.panels.add(panel);
35383         el.setStyle("position", "absolute");
35384         if(!panel.background){
35385             this.setActivePanel(panel);
35386             if(this.config.initialSize && this.panels.getCount()==1){
35387                 this.resizeTo(this.config.initialSize);
35388             }
35389         }
35390         this.fireEvent("paneladded", this, panel);
35391         return panel;
35392     },
35393     
35394     /**
35395      * Returns true if the panel is in this region.
35396      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35397      * @return {Boolean}
35398      */
35399     hasPanel : function(panel){
35400         if(typeof panel == "object"){ // must be panel obj
35401             panel = panel.getId();
35402         }
35403         return this.getPanel(panel) ? true : false;
35404     },
35405     
35406     /**
35407      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35408      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35409      * @param {Boolean} preservePanel Overrides the config preservePanel option
35410      * @return {Roo.ContentPanel} The panel that was removed
35411      */
35412     remove : function(panel, preservePanel){
35413         panel = this.getPanel(panel);
35414         if(!panel){
35415             return null;
35416         }
35417         var e = {};
35418         this.fireEvent("beforeremove", this, panel, e);
35419         if(e.cancel === true){
35420             return null;
35421         }
35422         var panelId = panel.getId();
35423         this.panels.removeKey(panelId);
35424         return panel;
35425     },
35426     
35427     /**
35428      * Returns the panel specified or null if it's not in this region.
35429      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35430      * @return {Roo.ContentPanel}
35431      */
35432     getPanel : function(id){
35433         if(typeof id == "object"){ // must be panel obj
35434             return id;
35435         }
35436         return this.panels.get(id);
35437     },
35438     
35439     /**
35440      * Returns this regions position (north/south/east/west/center).
35441      * @return {String} 
35442      */
35443     getPosition: function(){
35444         return this.position;    
35445     }
35446 });/*
35447  * Based on:
35448  * Ext JS Library 1.1.1
35449  * Copyright(c) 2006-2007, Ext JS, LLC.
35450  *
35451  * Originally Released Under LGPL - original licence link has changed is not relivant.
35452  *
35453  * Fork - LGPL
35454  * <script type="text/javascript">
35455  */
35456  
35457 /**
35458  * @class Roo.bootstrap.layout.Region
35459  * @extends Roo.bootstrap.layout.Basic
35460  * This class represents a region in a layout manager.
35461  
35462  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35463  * @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})
35464  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35465  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35466  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35467  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35468  * @cfg {String}    title           The title for the region (overrides panel titles)
35469  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35470  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35471  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35472  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35473  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35474  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35475  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35476  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35477  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35478  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35479
35480  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35481  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35482  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35483  * @cfg {Number}    width           For East/West panels
35484  * @cfg {Number}    height          For North/South panels
35485  * @cfg {Boolean}   split           To show the splitter
35486  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35487  * 
35488  * @cfg {string}   cls             Extra CSS classes to add to region
35489  * 
35490  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35491  * @cfg {string}   region  the region that it inhabits..
35492  *
35493
35494  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35495  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35496
35497  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35498  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35499  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35500  */
35501 Roo.bootstrap.layout.Region = function(config)
35502 {
35503     this.applyConfig(config);
35504
35505     var mgr = config.mgr;
35506     var pos = config.region;
35507     config.skipConfig = true;
35508     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35509     
35510     if (mgr.el) {
35511         this.onRender(mgr.el);   
35512     }
35513      
35514     this.visible = true;
35515     this.collapsed = false;
35516     this.unrendered_panels = [];
35517 };
35518
35519 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35520
35521     position: '', // set by wrapper (eg. north/south etc..)
35522     unrendered_panels : null,  // unrendered panels.
35523     createBody : function(){
35524         /** This region's body element 
35525         * @type Roo.Element */
35526         this.bodyEl = this.el.createChild({
35527                 tag: "div",
35528                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35529         });
35530     },
35531
35532     onRender: function(ctr, pos)
35533     {
35534         var dh = Roo.DomHelper;
35535         /** This region's container element 
35536         * @type Roo.Element */
35537         this.el = dh.append(ctr.dom, {
35538                 tag: "div",
35539                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35540             }, true);
35541         /** This region's title element 
35542         * @type Roo.Element */
35543     
35544         this.titleEl = dh.append(this.el.dom,
35545             {
35546                     tag: "div",
35547                     unselectable: "on",
35548                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35549                     children:[
35550                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35551                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35552                     ]}, true);
35553         
35554         this.titleEl.enableDisplayMode();
35555         /** This region's title text element 
35556         * @type HTMLElement */
35557         this.titleTextEl = this.titleEl.dom.firstChild;
35558         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35559         /*
35560         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35561         this.closeBtn.enableDisplayMode();
35562         this.closeBtn.on("click", this.closeClicked, this);
35563         this.closeBtn.hide();
35564     */
35565         this.createBody(this.config);
35566         if(this.config.hideWhenEmpty){
35567             this.hide();
35568             this.on("paneladded", this.validateVisibility, this);
35569             this.on("panelremoved", this.validateVisibility, this);
35570         }
35571         if(this.autoScroll){
35572             this.bodyEl.setStyle("overflow", "auto");
35573         }else{
35574             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35575         }
35576         //if(c.titlebar !== false){
35577             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35578                 this.titleEl.hide();
35579             }else{
35580                 this.titleEl.show();
35581                 if(this.config.title){
35582                     this.titleTextEl.innerHTML = this.config.title;
35583                 }
35584             }
35585         //}
35586         if(this.config.collapsed){
35587             this.collapse(true);
35588         }
35589         if(this.config.hidden){
35590             this.hide();
35591         }
35592         
35593         if (this.unrendered_panels && this.unrendered_panels.length) {
35594             for (var i =0;i< this.unrendered_panels.length; i++) {
35595                 this.add(this.unrendered_panels[i]);
35596             }
35597             this.unrendered_panels = null;
35598             
35599         }
35600         
35601     },
35602     
35603     applyConfig : function(c)
35604     {
35605         /*
35606          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35607             var dh = Roo.DomHelper;
35608             if(c.titlebar !== false){
35609                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35610                 this.collapseBtn.on("click", this.collapse, this);
35611                 this.collapseBtn.enableDisplayMode();
35612                 /*
35613                 if(c.showPin === true || this.showPin){
35614                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35615                     this.stickBtn.enableDisplayMode();
35616                     this.stickBtn.on("click", this.expand, this);
35617                     this.stickBtn.hide();
35618                 }
35619                 
35620             }
35621             */
35622             /** This region's collapsed element
35623             * @type Roo.Element */
35624             /*
35625              *
35626             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35627                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35628             ]}, true);
35629             
35630             if(c.floatable !== false){
35631                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35632                this.collapsedEl.on("click", this.collapseClick, this);
35633             }
35634
35635             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35636                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35637                    id: "message", unselectable: "on", style:{"float":"left"}});
35638                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35639              }
35640             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35641             this.expandBtn.on("click", this.expand, this);
35642             
35643         }
35644         
35645         if(this.collapseBtn){
35646             this.collapseBtn.setVisible(c.collapsible == true);
35647         }
35648         
35649         this.cmargins = c.cmargins || this.cmargins ||
35650                          (this.position == "west" || this.position == "east" ?
35651                              {top: 0, left: 2, right:2, bottom: 0} :
35652                              {top: 2, left: 0, right:0, bottom: 2});
35653         */
35654         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35655         
35656         
35657         this.bottomTabs = c.tabPosition != "top";
35658         
35659         this.autoScroll = c.autoScroll || false;
35660         
35661         
35662        
35663         
35664         this.duration = c.duration || .30;
35665         this.slideDuration = c.slideDuration || .45;
35666         this.config = c;
35667        
35668     },
35669     /**
35670      * Returns true if this region is currently visible.
35671      * @return {Boolean}
35672      */
35673     isVisible : function(){
35674         return this.visible;
35675     },
35676
35677     /**
35678      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35679      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35680      */
35681     //setCollapsedTitle : function(title){
35682     //    title = title || "&#160;";
35683      //   if(this.collapsedTitleTextEl){
35684       //      this.collapsedTitleTextEl.innerHTML = title;
35685        // }
35686     //},
35687
35688     getBox : function(){
35689         var b;
35690       //  if(!this.collapsed){
35691             b = this.el.getBox(false, true);
35692        // }else{
35693           //  b = this.collapsedEl.getBox(false, true);
35694         //}
35695         return b;
35696     },
35697
35698     getMargins : function(){
35699         return this.margins;
35700         //return this.collapsed ? this.cmargins : this.margins;
35701     },
35702 /*
35703     highlight : function(){
35704         this.el.addClass("x-layout-panel-dragover");
35705     },
35706
35707     unhighlight : function(){
35708         this.el.removeClass("x-layout-panel-dragover");
35709     },
35710 */
35711     updateBox : function(box)
35712     {
35713         if (!this.bodyEl) {
35714             return; // not rendered yet..
35715         }
35716         
35717         this.box = box;
35718         if(!this.collapsed){
35719             this.el.dom.style.left = box.x + "px";
35720             this.el.dom.style.top = box.y + "px";
35721             this.updateBody(box.width, box.height);
35722         }else{
35723             this.collapsedEl.dom.style.left = box.x + "px";
35724             this.collapsedEl.dom.style.top = box.y + "px";
35725             this.collapsedEl.setSize(box.width, box.height);
35726         }
35727         if(this.tabs){
35728             this.tabs.autoSizeTabs();
35729         }
35730     },
35731
35732     updateBody : function(w, h)
35733     {
35734         if(w !== null){
35735             this.el.setWidth(w);
35736             w -= this.el.getBorderWidth("rl");
35737             if(this.config.adjustments){
35738                 w += this.config.adjustments[0];
35739             }
35740         }
35741         if(h !== null && h > 0){
35742             this.el.setHeight(h);
35743             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35744             h -= this.el.getBorderWidth("tb");
35745             if(this.config.adjustments){
35746                 h += this.config.adjustments[1];
35747             }
35748             this.bodyEl.setHeight(h);
35749             if(this.tabs){
35750                 h = this.tabs.syncHeight(h);
35751             }
35752         }
35753         if(this.panelSize){
35754             w = w !== null ? w : this.panelSize.width;
35755             h = h !== null ? h : this.panelSize.height;
35756         }
35757         if(this.activePanel){
35758             var el = this.activePanel.getEl();
35759             w = w !== null ? w : el.getWidth();
35760             h = h !== null ? h : el.getHeight();
35761             this.panelSize = {width: w, height: h};
35762             this.activePanel.setSize(w, h);
35763         }
35764         if(Roo.isIE && this.tabs){
35765             this.tabs.el.repaint();
35766         }
35767     },
35768
35769     /**
35770      * Returns the container element for this region.
35771      * @return {Roo.Element}
35772      */
35773     getEl : function(){
35774         return this.el;
35775     },
35776
35777     /**
35778      * Hides this region.
35779      */
35780     hide : function(){
35781         //if(!this.collapsed){
35782             this.el.dom.style.left = "-2000px";
35783             this.el.hide();
35784         //}else{
35785          //   this.collapsedEl.dom.style.left = "-2000px";
35786          //   this.collapsedEl.hide();
35787        // }
35788         this.visible = false;
35789         this.fireEvent("visibilitychange", this, false);
35790     },
35791
35792     /**
35793      * Shows this region if it was previously hidden.
35794      */
35795     show : function(){
35796         //if(!this.collapsed){
35797             this.el.show();
35798         //}else{
35799         //    this.collapsedEl.show();
35800        // }
35801         this.visible = true;
35802         this.fireEvent("visibilitychange", this, true);
35803     },
35804 /*
35805     closeClicked : function(){
35806         if(this.activePanel){
35807             this.remove(this.activePanel);
35808         }
35809     },
35810
35811     collapseClick : function(e){
35812         if(this.isSlid){
35813            e.stopPropagation();
35814            this.slideIn();
35815         }else{
35816            e.stopPropagation();
35817            this.slideOut();
35818         }
35819     },
35820 */
35821     /**
35822      * Collapses this region.
35823      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35824      */
35825     /*
35826     collapse : function(skipAnim, skipCheck = false){
35827         if(this.collapsed) {
35828             return;
35829         }
35830         
35831         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35832             
35833             this.collapsed = true;
35834             if(this.split){
35835                 this.split.el.hide();
35836             }
35837             if(this.config.animate && skipAnim !== true){
35838                 this.fireEvent("invalidated", this);
35839                 this.animateCollapse();
35840             }else{
35841                 this.el.setLocation(-20000,-20000);
35842                 this.el.hide();
35843                 this.collapsedEl.show();
35844                 this.fireEvent("collapsed", this);
35845                 this.fireEvent("invalidated", this);
35846             }
35847         }
35848         
35849     },
35850 */
35851     animateCollapse : function(){
35852         // overridden
35853     },
35854
35855     /**
35856      * Expands this region if it was previously collapsed.
35857      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35858      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35859      */
35860     /*
35861     expand : function(e, skipAnim){
35862         if(e) {
35863             e.stopPropagation();
35864         }
35865         if(!this.collapsed || this.el.hasActiveFx()) {
35866             return;
35867         }
35868         if(this.isSlid){
35869             this.afterSlideIn();
35870             skipAnim = true;
35871         }
35872         this.collapsed = false;
35873         if(this.config.animate && skipAnim !== true){
35874             this.animateExpand();
35875         }else{
35876             this.el.show();
35877             if(this.split){
35878                 this.split.el.show();
35879             }
35880             this.collapsedEl.setLocation(-2000,-2000);
35881             this.collapsedEl.hide();
35882             this.fireEvent("invalidated", this);
35883             this.fireEvent("expanded", this);
35884         }
35885     },
35886 */
35887     animateExpand : function(){
35888         // overridden
35889     },
35890
35891     initTabs : function()
35892     {
35893         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35894         
35895         var ts = new Roo.bootstrap.panel.Tabs({
35896                 el: this.bodyEl.dom,
35897                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35898                 disableTooltips: this.config.disableTabTips,
35899                 toolbar : this.config.toolbar
35900             });
35901         
35902         if(this.config.hideTabs){
35903             ts.stripWrap.setDisplayed(false);
35904         }
35905         this.tabs = ts;
35906         ts.resizeTabs = this.config.resizeTabs === true;
35907         ts.minTabWidth = this.config.minTabWidth || 40;
35908         ts.maxTabWidth = this.config.maxTabWidth || 250;
35909         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35910         ts.monitorResize = false;
35911         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35912         ts.bodyEl.addClass('roo-layout-tabs-body');
35913         this.panels.each(this.initPanelAsTab, this);
35914     },
35915
35916     initPanelAsTab : function(panel){
35917         var ti = this.tabs.addTab(
35918             panel.getEl().id,
35919             panel.getTitle(),
35920             null,
35921             this.config.closeOnTab && panel.isClosable(),
35922             panel.tpl
35923         );
35924         if(panel.tabTip !== undefined){
35925             ti.setTooltip(panel.tabTip);
35926         }
35927         ti.on("activate", function(){
35928               this.setActivePanel(panel);
35929         }, this);
35930         
35931         if(this.config.closeOnTab){
35932             ti.on("beforeclose", function(t, e){
35933                 e.cancel = true;
35934                 this.remove(panel);
35935             }, this);
35936         }
35937         
35938         panel.tabItem = ti;
35939         
35940         return ti;
35941     },
35942
35943     updatePanelTitle : function(panel, title)
35944     {
35945         if(this.activePanel == panel){
35946             this.updateTitle(title);
35947         }
35948         if(this.tabs){
35949             var ti = this.tabs.getTab(panel.getEl().id);
35950             ti.setText(title);
35951             if(panel.tabTip !== undefined){
35952                 ti.setTooltip(panel.tabTip);
35953             }
35954         }
35955     },
35956
35957     updateTitle : function(title){
35958         if(this.titleTextEl && !this.config.title){
35959             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35960         }
35961     },
35962
35963     setActivePanel : function(panel)
35964     {
35965         panel = this.getPanel(panel);
35966         if(this.activePanel && this.activePanel != panel){
35967             if(this.activePanel.setActiveState(false) === false){
35968                 return;
35969             }
35970         }
35971         this.activePanel = panel;
35972         panel.setActiveState(true);
35973         if(this.panelSize){
35974             panel.setSize(this.panelSize.width, this.panelSize.height);
35975         }
35976         if(this.closeBtn){
35977             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35978         }
35979         this.updateTitle(panel.getTitle());
35980         if(this.tabs){
35981             this.fireEvent("invalidated", this);
35982         }
35983         this.fireEvent("panelactivated", this, panel);
35984     },
35985
35986     /**
35987      * Shows the specified panel.
35988      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35989      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35990      */
35991     showPanel : function(panel)
35992     {
35993         panel = this.getPanel(panel);
35994         if(panel){
35995             if(this.tabs){
35996                 var tab = this.tabs.getTab(panel.getEl().id);
35997                 if(tab.isHidden()){
35998                     this.tabs.unhideTab(tab.id);
35999                 }
36000                 tab.activate();
36001             }else{
36002                 this.setActivePanel(panel);
36003             }
36004         }
36005         return panel;
36006     },
36007
36008     /**
36009      * Get the active panel for this region.
36010      * @return {Roo.ContentPanel} The active panel or null
36011      */
36012     getActivePanel : function(){
36013         return this.activePanel;
36014     },
36015
36016     validateVisibility : function(){
36017         if(this.panels.getCount() < 1){
36018             this.updateTitle("&#160;");
36019             this.closeBtn.hide();
36020             this.hide();
36021         }else{
36022             if(!this.isVisible()){
36023                 this.show();
36024             }
36025         }
36026     },
36027
36028     /**
36029      * Adds the passed ContentPanel(s) to this region.
36030      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36031      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36032      */
36033     add : function(panel)
36034     {
36035         if(arguments.length > 1){
36036             for(var i = 0, len = arguments.length; i < len; i++) {
36037                 this.add(arguments[i]);
36038             }
36039             return null;
36040         }
36041         
36042         // if we have not been rendered yet, then we can not really do much of this..
36043         if (!this.bodyEl) {
36044             this.unrendered_panels.push(panel);
36045             return panel;
36046         }
36047         
36048         
36049         
36050         
36051         if(this.hasPanel(panel)){
36052             this.showPanel(panel);
36053             return panel;
36054         }
36055         panel.setRegion(this);
36056         this.panels.add(panel);
36057        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36058             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36059             // and hide them... ???
36060             this.bodyEl.dom.appendChild(panel.getEl().dom);
36061             if(panel.background !== true){
36062                 this.setActivePanel(panel);
36063             }
36064             this.fireEvent("paneladded", this, panel);
36065             return panel;
36066         }
36067         */
36068         if(!this.tabs){
36069             this.initTabs();
36070         }else{
36071             this.initPanelAsTab(panel);
36072         }
36073         
36074         
36075         if(panel.background !== true){
36076             this.tabs.activate(panel.getEl().id);
36077         }
36078         this.fireEvent("paneladded", this, panel);
36079         return panel;
36080     },
36081
36082     /**
36083      * Hides the tab for the specified panel.
36084      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36085      */
36086     hidePanel : function(panel){
36087         if(this.tabs && (panel = this.getPanel(panel))){
36088             this.tabs.hideTab(panel.getEl().id);
36089         }
36090     },
36091
36092     /**
36093      * Unhides the tab for a previously hidden panel.
36094      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36095      */
36096     unhidePanel : function(panel){
36097         if(this.tabs && (panel = this.getPanel(panel))){
36098             this.tabs.unhideTab(panel.getEl().id);
36099         }
36100     },
36101
36102     clearPanels : function(){
36103         while(this.panels.getCount() > 0){
36104              this.remove(this.panels.first());
36105         }
36106     },
36107
36108     /**
36109      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36110      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36111      * @param {Boolean} preservePanel Overrides the config preservePanel option
36112      * @return {Roo.ContentPanel} The panel that was removed
36113      */
36114     remove : function(panel, preservePanel)
36115     {
36116         panel = this.getPanel(panel);
36117         if(!panel){
36118             return null;
36119         }
36120         var e = {};
36121         this.fireEvent("beforeremove", this, panel, e);
36122         if(e.cancel === true){
36123             return null;
36124         }
36125         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36126         var panelId = panel.getId();
36127         this.panels.removeKey(panelId);
36128         if(preservePanel){
36129             document.body.appendChild(panel.getEl().dom);
36130         }
36131         if(this.tabs){
36132             this.tabs.removeTab(panel.getEl().id);
36133         }else if (!preservePanel){
36134             this.bodyEl.dom.removeChild(panel.getEl().dom);
36135         }
36136         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36137             var p = this.panels.first();
36138             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36139             tempEl.appendChild(p.getEl().dom);
36140             this.bodyEl.update("");
36141             this.bodyEl.dom.appendChild(p.getEl().dom);
36142             tempEl = null;
36143             this.updateTitle(p.getTitle());
36144             this.tabs = null;
36145             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36146             this.setActivePanel(p);
36147         }
36148         panel.setRegion(null);
36149         if(this.activePanel == panel){
36150             this.activePanel = null;
36151         }
36152         if(this.config.autoDestroy !== false && preservePanel !== true){
36153             try{panel.destroy();}catch(e){}
36154         }
36155         this.fireEvent("panelremoved", this, panel);
36156         return panel;
36157     },
36158
36159     /**
36160      * Returns the TabPanel component used by this region
36161      * @return {Roo.TabPanel}
36162      */
36163     getTabs : function(){
36164         return this.tabs;
36165     },
36166
36167     createTool : function(parentEl, className){
36168         var btn = Roo.DomHelper.append(parentEl, {
36169             tag: "div",
36170             cls: "x-layout-tools-button",
36171             children: [ {
36172                 tag: "div",
36173                 cls: "roo-layout-tools-button-inner " + className,
36174                 html: "&#160;"
36175             }]
36176         }, true);
36177         btn.addClassOnOver("roo-layout-tools-button-over");
36178         return btn;
36179     }
36180 });/*
36181  * Based on:
36182  * Ext JS Library 1.1.1
36183  * Copyright(c) 2006-2007, Ext JS, LLC.
36184  *
36185  * Originally Released Under LGPL - original licence link has changed is not relivant.
36186  *
36187  * Fork - LGPL
36188  * <script type="text/javascript">
36189  */
36190  
36191
36192
36193 /**
36194  * @class Roo.SplitLayoutRegion
36195  * @extends Roo.LayoutRegion
36196  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36197  */
36198 Roo.bootstrap.layout.Split = function(config){
36199     this.cursor = config.cursor;
36200     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36201 };
36202
36203 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36204 {
36205     splitTip : "Drag to resize.",
36206     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36207     useSplitTips : false,
36208
36209     applyConfig : function(config){
36210         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36211     },
36212     
36213     onRender : function(ctr,pos) {
36214         
36215         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36216         if(!this.config.split){
36217             return;
36218         }
36219         if(!this.split){
36220             
36221             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36222                             tag: "div",
36223                             id: this.el.id + "-split",
36224                             cls: "roo-layout-split roo-layout-split-"+this.position,
36225                             html: "&#160;"
36226             });
36227             /** The SplitBar for this region 
36228             * @type Roo.SplitBar */
36229             // does not exist yet...
36230             Roo.log([this.position, this.orientation]);
36231             
36232             this.split = new Roo.bootstrap.SplitBar({
36233                 dragElement : splitEl,
36234                 resizingElement: this.el,
36235                 orientation : this.orientation
36236             });
36237             
36238             this.split.on("moved", this.onSplitMove, this);
36239             this.split.useShim = this.config.useShim === true;
36240             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36241             if(this.useSplitTips){
36242                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36243             }
36244             //if(config.collapsible){
36245             //    this.split.el.on("dblclick", this.collapse,  this);
36246             //}
36247         }
36248         if(typeof this.config.minSize != "undefined"){
36249             this.split.minSize = this.config.minSize;
36250         }
36251         if(typeof this.config.maxSize != "undefined"){
36252             this.split.maxSize = this.config.maxSize;
36253         }
36254         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36255             this.hideSplitter();
36256         }
36257         
36258     },
36259
36260     getHMaxSize : function(){
36261          var cmax = this.config.maxSize || 10000;
36262          var center = this.mgr.getRegion("center");
36263          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36264     },
36265
36266     getVMaxSize : function(){
36267          var cmax = this.config.maxSize || 10000;
36268          var center = this.mgr.getRegion("center");
36269          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36270     },
36271
36272     onSplitMove : function(split, newSize){
36273         this.fireEvent("resized", this, newSize);
36274     },
36275     
36276     /** 
36277      * Returns the {@link Roo.SplitBar} for this region.
36278      * @return {Roo.SplitBar}
36279      */
36280     getSplitBar : function(){
36281         return this.split;
36282     },
36283     
36284     hide : function(){
36285         this.hideSplitter();
36286         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36287     },
36288
36289     hideSplitter : function(){
36290         if(this.split){
36291             this.split.el.setLocation(-2000,-2000);
36292             this.split.el.hide();
36293         }
36294     },
36295
36296     show : function(){
36297         if(this.split){
36298             this.split.el.show();
36299         }
36300         Roo.bootstrap.layout.Split.superclass.show.call(this);
36301     },
36302     
36303     beforeSlide: function(){
36304         if(Roo.isGecko){// firefox overflow auto bug workaround
36305             this.bodyEl.clip();
36306             if(this.tabs) {
36307                 this.tabs.bodyEl.clip();
36308             }
36309             if(this.activePanel){
36310                 this.activePanel.getEl().clip();
36311                 
36312                 if(this.activePanel.beforeSlide){
36313                     this.activePanel.beforeSlide();
36314                 }
36315             }
36316         }
36317     },
36318     
36319     afterSlide : function(){
36320         if(Roo.isGecko){// firefox overflow auto bug workaround
36321             this.bodyEl.unclip();
36322             if(this.tabs) {
36323                 this.tabs.bodyEl.unclip();
36324             }
36325             if(this.activePanel){
36326                 this.activePanel.getEl().unclip();
36327                 if(this.activePanel.afterSlide){
36328                     this.activePanel.afterSlide();
36329                 }
36330             }
36331         }
36332     },
36333
36334     initAutoHide : function(){
36335         if(this.autoHide !== false){
36336             if(!this.autoHideHd){
36337                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36338                 this.autoHideHd = {
36339                     "mouseout": function(e){
36340                         if(!e.within(this.el, true)){
36341                             st.delay(500);
36342                         }
36343                     },
36344                     "mouseover" : function(e){
36345                         st.cancel();
36346                     },
36347                     scope : this
36348                 };
36349             }
36350             this.el.on(this.autoHideHd);
36351         }
36352     },
36353
36354     clearAutoHide : function(){
36355         if(this.autoHide !== false){
36356             this.el.un("mouseout", this.autoHideHd.mouseout);
36357             this.el.un("mouseover", this.autoHideHd.mouseover);
36358         }
36359     },
36360
36361     clearMonitor : function(){
36362         Roo.get(document).un("click", this.slideInIf, this);
36363     },
36364
36365     // these names are backwards but not changed for compat
36366     slideOut : function(){
36367         if(this.isSlid || this.el.hasActiveFx()){
36368             return;
36369         }
36370         this.isSlid = true;
36371         if(this.collapseBtn){
36372             this.collapseBtn.hide();
36373         }
36374         this.closeBtnState = this.closeBtn.getStyle('display');
36375         this.closeBtn.hide();
36376         if(this.stickBtn){
36377             this.stickBtn.show();
36378         }
36379         this.el.show();
36380         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36381         this.beforeSlide();
36382         this.el.setStyle("z-index", 10001);
36383         this.el.slideIn(this.getSlideAnchor(), {
36384             callback: function(){
36385                 this.afterSlide();
36386                 this.initAutoHide();
36387                 Roo.get(document).on("click", this.slideInIf, this);
36388                 this.fireEvent("slideshow", this);
36389             },
36390             scope: this,
36391             block: true
36392         });
36393     },
36394
36395     afterSlideIn : function(){
36396         this.clearAutoHide();
36397         this.isSlid = false;
36398         this.clearMonitor();
36399         this.el.setStyle("z-index", "");
36400         if(this.collapseBtn){
36401             this.collapseBtn.show();
36402         }
36403         this.closeBtn.setStyle('display', this.closeBtnState);
36404         if(this.stickBtn){
36405             this.stickBtn.hide();
36406         }
36407         this.fireEvent("slidehide", this);
36408     },
36409
36410     slideIn : function(cb){
36411         if(!this.isSlid || this.el.hasActiveFx()){
36412             Roo.callback(cb);
36413             return;
36414         }
36415         this.isSlid = false;
36416         this.beforeSlide();
36417         this.el.slideOut(this.getSlideAnchor(), {
36418             callback: function(){
36419                 this.el.setLeftTop(-10000, -10000);
36420                 this.afterSlide();
36421                 this.afterSlideIn();
36422                 Roo.callback(cb);
36423             },
36424             scope: this,
36425             block: true
36426         });
36427     },
36428     
36429     slideInIf : function(e){
36430         if(!e.within(this.el)){
36431             this.slideIn();
36432         }
36433     },
36434
36435     animateCollapse : function(){
36436         this.beforeSlide();
36437         this.el.setStyle("z-index", 20000);
36438         var anchor = this.getSlideAnchor();
36439         this.el.slideOut(anchor, {
36440             callback : function(){
36441                 this.el.setStyle("z-index", "");
36442                 this.collapsedEl.slideIn(anchor, {duration:.3});
36443                 this.afterSlide();
36444                 this.el.setLocation(-10000,-10000);
36445                 this.el.hide();
36446                 this.fireEvent("collapsed", this);
36447             },
36448             scope: this,
36449             block: true
36450         });
36451     },
36452
36453     animateExpand : function(){
36454         this.beforeSlide();
36455         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36456         this.el.setStyle("z-index", 20000);
36457         this.collapsedEl.hide({
36458             duration:.1
36459         });
36460         this.el.slideIn(this.getSlideAnchor(), {
36461             callback : function(){
36462                 this.el.setStyle("z-index", "");
36463                 this.afterSlide();
36464                 if(this.split){
36465                     this.split.el.show();
36466                 }
36467                 this.fireEvent("invalidated", this);
36468                 this.fireEvent("expanded", this);
36469             },
36470             scope: this,
36471             block: true
36472         });
36473     },
36474
36475     anchors : {
36476         "west" : "left",
36477         "east" : "right",
36478         "north" : "top",
36479         "south" : "bottom"
36480     },
36481
36482     sanchors : {
36483         "west" : "l",
36484         "east" : "r",
36485         "north" : "t",
36486         "south" : "b"
36487     },
36488
36489     canchors : {
36490         "west" : "tl-tr",
36491         "east" : "tr-tl",
36492         "north" : "tl-bl",
36493         "south" : "bl-tl"
36494     },
36495
36496     getAnchor : function(){
36497         return this.anchors[this.position];
36498     },
36499
36500     getCollapseAnchor : function(){
36501         return this.canchors[this.position];
36502     },
36503
36504     getSlideAnchor : function(){
36505         return this.sanchors[this.position];
36506     },
36507
36508     getAlignAdj : function(){
36509         var cm = this.cmargins;
36510         switch(this.position){
36511             case "west":
36512                 return [0, 0];
36513             break;
36514             case "east":
36515                 return [0, 0];
36516             break;
36517             case "north":
36518                 return [0, 0];
36519             break;
36520             case "south":
36521                 return [0, 0];
36522             break;
36523         }
36524     },
36525
36526     getExpandAdj : function(){
36527         var c = this.collapsedEl, cm = this.cmargins;
36528         switch(this.position){
36529             case "west":
36530                 return [-(cm.right+c.getWidth()+cm.left), 0];
36531             break;
36532             case "east":
36533                 return [cm.right+c.getWidth()+cm.left, 0];
36534             break;
36535             case "north":
36536                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36537             break;
36538             case "south":
36539                 return [0, cm.top+cm.bottom+c.getHeight()];
36540             break;
36541         }
36542     }
36543 });/*
36544  * Based on:
36545  * Ext JS Library 1.1.1
36546  * Copyright(c) 2006-2007, Ext JS, LLC.
36547  *
36548  * Originally Released Under LGPL - original licence link has changed is not relivant.
36549  *
36550  * Fork - LGPL
36551  * <script type="text/javascript">
36552  */
36553 /*
36554  * These classes are private internal classes
36555  */
36556 Roo.bootstrap.layout.Center = function(config){
36557     config.region = "center";
36558     Roo.bootstrap.layout.Region.call(this, config);
36559     this.visible = true;
36560     this.minWidth = config.minWidth || 20;
36561     this.minHeight = config.minHeight || 20;
36562 };
36563
36564 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36565     hide : function(){
36566         // center panel can't be hidden
36567     },
36568     
36569     show : function(){
36570         // center panel can't be hidden
36571     },
36572     
36573     getMinWidth: function(){
36574         return this.minWidth;
36575     },
36576     
36577     getMinHeight: function(){
36578         return this.minHeight;
36579     }
36580 });
36581
36582
36583
36584
36585  
36586
36587
36588
36589
36590
36591 Roo.bootstrap.layout.North = function(config)
36592 {
36593     config.region = 'north';
36594     config.cursor = 'n-resize';
36595     
36596     Roo.bootstrap.layout.Split.call(this, config);
36597     
36598     
36599     if(this.split){
36600         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36601         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36602         this.split.el.addClass("roo-layout-split-v");
36603     }
36604     var size = config.initialSize || config.height;
36605     if(typeof size != "undefined"){
36606         this.el.setHeight(size);
36607     }
36608 };
36609 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36610 {
36611     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36612     
36613     
36614     
36615     getBox : function(){
36616         if(this.collapsed){
36617             return this.collapsedEl.getBox();
36618         }
36619         var box = this.el.getBox();
36620         if(this.split){
36621             box.height += this.split.el.getHeight();
36622         }
36623         return box;
36624     },
36625     
36626     updateBox : function(box){
36627         if(this.split && !this.collapsed){
36628             box.height -= this.split.el.getHeight();
36629             this.split.el.setLeft(box.x);
36630             this.split.el.setTop(box.y+box.height);
36631             this.split.el.setWidth(box.width);
36632         }
36633         if(this.collapsed){
36634             this.updateBody(box.width, null);
36635         }
36636         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36637     }
36638 });
36639
36640
36641
36642
36643
36644 Roo.bootstrap.layout.South = function(config){
36645     config.region = 'south';
36646     config.cursor = 's-resize';
36647     Roo.bootstrap.layout.Split.call(this, config);
36648     if(this.split){
36649         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36650         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36651         this.split.el.addClass("roo-layout-split-v");
36652     }
36653     var size = config.initialSize || config.height;
36654     if(typeof size != "undefined"){
36655         this.el.setHeight(size);
36656     }
36657 };
36658
36659 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36660     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36661     getBox : function(){
36662         if(this.collapsed){
36663             return this.collapsedEl.getBox();
36664         }
36665         var box = this.el.getBox();
36666         if(this.split){
36667             var sh = this.split.el.getHeight();
36668             box.height += sh;
36669             box.y -= sh;
36670         }
36671         return box;
36672     },
36673     
36674     updateBox : function(box){
36675         if(this.split && !this.collapsed){
36676             var sh = this.split.el.getHeight();
36677             box.height -= sh;
36678             box.y += sh;
36679             this.split.el.setLeft(box.x);
36680             this.split.el.setTop(box.y-sh);
36681             this.split.el.setWidth(box.width);
36682         }
36683         if(this.collapsed){
36684             this.updateBody(box.width, null);
36685         }
36686         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36687     }
36688 });
36689
36690 Roo.bootstrap.layout.East = function(config){
36691     config.region = "east";
36692     config.cursor = "e-resize";
36693     Roo.bootstrap.layout.Split.call(this, config);
36694     if(this.split){
36695         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36696         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36697         this.split.el.addClass("roo-layout-split-h");
36698     }
36699     var size = config.initialSize || config.width;
36700     if(typeof size != "undefined"){
36701         this.el.setWidth(size);
36702     }
36703 };
36704 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36705     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36706     getBox : function(){
36707         if(this.collapsed){
36708             return this.collapsedEl.getBox();
36709         }
36710         var box = this.el.getBox();
36711         if(this.split){
36712             var sw = this.split.el.getWidth();
36713             box.width += sw;
36714             box.x -= sw;
36715         }
36716         return box;
36717     },
36718
36719     updateBox : function(box){
36720         if(this.split && !this.collapsed){
36721             var sw = this.split.el.getWidth();
36722             box.width -= sw;
36723             this.split.el.setLeft(box.x);
36724             this.split.el.setTop(box.y);
36725             this.split.el.setHeight(box.height);
36726             box.x += sw;
36727         }
36728         if(this.collapsed){
36729             this.updateBody(null, box.height);
36730         }
36731         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36732     }
36733 });
36734
36735 Roo.bootstrap.layout.West = function(config){
36736     config.region = "west";
36737     config.cursor = "w-resize";
36738     
36739     Roo.bootstrap.layout.Split.call(this, config);
36740     if(this.split){
36741         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36742         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36743         this.split.el.addClass("roo-layout-split-h");
36744     }
36745     
36746 };
36747 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36748     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36749     
36750     onRender: function(ctr, pos)
36751     {
36752         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36753         var size = this.config.initialSize || this.config.width;
36754         if(typeof size != "undefined"){
36755             this.el.setWidth(size);
36756         }
36757     },
36758     
36759     getBox : function(){
36760         if(this.collapsed){
36761             return this.collapsedEl.getBox();
36762         }
36763         var box = this.el.getBox();
36764         if(this.split){
36765             box.width += this.split.el.getWidth();
36766         }
36767         return box;
36768     },
36769     
36770     updateBox : function(box){
36771         if(this.split && !this.collapsed){
36772             var sw = this.split.el.getWidth();
36773             box.width -= sw;
36774             this.split.el.setLeft(box.x+box.width);
36775             this.split.el.setTop(box.y);
36776             this.split.el.setHeight(box.height);
36777         }
36778         if(this.collapsed){
36779             this.updateBody(null, box.height);
36780         }
36781         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36782     }
36783 });
36784 Roo.namespace("Roo.bootstrap.panel");/*
36785  * Based on:
36786  * Ext JS Library 1.1.1
36787  * Copyright(c) 2006-2007, Ext JS, LLC.
36788  *
36789  * Originally Released Under LGPL - original licence link has changed is not relivant.
36790  *
36791  * Fork - LGPL
36792  * <script type="text/javascript">
36793  */
36794 /**
36795  * @class Roo.ContentPanel
36796  * @extends Roo.util.Observable
36797  * A basic ContentPanel element.
36798  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36799  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36800  * @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
36801  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36802  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36803  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36804  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36805  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36806  * @cfg {String} title          The title for this panel
36807  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36808  * @cfg {String} url            Calls {@link #setUrl} with this value
36809  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36810  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36811  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36812  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36813  * @cfg {Boolean} badges render the badges
36814
36815  * @constructor
36816  * Create a new ContentPanel.
36817  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36818  * @param {String/Object} config A string to set only the title or a config object
36819  * @param {String} content (optional) Set the HTML content for this panel
36820  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36821  */
36822 Roo.bootstrap.panel.Content = function( config){
36823     
36824     this.tpl = config.tpl || false;
36825     
36826     var el = config.el;
36827     var content = config.content;
36828
36829     if(config.autoCreate){ // xtype is available if this is called from factory
36830         el = Roo.id();
36831     }
36832     this.el = Roo.get(el);
36833     if(!this.el && config && config.autoCreate){
36834         if(typeof config.autoCreate == "object"){
36835             if(!config.autoCreate.id){
36836                 config.autoCreate.id = config.id||el;
36837             }
36838             this.el = Roo.DomHelper.append(document.body,
36839                         config.autoCreate, true);
36840         }else{
36841             var elcfg =  {   tag: "div",
36842                             cls: "roo-layout-inactive-content",
36843                             id: config.id||el
36844                             };
36845             if (config.html) {
36846                 elcfg.html = config.html;
36847                 
36848             }
36849                         
36850             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36851         }
36852     } 
36853     this.closable = false;
36854     this.loaded = false;
36855     this.active = false;
36856    
36857       
36858     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36859         
36860         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36861         
36862         this.wrapEl = this.el; //this.el.wrap();
36863         var ti = [];
36864         if (config.toolbar.items) {
36865             ti = config.toolbar.items ;
36866             delete config.toolbar.items ;
36867         }
36868         
36869         var nitems = [];
36870         this.toolbar.render(this.wrapEl, 'before');
36871         for(var i =0;i < ti.length;i++) {
36872           //  Roo.log(['add child', items[i]]);
36873             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36874         }
36875         this.toolbar.items = nitems;
36876         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36877         delete config.toolbar;
36878         
36879     }
36880     /*
36881     // xtype created footer. - not sure if will work as we normally have to render first..
36882     if (this.footer && !this.footer.el && this.footer.xtype) {
36883         if (!this.wrapEl) {
36884             this.wrapEl = this.el.wrap();
36885         }
36886     
36887         this.footer.container = this.wrapEl.createChild();
36888          
36889         this.footer = Roo.factory(this.footer, Roo);
36890         
36891     }
36892     */
36893     
36894      if(typeof config == "string"){
36895         this.title = config;
36896     }else{
36897         Roo.apply(this, config);
36898     }
36899     
36900     if(this.resizeEl){
36901         this.resizeEl = Roo.get(this.resizeEl, true);
36902     }else{
36903         this.resizeEl = this.el;
36904     }
36905     // handle view.xtype
36906     
36907  
36908     
36909     
36910     this.addEvents({
36911         /**
36912          * @event activate
36913          * Fires when this panel is activated. 
36914          * @param {Roo.ContentPanel} this
36915          */
36916         "activate" : true,
36917         /**
36918          * @event deactivate
36919          * Fires when this panel is activated. 
36920          * @param {Roo.ContentPanel} this
36921          */
36922         "deactivate" : true,
36923
36924         /**
36925          * @event resize
36926          * Fires when this panel is resized if fitToFrame is true.
36927          * @param {Roo.ContentPanel} this
36928          * @param {Number} width The width after any component adjustments
36929          * @param {Number} height The height after any component adjustments
36930          */
36931         "resize" : true,
36932         
36933          /**
36934          * @event render
36935          * Fires when this tab is created
36936          * @param {Roo.ContentPanel} this
36937          */
36938         "render" : true
36939         
36940         
36941         
36942     });
36943     
36944
36945     
36946     
36947     if(this.autoScroll){
36948         this.resizeEl.setStyle("overflow", "auto");
36949     } else {
36950         // fix randome scrolling
36951         //this.el.on('scroll', function() {
36952         //    Roo.log('fix random scolling');
36953         //    this.scrollTo('top',0); 
36954         //});
36955     }
36956     content = content || this.content;
36957     if(content){
36958         this.setContent(content);
36959     }
36960     if(config && config.url){
36961         this.setUrl(this.url, this.params, this.loadOnce);
36962     }
36963     
36964     
36965     
36966     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36967     
36968     if (this.view && typeof(this.view.xtype) != 'undefined') {
36969         this.view.el = this.el.appendChild(document.createElement("div"));
36970         this.view = Roo.factory(this.view); 
36971         this.view.render  &&  this.view.render(false, '');  
36972     }
36973     
36974     
36975     this.fireEvent('render', this);
36976 };
36977
36978 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36979     
36980     tabTip : '',
36981     
36982     setRegion : function(region){
36983         this.region = region;
36984         this.setActiveClass(region && !this.background);
36985     },
36986     
36987     
36988     setActiveClass: function(state)
36989     {
36990         if(state){
36991            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36992            this.el.setStyle('position','relative');
36993         }else{
36994            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36995            this.el.setStyle('position', 'absolute');
36996         } 
36997     },
36998     
36999     /**
37000      * Returns the toolbar for this Panel if one was configured. 
37001      * @return {Roo.Toolbar} 
37002      */
37003     getToolbar : function(){
37004         return this.toolbar;
37005     },
37006     
37007     setActiveState : function(active)
37008     {
37009         this.active = active;
37010         this.setActiveClass(active);
37011         if(!active){
37012             if(this.fireEvent("deactivate", this) === false){
37013                 return false;
37014             }
37015             return true;
37016         }
37017         this.fireEvent("activate", this);
37018         return true;
37019     },
37020     /**
37021      * Updates this panel's element
37022      * @param {String} content The new content
37023      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37024     */
37025     setContent : function(content, loadScripts){
37026         this.el.update(content, loadScripts);
37027     },
37028
37029     ignoreResize : function(w, h){
37030         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37031             return true;
37032         }else{
37033             this.lastSize = {width: w, height: h};
37034             return false;
37035         }
37036     },
37037     /**
37038      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37039      * @return {Roo.UpdateManager} The UpdateManager
37040      */
37041     getUpdateManager : function(){
37042         return this.el.getUpdateManager();
37043     },
37044      /**
37045      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37046      * @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:
37047 <pre><code>
37048 panel.load({
37049     url: "your-url.php",
37050     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37051     callback: yourFunction,
37052     scope: yourObject, //(optional scope)
37053     discardUrl: false,
37054     nocache: false,
37055     text: "Loading...",
37056     timeout: 30,
37057     scripts: false
37058 });
37059 </code></pre>
37060      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37061      * 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.
37062      * @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}
37063      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37064      * @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.
37065      * @return {Roo.ContentPanel} this
37066      */
37067     load : function(){
37068         var um = this.el.getUpdateManager();
37069         um.update.apply(um, arguments);
37070         return this;
37071     },
37072
37073
37074     /**
37075      * 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.
37076      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37077      * @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)
37078      * @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)
37079      * @return {Roo.UpdateManager} The UpdateManager
37080      */
37081     setUrl : function(url, params, loadOnce){
37082         if(this.refreshDelegate){
37083             this.removeListener("activate", this.refreshDelegate);
37084         }
37085         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37086         this.on("activate", this.refreshDelegate);
37087         return this.el.getUpdateManager();
37088     },
37089     
37090     _handleRefresh : function(url, params, loadOnce){
37091         if(!loadOnce || !this.loaded){
37092             var updater = this.el.getUpdateManager();
37093             updater.update(url, params, this._setLoaded.createDelegate(this));
37094         }
37095     },
37096     
37097     _setLoaded : function(){
37098         this.loaded = true;
37099     }, 
37100     
37101     /**
37102      * Returns this panel's id
37103      * @return {String} 
37104      */
37105     getId : function(){
37106         return this.el.id;
37107     },
37108     
37109     /** 
37110      * Returns this panel's element - used by regiosn to add.
37111      * @return {Roo.Element} 
37112      */
37113     getEl : function(){
37114         return this.wrapEl || this.el;
37115     },
37116     
37117    
37118     
37119     adjustForComponents : function(width, height)
37120     {
37121         //Roo.log('adjustForComponents ');
37122         if(this.resizeEl != this.el){
37123             width -= this.el.getFrameWidth('lr');
37124             height -= this.el.getFrameWidth('tb');
37125         }
37126         if(this.toolbar){
37127             var te = this.toolbar.getEl();
37128             te.setWidth(width);
37129             height -= te.getHeight();
37130         }
37131         if(this.footer){
37132             var te = this.footer.getEl();
37133             te.setWidth(width);
37134             height -= te.getHeight();
37135         }
37136         
37137         
37138         if(this.adjustments){
37139             width += this.adjustments[0];
37140             height += this.adjustments[1];
37141         }
37142         return {"width": width, "height": height};
37143     },
37144     
37145     setSize : function(width, height){
37146         if(this.fitToFrame && !this.ignoreResize(width, height)){
37147             if(this.fitContainer && this.resizeEl != this.el){
37148                 this.el.setSize(width, height);
37149             }
37150             var size = this.adjustForComponents(width, height);
37151             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37152             this.fireEvent('resize', this, size.width, size.height);
37153         }
37154     },
37155     
37156     /**
37157      * Returns this panel's title
37158      * @return {String} 
37159      */
37160     getTitle : function(){
37161         
37162         if (typeof(this.title) != 'object') {
37163             return this.title;
37164         }
37165         
37166         var t = '';
37167         for (var k in this.title) {
37168             if (!this.title.hasOwnProperty(k)) {
37169                 continue;
37170             }
37171             
37172             if (k.indexOf('-') >= 0) {
37173                 var s = k.split('-');
37174                 for (var i = 0; i<s.length; i++) {
37175                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37176                 }
37177             } else {
37178                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37179             }
37180         }
37181         return t;
37182     },
37183     
37184     /**
37185      * Set this panel's title
37186      * @param {String} title
37187      */
37188     setTitle : function(title){
37189         this.title = title;
37190         if(this.region){
37191             this.region.updatePanelTitle(this, title);
37192         }
37193     },
37194     
37195     /**
37196      * Returns true is this panel was configured to be closable
37197      * @return {Boolean} 
37198      */
37199     isClosable : function(){
37200         return this.closable;
37201     },
37202     
37203     beforeSlide : function(){
37204         this.el.clip();
37205         this.resizeEl.clip();
37206     },
37207     
37208     afterSlide : function(){
37209         this.el.unclip();
37210         this.resizeEl.unclip();
37211     },
37212     
37213     /**
37214      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37215      *   Will fail silently if the {@link #setUrl} method has not been called.
37216      *   This does not activate the panel, just updates its content.
37217      */
37218     refresh : function(){
37219         if(this.refreshDelegate){
37220            this.loaded = false;
37221            this.refreshDelegate();
37222         }
37223     },
37224     
37225     /**
37226      * Destroys this panel
37227      */
37228     destroy : function(){
37229         this.el.removeAllListeners();
37230         var tempEl = document.createElement("span");
37231         tempEl.appendChild(this.el.dom);
37232         tempEl.innerHTML = "";
37233         this.el.remove();
37234         this.el = null;
37235     },
37236     
37237     /**
37238      * form - if the content panel contains a form - this is a reference to it.
37239      * @type {Roo.form.Form}
37240      */
37241     form : false,
37242     /**
37243      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37244      *    This contains a reference to it.
37245      * @type {Roo.View}
37246      */
37247     view : false,
37248     
37249       /**
37250      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37251      * <pre><code>
37252
37253 layout.addxtype({
37254        xtype : 'Form',
37255        items: [ .... ]
37256    }
37257 );
37258
37259 </code></pre>
37260      * @param {Object} cfg Xtype definition of item to add.
37261      */
37262     
37263     
37264     getChildContainer: function () {
37265         return this.getEl();
37266     }
37267     
37268     
37269     /*
37270         var  ret = new Roo.factory(cfg);
37271         return ret;
37272         
37273         
37274         // add form..
37275         if (cfg.xtype.match(/^Form$/)) {
37276             
37277             var el;
37278             //if (this.footer) {
37279             //    el = this.footer.container.insertSibling(false, 'before');
37280             //} else {
37281                 el = this.el.createChild();
37282             //}
37283
37284             this.form = new  Roo.form.Form(cfg);
37285             
37286             
37287             if ( this.form.allItems.length) {
37288                 this.form.render(el.dom);
37289             }
37290             return this.form;
37291         }
37292         // should only have one of theses..
37293         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37294             // views.. should not be just added - used named prop 'view''
37295             
37296             cfg.el = this.el.appendChild(document.createElement("div"));
37297             // factory?
37298             
37299             var ret = new Roo.factory(cfg);
37300              
37301              ret.render && ret.render(false, ''); // render blank..
37302             this.view = ret;
37303             return ret;
37304         }
37305         return false;
37306     }
37307     \*/
37308 });
37309  
37310 /**
37311  * @class Roo.bootstrap.panel.Grid
37312  * @extends Roo.bootstrap.panel.Content
37313  * @constructor
37314  * Create a new GridPanel.
37315  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37316  * @param {Object} config A the config object
37317   
37318  */
37319
37320
37321
37322 Roo.bootstrap.panel.Grid = function(config)
37323 {
37324     
37325       
37326     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37327         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37328
37329     config.el = this.wrapper;
37330     //this.el = this.wrapper;
37331     
37332       if (config.container) {
37333         // ctor'ed from a Border/panel.grid
37334         
37335         
37336         this.wrapper.setStyle("overflow", "hidden");
37337         this.wrapper.addClass('roo-grid-container');
37338
37339     }
37340     
37341     
37342     if(config.toolbar){
37343         var tool_el = this.wrapper.createChild();    
37344         this.toolbar = Roo.factory(config.toolbar);
37345         var ti = [];
37346         if (config.toolbar.items) {
37347             ti = config.toolbar.items ;
37348             delete config.toolbar.items ;
37349         }
37350         
37351         var nitems = [];
37352         this.toolbar.render(tool_el);
37353         for(var i =0;i < ti.length;i++) {
37354           //  Roo.log(['add child', items[i]]);
37355             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37356         }
37357         this.toolbar.items = nitems;
37358         
37359         delete config.toolbar;
37360     }
37361     
37362     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37363     config.grid.scrollBody = true;;
37364     config.grid.monitorWindowResize = false; // turn off autosizing
37365     config.grid.autoHeight = false;
37366     config.grid.autoWidth = false;
37367     
37368     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37369     
37370     if (config.background) {
37371         // render grid on panel activation (if panel background)
37372         this.on('activate', function(gp) {
37373             if (!gp.grid.rendered) {
37374                 gp.grid.render(this.wrapper);
37375                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37376             }
37377         });
37378             
37379     } else {
37380         this.grid.render(this.wrapper);
37381         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37382
37383     }
37384     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37385     // ??? needed ??? config.el = this.wrapper;
37386     
37387     
37388     
37389   
37390     // xtype created footer. - not sure if will work as we normally have to render first..
37391     if (this.footer && !this.footer.el && this.footer.xtype) {
37392         
37393         var ctr = this.grid.getView().getFooterPanel(true);
37394         this.footer.dataSource = this.grid.dataSource;
37395         this.footer = Roo.factory(this.footer, Roo);
37396         this.footer.render(ctr);
37397         
37398     }
37399     
37400     
37401     
37402     
37403      
37404 };
37405
37406 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37407     getId : function(){
37408         return this.grid.id;
37409     },
37410     
37411     /**
37412      * Returns the grid for this panel
37413      * @return {Roo.bootstrap.Table} 
37414      */
37415     getGrid : function(){
37416         return this.grid;    
37417     },
37418     
37419     setSize : function(width, height){
37420         if(!this.ignoreResize(width, height)){
37421             var grid = this.grid;
37422             var size = this.adjustForComponents(width, height);
37423             var gridel = grid.getGridEl();
37424             gridel.setSize(size.width, size.height);
37425             /*
37426             var thd = grid.getGridEl().select('thead',true).first();
37427             var tbd = grid.getGridEl().select('tbody', true).first();
37428             if (tbd) {
37429                 tbd.setSize(width, height - thd.getHeight());
37430             }
37431             */
37432             grid.autoSize();
37433         }
37434     },
37435      
37436     
37437     
37438     beforeSlide : function(){
37439         this.grid.getView().scroller.clip();
37440     },
37441     
37442     afterSlide : function(){
37443         this.grid.getView().scroller.unclip();
37444     },
37445     
37446     destroy : function(){
37447         this.grid.destroy();
37448         delete this.grid;
37449         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37450     }
37451 });
37452
37453 /**
37454  * @class Roo.bootstrap.panel.Nest
37455  * @extends Roo.bootstrap.panel.Content
37456  * @constructor
37457  * Create a new Panel, that can contain a layout.Border.
37458  * 
37459  * 
37460  * @param {Roo.BorderLayout} layout The layout for this panel
37461  * @param {String/Object} config A string to set only the title or a config object
37462  */
37463 Roo.bootstrap.panel.Nest = function(config)
37464 {
37465     // construct with only one argument..
37466     /* FIXME - implement nicer consturctors
37467     if (layout.layout) {
37468         config = layout;
37469         layout = config.layout;
37470         delete config.layout;
37471     }
37472     if (layout.xtype && !layout.getEl) {
37473         // then layout needs constructing..
37474         layout = Roo.factory(layout, Roo);
37475     }
37476     */
37477     
37478     config.el =  config.layout.getEl();
37479     
37480     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37481     
37482     config.layout.monitorWindowResize = false; // turn off autosizing
37483     this.layout = config.layout;
37484     this.layout.getEl().addClass("roo-layout-nested-layout");
37485     
37486     
37487     
37488     
37489 };
37490
37491 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37492
37493     setSize : function(width, height){
37494         if(!this.ignoreResize(width, height)){
37495             var size = this.adjustForComponents(width, height);
37496             var el = this.layout.getEl();
37497             if (size.height < 1) {
37498                 el.setWidth(size.width);   
37499             } else {
37500                 el.setSize(size.width, size.height);
37501             }
37502             var touch = el.dom.offsetWidth;
37503             this.layout.layout();
37504             // ie requires a double layout on the first pass
37505             if(Roo.isIE && !this.initialized){
37506                 this.initialized = true;
37507                 this.layout.layout();
37508             }
37509         }
37510     },
37511     
37512     // activate all subpanels if not currently active..
37513     
37514     setActiveState : function(active){
37515         this.active = active;
37516         this.setActiveClass(active);
37517         
37518         if(!active){
37519             this.fireEvent("deactivate", this);
37520             return;
37521         }
37522         
37523         this.fireEvent("activate", this);
37524         // not sure if this should happen before or after..
37525         if (!this.layout) {
37526             return; // should not happen..
37527         }
37528         var reg = false;
37529         for (var r in this.layout.regions) {
37530             reg = this.layout.getRegion(r);
37531             if (reg.getActivePanel()) {
37532                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37533                 reg.setActivePanel(reg.getActivePanel());
37534                 continue;
37535             }
37536             if (!reg.panels.length) {
37537                 continue;
37538             }
37539             reg.showPanel(reg.getPanel(0));
37540         }
37541         
37542         
37543         
37544         
37545     },
37546     
37547     /**
37548      * Returns the nested BorderLayout for this panel
37549      * @return {Roo.BorderLayout} 
37550      */
37551     getLayout : function(){
37552         return this.layout;
37553     },
37554     
37555      /**
37556      * Adds a xtype elements to the layout of the nested panel
37557      * <pre><code>
37558
37559 panel.addxtype({
37560        xtype : 'ContentPanel',
37561        region: 'west',
37562        items: [ .... ]
37563    }
37564 );
37565
37566 panel.addxtype({
37567         xtype : 'NestedLayoutPanel',
37568         region: 'west',
37569         layout: {
37570            center: { },
37571            west: { }   
37572         },
37573         items : [ ... list of content panels or nested layout panels.. ]
37574    }
37575 );
37576 </code></pre>
37577      * @param {Object} cfg Xtype definition of item to add.
37578      */
37579     addxtype : function(cfg) {
37580         return this.layout.addxtype(cfg);
37581     
37582     }
37583 });        /*
37584  * Based on:
37585  * Ext JS Library 1.1.1
37586  * Copyright(c) 2006-2007, Ext JS, LLC.
37587  *
37588  * Originally Released Under LGPL - original licence link has changed is not relivant.
37589  *
37590  * Fork - LGPL
37591  * <script type="text/javascript">
37592  */
37593 /**
37594  * @class Roo.TabPanel
37595  * @extends Roo.util.Observable
37596  * A lightweight tab container.
37597  * <br><br>
37598  * Usage:
37599  * <pre><code>
37600 // basic tabs 1, built from existing content
37601 var tabs = new Roo.TabPanel("tabs1");
37602 tabs.addTab("script", "View Script");
37603 tabs.addTab("markup", "View Markup");
37604 tabs.activate("script");
37605
37606 // more advanced tabs, built from javascript
37607 var jtabs = new Roo.TabPanel("jtabs");
37608 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37609
37610 // set up the UpdateManager
37611 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37612 var updater = tab2.getUpdateManager();
37613 updater.setDefaultUrl("ajax1.htm");
37614 tab2.on('activate', updater.refresh, updater, true);
37615
37616 // Use setUrl for Ajax loading
37617 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37618 tab3.setUrl("ajax2.htm", null, true);
37619
37620 // Disabled tab
37621 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37622 tab4.disable();
37623
37624 jtabs.activate("jtabs-1");
37625  * </code></pre>
37626  * @constructor
37627  * Create a new TabPanel.
37628  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37629  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37630  */
37631 Roo.bootstrap.panel.Tabs = function(config){
37632     /**
37633     * The container element for this TabPanel.
37634     * @type Roo.Element
37635     */
37636     this.el = Roo.get(config.el);
37637     delete config.el;
37638     if(config){
37639         if(typeof config == "boolean"){
37640             this.tabPosition = config ? "bottom" : "top";
37641         }else{
37642             Roo.apply(this, config);
37643         }
37644     }
37645     
37646     if(this.tabPosition == "bottom"){
37647         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37648         this.el.addClass("roo-tabs-bottom");
37649     }
37650     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37651     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37652     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37653     if(Roo.isIE){
37654         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37655     }
37656     if(this.tabPosition != "bottom"){
37657         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37658          * @type Roo.Element
37659          */
37660         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37661         this.el.addClass("roo-tabs-top");
37662     }
37663     this.items = [];
37664
37665     this.bodyEl.setStyle("position", "relative");
37666
37667     this.active = null;
37668     this.activateDelegate = this.activate.createDelegate(this);
37669
37670     this.addEvents({
37671         /**
37672          * @event tabchange
37673          * Fires when the active tab changes
37674          * @param {Roo.TabPanel} this
37675          * @param {Roo.TabPanelItem} activePanel The new active tab
37676          */
37677         "tabchange": true,
37678         /**
37679          * @event beforetabchange
37680          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37681          * @param {Roo.TabPanel} this
37682          * @param {Object} e Set cancel to true on this object to cancel the tab change
37683          * @param {Roo.TabPanelItem} tab The tab being changed to
37684          */
37685         "beforetabchange" : true
37686     });
37687
37688     Roo.EventManager.onWindowResize(this.onResize, this);
37689     this.cpad = this.el.getPadding("lr");
37690     this.hiddenCount = 0;
37691
37692
37693     // toolbar on the tabbar support...
37694     if (this.toolbar) {
37695         alert("no toolbar support yet");
37696         this.toolbar  = false;
37697         /*
37698         var tcfg = this.toolbar;
37699         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37700         this.toolbar = new Roo.Toolbar(tcfg);
37701         if (Roo.isSafari) {
37702             var tbl = tcfg.container.child('table', true);
37703             tbl.setAttribute('width', '100%');
37704         }
37705         */
37706         
37707     }
37708    
37709
37710
37711     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37712 };
37713
37714 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37715     /*
37716      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37717      */
37718     tabPosition : "top",
37719     /*
37720      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37721      */
37722     currentTabWidth : 0,
37723     /*
37724      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37725      */
37726     minTabWidth : 40,
37727     /*
37728      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37729      */
37730     maxTabWidth : 250,
37731     /*
37732      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37733      */
37734     preferredTabWidth : 175,
37735     /*
37736      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37737      */
37738     resizeTabs : false,
37739     /*
37740      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37741      */
37742     monitorResize : true,
37743     /*
37744      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37745      */
37746     toolbar : false,
37747
37748     /**
37749      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37750      * @param {String} id The id of the div to use <b>or create</b>
37751      * @param {String} text The text for the tab
37752      * @param {String} content (optional) Content to put in the TabPanelItem body
37753      * @param {Boolean} closable (optional) True to create a close icon on the tab
37754      * @return {Roo.TabPanelItem} The created TabPanelItem
37755      */
37756     addTab : function(id, text, content, closable, tpl)
37757     {
37758         var item = new Roo.bootstrap.panel.TabItem({
37759             panel: this,
37760             id : id,
37761             text : text,
37762             closable : closable,
37763             tpl : tpl
37764         });
37765         this.addTabItem(item);
37766         if(content){
37767             item.setContent(content);
37768         }
37769         return item;
37770     },
37771
37772     /**
37773      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37774      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37775      * @return {Roo.TabPanelItem}
37776      */
37777     getTab : function(id){
37778         return this.items[id];
37779     },
37780
37781     /**
37782      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37783      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37784      */
37785     hideTab : function(id){
37786         var t = this.items[id];
37787         if(!t.isHidden()){
37788            t.setHidden(true);
37789            this.hiddenCount++;
37790            this.autoSizeTabs();
37791         }
37792     },
37793
37794     /**
37795      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37796      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37797      */
37798     unhideTab : function(id){
37799         var t = this.items[id];
37800         if(t.isHidden()){
37801            t.setHidden(false);
37802            this.hiddenCount--;
37803            this.autoSizeTabs();
37804         }
37805     },
37806
37807     /**
37808      * Adds an existing {@link Roo.TabPanelItem}.
37809      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37810      */
37811     addTabItem : function(item){
37812         this.items[item.id] = item;
37813         this.items.push(item);
37814       //  if(this.resizeTabs){
37815     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37816   //         this.autoSizeTabs();
37817 //        }else{
37818 //            item.autoSize();
37819        // }
37820     },
37821
37822     /**
37823      * Removes a {@link Roo.TabPanelItem}.
37824      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37825      */
37826     removeTab : function(id){
37827         var items = this.items;
37828         var tab = items[id];
37829         if(!tab) { return; }
37830         var index = items.indexOf(tab);
37831         if(this.active == tab && items.length > 1){
37832             var newTab = this.getNextAvailable(index);
37833             if(newTab) {
37834                 newTab.activate();
37835             }
37836         }
37837         this.stripEl.dom.removeChild(tab.pnode.dom);
37838         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37839             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37840         }
37841         items.splice(index, 1);
37842         delete this.items[tab.id];
37843         tab.fireEvent("close", tab);
37844         tab.purgeListeners();
37845         this.autoSizeTabs();
37846     },
37847
37848     getNextAvailable : function(start){
37849         var items = this.items;
37850         var index = start;
37851         // look for a next tab that will slide over to
37852         // replace the one being removed
37853         while(index < items.length){
37854             var item = items[++index];
37855             if(item && !item.isHidden()){
37856                 return item;
37857             }
37858         }
37859         // if one isn't found select the previous tab (on the left)
37860         index = start;
37861         while(index >= 0){
37862             var item = items[--index];
37863             if(item && !item.isHidden()){
37864                 return item;
37865             }
37866         }
37867         return null;
37868     },
37869
37870     /**
37871      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37872      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37873      */
37874     disableTab : function(id){
37875         var tab = this.items[id];
37876         if(tab && this.active != tab){
37877             tab.disable();
37878         }
37879     },
37880
37881     /**
37882      * Enables a {@link Roo.TabPanelItem} that is disabled.
37883      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37884      */
37885     enableTab : function(id){
37886         var tab = this.items[id];
37887         tab.enable();
37888     },
37889
37890     /**
37891      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37892      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37893      * @return {Roo.TabPanelItem} The TabPanelItem.
37894      */
37895     activate : function(id){
37896         var tab = this.items[id];
37897         if(!tab){
37898             return null;
37899         }
37900         if(tab == this.active || tab.disabled){
37901             return tab;
37902         }
37903         var e = {};
37904         this.fireEvent("beforetabchange", this, e, tab);
37905         if(e.cancel !== true && !tab.disabled){
37906             if(this.active){
37907                 this.active.hide();
37908             }
37909             this.active = this.items[id];
37910             this.active.show();
37911             this.fireEvent("tabchange", this, this.active);
37912         }
37913         return tab;
37914     },
37915
37916     /**
37917      * Gets the active {@link Roo.TabPanelItem}.
37918      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37919      */
37920     getActiveTab : function(){
37921         return this.active;
37922     },
37923
37924     /**
37925      * Updates the tab body element to fit the height of the container element
37926      * for overflow scrolling
37927      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37928      */
37929     syncHeight : function(targetHeight){
37930         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37931         var bm = this.bodyEl.getMargins();
37932         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37933         this.bodyEl.setHeight(newHeight);
37934         return newHeight;
37935     },
37936
37937     onResize : function(){
37938         if(this.monitorResize){
37939             this.autoSizeTabs();
37940         }
37941     },
37942
37943     /**
37944      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37945      */
37946     beginUpdate : function(){
37947         this.updating = true;
37948     },
37949
37950     /**
37951      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37952      */
37953     endUpdate : function(){
37954         this.updating = false;
37955         this.autoSizeTabs();
37956     },
37957
37958     /**
37959      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37960      */
37961     autoSizeTabs : function(){
37962         var count = this.items.length;
37963         var vcount = count - this.hiddenCount;
37964         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37965             return;
37966         }
37967         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37968         var availWidth = Math.floor(w / vcount);
37969         var b = this.stripBody;
37970         if(b.getWidth() > w){
37971             var tabs = this.items;
37972             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37973             if(availWidth < this.minTabWidth){
37974                 /*if(!this.sleft){    // incomplete scrolling code
37975                     this.createScrollButtons();
37976                 }
37977                 this.showScroll();
37978                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37979             }
37980         }else{
37981             if(this.currentTabWidth < this.preferredTabWidth){
37982                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37983             }
37984         }
37985     },
37986
37987     /**
37988      * Returns the number of tabs in this TabPanel.
37989      * @return {Number}
37990      */
37991      getCount : function(){
37992          return this.items.length;
37993      },
37994
37995     /**
37996      * Resizes all the tabs to the passed width
37997      * @param {Number} The new width
37998      */
37999     setTabWidth : function(width){
38000         this.currentTabWidth = width;
38001         for(var i = 0, len = this.items.length; i < len; i++) {
38002                 if(!this.items[i].isHidden()) {
38003                 this.items[i].setWidth(width);
38004             }
38005         }
38006     },
38007
38008     /**
38009      * Destroys this TabPanel
38010      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38011      */
38012     destroy : function(removeEl){
38013         Roo.EventManager.removeResizeListener(this.onResize, this);
38014         for(var i = 0, len = this.items.length; i < len; i++){
38015             this.items[i].purgeListeners();
38016         }
38017         if(removeEl === true){
38018             this.el.update("");
38019             this.el.remove();
38020         }
38021     },
38022     
38023     createStrip : function(container)
38024     {
38025         var strip = document.createElement("nav");
38026         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38027         container.appendChild(strip);
38028         return strip;
38029     },
38030     
38031     createStripList : function(strip)
38032     {
38033         // div wrapper for retard IE
38034         // returns the "tr" element.
38035         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38036         //'<div class="x-tabs-strip-wrap">'+
38037           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38038           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38039         return strip.firstChild; //.firstChild.firstChild.firstChild;
38040     },
38041     createBody : function(container)
38042     {
38043         var body = document.createElement("div");
38044         Roo.id(body, "tab-body");
38045         //Roo.fly(body).addClass("x-tabs-body");
38046         Roo.fly(body).addClass("tab-content");
38047         container.appendChild(body);
38048         return body;
38049     },
38050     createItemBody :function(bodyEl, id){
38051         var body = Roo.getDom(id);
38052         if(!body){
38053             body = document.createElement("div");
38054             body.id = id;
38055         }
38056         //Roo.fly(body).addClass("x-tabs-item-body");
38057         Roo.fly(body).addClass("tab-pane");
38058          bodyEl.insertBefore(body, bodyEl.firstChild);
38059         return body;
38060     },
38061     /** @private */
38062     createStripElements :  function(stripEl, text, closable, tpl)
38063     {
38064         var td = document.createElement("li"); // was td..
38065         
38066         
38067         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38068         
38069         
38070         stripEl.appendChild(td);
38071         /*if(closable){
38072             td.className = "x-tabs-closable";
38073             if(!this.closeTpl){
38074                 this.closeTpl = new Roo.Template(
38075                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38076                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38077                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38078                 );
38079             }
38080             var el = this.closeTpl.overwrite(td, {"text": text});
38081             var close = el.getElementsByTagName("div")[0];
38082             var inner = el.getElementsByTagName("em")[0];
38083             return {"el": el, "close": close, "inner": inner};
38084         } else {
38085         */
38086         // not sure what this is..
38087 //            if(!this.tabTpl){
38088                 //this.tabTpl = new Roo.Template(
38089                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38090                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38091                 //);
38092 //                this.tabTpl = new Roo.Template(
38093 //                   '<a href="#">' +
38094 //                   '<span unselectable="on"' +
38095 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38096 //                            ' >{text}</span></a>'
38097 //                );
38098 //                
38099 //            }
38100
38101
38102             var template = tpl || this.tabTpl || false;
38103             
38104             if(!template){
38105                 
38106                 template = new Roo.Template(
38107                    '<a href="#">' +
38108                    '<span unselectable="on"' +
38109                             (this.disableTooltips ? '' : ' title="{text}"') +
38110                             ' >{text}</span></a>'
38111                 );
38112             }
38113             
38114             switch (typeof(template)) {
38115                 case 'object' :
38116                     break;
38117                 case 'string' :
38118                     template = new Roo.Template(template);
38119                     break;
38120                 default :
38121                     break;
38122             }
38123             
38124             var el = template.overwrite(td, {"text": text});
38125             
38126             var inner = el.getElementsByTagName("span")[0];
38127             
38128             return {"el": el, "inner": inner};
38129             
38130     }
38131         
38132     
38133 });
38134
38135 /**
38136  * @class Roo.TabPanelItem
38137  * @extends Roo.util.Observable
38138  * Represents an individual item (tab plus body) in a TabPanel.
38139  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38140  * @param {String} id The id of this TabPanelItem
38141  * @param {String} text The text for the tab of this TabPanelItem
38142  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38143  */
38144 Roo.bootstrap.panel.TabItem = function(config){
38145     /**
38146      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38147      * @type Roo.TabPanel
38148      */
38149     this.tabPanel = config.panel;
38150     /**
38151      * The id for this TabPanelItem
38152      * @type String
38153      */
38154     this.id = config.id;
38155     /** @private */
38156     this.disabled = false;
38157     /** @private */
38158     this.text = config.text;
38159     /** @private */
38160     this.loaded = false;
38161     this.closable = config.closable;
38162
38163     /**
38164      * The body element for this TabPanelItem.
38165      * @type Roo.Element
38166      */
38167     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38168     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38169     this.bodyEl.setStyle("display", "block");
38170     this.bodyEl.setStyle("zoom", "1");
38171     //this.hideAction();
38172
38173     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38174     /** @private */
38175     this.el = Roo.get(els.el);
38176     this.inner = Roo.get(els.inner, true);
38177     this.textEl = Roo.get(this.el.dom.firstChild, true);
38178     this.pnode = Roo.get(els.el.parentNode, true);
38179 //    this.el.on("mousedown", this.onTabMouseDown, this);
38180     this.el.on("click", this.onTabClick, this);
38181     /** @private */
38182     if(config.closable){
38183         var c = Roo.get(els.close, true);
38184         c.dom.title = this.closeText;
38185         c.addClassOnOver("close-over");
38186         c.on("click", this.closeClick, this);
38187      }
38188
38189     this.addEvents({
38190          /**
38191          * @event activate
38192          * Fires when this tab becomes the active tab.
38193          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38194          * @param {Roo.TabPanelItem} this
38195          */
38196         "activate": true,
38197         /**
38198          * @event beforeclose
38199          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38200          * @param {Roo.TabPanelItem} this
38201          * @param {Object} e Set cancel to true on this object to cancel the close.
38202          */
38203         "beforeclose": true,
38204         /**
38205          * @event close
38206          * Fires when this tab is closed.
38207          * @param {Roo.TabPanelItem} this
38208          */
38209          "close": true,
38210         /**
38211          * @event deactivate
38212          * Fires when this tab is no longer the active tab.
38213          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38214          * @param {Roo.TabPanelItem} this
38215          */
38216          "deactivate" : true
38217     });
38218     this.hidden = false;
38219
38220     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38221 };
38222
38223 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38224            {
38225     purgeListeners : function(){
38226        Roo.util.Observable.prototype.purgeListeners.call(this);
38227        this.el.removeAllListeners();
38228     },
38229     /**
38230      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38231      */
38232     show : function(){
38233         this.pnode.addClass("active");
38234         this.showAction();
38235         if(Roo.isOpera){
38236             this.tabPanel.stripWrap.repaint();
38237         }
38238         this.fireEvent("activate", this.tabPanel, this);
38239     },
38240
38241     /**
38242      * Returns true if this tab is the active tab.
38243      * @return {Boolean}
38244      */
38245     isActive : function(){
38246         return this.tabPanel.getActiveTab() == this;
38247     },
38248
38249     /**
38250      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38251      */
38252     hide : function(){
38253         this.pnode.removeClass("active");
38254         this.hideAction();
38255         this.fireEvent("deactivate", this.tabPanel, this);
38256     },
38257
38258     hideAction : function(){
38259         this.bodyEl.hide();
38260         this.bodyEl.setStyle("position", "absolute");
38261         this.bodyEl.setLeft("-20000px");
38262         this.bodyEl.setTop("-20000px");
38263     },
38264
38265     showAction : function(){
38266         this.bodyEl.setStyle("position", "relative");
38267         this.bodyEl.setTop("");
38268         this.bodyEl.setLeft("");
38269         this.bodyEl.show();
38270     },
38271
38272     /**
38273      * Set the tooltip for the tab.
38274      * @param {String} tooltip The tab's tooltip
38275      */
38276     setTooltip : function(text){
38277         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38278             this.textEl.dom.qtip = text;
38279             this.textEl.dom.removeAttribute('title');
38280         }else{
38281             this.textEl.dom.title = text;
38282         }
38283     },
38284
38285     onTabClick : function(e){
38286         e.preventDefault();
38287         this.tabPanel.activate(this.id);
38288     },
38289
38290     onTabMouseDown : function(e){
38291         e.preventDefault();
38292         this.tabPanel.activate(this.id);
38293     },
38294 /*
38295     getWidth : function(){
38296         return this.inner.getWidth();
38297     },
38298
38299     setWidth : function(width){
38300         var iwidth = width - this.pnode.getPadding("lr");
38301         this.inner.setWidth(iwidth);
38302         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38303         this.pnode.setWidth(width);
38304     },
38305 */
38306     /**
38307      * Show or hide the tab
38308      * @param {Boolean} hidden True to hide or false to show.
38309      */
38310     setHidden : function(hidden){
38311         this.hidden = hidden;
38312         this.pnode.setStyle("display", hidden ? "none" : "");
38313     },
38314
38315     /**
38316      * Returns true if this tab is "hidden"
38317      * @return {Boolean}
38318      */
38319     isHidden : function(){
38320         return this.hidden;
38321     },
38322
38323     /**
38324      * Returns the text for this tab
38325      * @return {String}
38326      */
38327     getText : function(){
38328         return this.text;
38329     },
38330     /*
38331     autoSize : function(){
38332         //this.el.beginMeasure();
38333         this.textEl.setWidth(1);
38334         /*
38335          *  #2804 [new] Tabs in Roojs
38336          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38337          */
38338         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38339         //this.el.endMeasure();
38340     //},
38341
38342     /**
38343      * Sets the text for the tab (Note: this also sets the tooltip text)
38344      * @param {String} text The tab's text and tooltip
38345      */
38346     setText : function(text){
38347         this.text = text;
38348         this.textEl.update(text);
38349         this.setTooltip(text);
38350         //if(!this.tabPanel.resizeTabs){
38351         //    this.autoSize();
38352         //}
38353     },
38354     /**
38355      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38356      */
38357     activate : function(){
38358         this.tabPanel.activate(this.id);
38359     },
38360
38361     /**
38362      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38363      */
38364     disable : function(){
38365         if(this.tabPanel.active != this){
38366             this.disabled = true;
38367             this.pnode.addClass("disabled");
38368         }
38369     },
38370
38371     /**
38372      * Enables this TabPanelItem if it was previously disabled.
38373      */
38374     enable : function(){
38375         this.disabled = false;
38376         this.pnode.removeClass("disabled");
38377     },
38378
38379     /**
38380      * Sets the content for this TabPanelItem.
38381      * @param {String} content The content
38382      * @param {Boolean} loadScripts true to look for and load scripts
38383      */
38384     setContent : function(content, loadScripts){
38385         this.bodyEl.update(content, loadScripts);
38386     },
38387
38388     /**
38389      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38390      * @return {Roo.UpdateManager} The UpdateManager
38391      */
38392     getUpdateManager : function(){
38393         return this.bodyEl.getUpdateManager();
38394     },
38395
38396     /**
38397      * Set a URL to be used to load the content for this TabPanelItem.
38398      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38399      * @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)
38400      * @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)
38401      * @return {Roo.UpdateManager} The UpdateManager
38402      */
38403     setUrl : function(url, params, loadOnce){
38404         if(this.refreshDelegate){
38405             this.un('activate', this.refreshDelegate);
38406         }
38407         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38408         this.on("activate", this.refreshDelegate);
38409         return this.bodyEl.getUpdateManager();
38410     },
38411
38412     /** @private */
38413     _handleRefresh : function(url, params, loadOnce){
38414         if(!loadOnce || !this.loaded){
38415             var updater = this.bodyEl.getUpdateManager();
38416             updater.update(url, params, this._setLoaded.createDelegate(this));
38417         }
38418     },
38419
38420     /**
38421      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38422      *   Will fail silently if the setUrl method has not been called.
38423      *   This does not activate the panel, just updates its content.
38424      */
38425     refresh : function(){
38426         if(this.refreshDelegate){
38427            this.loaded = false;
38428            this.refreshDelegate();
38429         }
38430     },
38431
38432     /** @private */
38433     _setLoaded : function(){
38434         this.loaded = true;
38435     },
38436
38437     /** @private */
38438     closeClick : function(e){
38439         var o = {};
38440         e.stopEvent();
38441         this.fireEvent("beforeclose", this, o);
38442         if(o.cancel !== true){
38443             this.tabPanel.removeTab(this.id);
38444         }
38445     },
38446     /**
38447      * The text displayed in the tooltip for the close icon.
38448      * @type String
38449      */
38450     closeText : "Close this tab"
38451 });
38452 /**
38453 *    This script refer to:
38454 *    Title: International Telephone Input
38455 *    Author: Jack O'Connor
38456 *    Code version:  v12.1.12
38457 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38458 **/
38459
38460 Roo.bootstrap.PhoneInputData = function() {
38461     var d = [
38462       [
38463         "Afghanistan (‫افغانستان‬‎)",
38464         "af",
38465         "93"
38466       ],
38467       [
38468         "Albania (Shqipëri)",
38469         "al",
38470         "355"
38471       ],
38472       [
38473         "Algeria (‫الجزائر‬‎)",
38474         "dz",
38475         "213"
38476       ],
38477       [
38478         "American Samoa",
38479         "as",
38480         "1684"
38481       ],
38482       [
38483         "Andorra",
38484         "ad",
38485         "376"
38486       ],
38487       [
38488         "Angola",
38489         "ao",
38490         "244"
38491       ],
38492       [
38493         "Anguilla",
38494         "ai",
38495         "1264"
38496       ],
38497       [
38498         "Antigua and Barbuda",
38499         "ag",
38500         "1268"
38501       ],
38502       [
38503         "Argentina",
38504         "ar",
38505         "54"
38506       ],
38507       [
38508         "Armenia (Հայաստան)",
38509         "am",
38510         "374"
38511       ],
38512       [
38513         "Aruba",
38514         "aw",
38515         "297"
38516       ],
38517       [
38518         "Australia",
38519         "au",
38520         "61",
38521         0
38522       ],
38523       [
38524         "Austria (Österreich)",
38525         "at",
38526         "43"
38527       ],
38528       [
38529         "Azerbaijan (Azərbaycan)",
38530         "az",
38531         "994"
38532       ],
38533       [
38534         "Bahamas",
38535         "bs",
38536         "1242"
38537       ],
38538       [
38539         "Bahrain (‫البحرين‬‎)",
38540         "bh",
38541         "973"
38542       ],
38543       [
38544         "Bangladesh (বাংলাদেশ)",
38545         "bd",
38546         "880"
38547       ],
38548       [
38549         "Barbados",
38550         "bb",
38551         "1246"
38552       ],
38553       [
38554         "Belarus (Беларусь)",
38555         "by",
38556         "375"
38557       ],
38558       [
38559         "Belgium (België)",
38560         "be",
38561         "32"
38562       ],
38563       [
38564         "Belize",
38565         "bz",
38566         "501"
38567       ],
38568       [
38569         "Benin (Bénin)",
38570         "bj",
38571         "229"
38572       ],
38573       [
38574         "Bermuda",
38575         "bm",
38576         "1441"
38577       ],
38578       [
38579         "Bhutan (འབྲུག)",
38580         "bt",
38581         "975"
38582       ],
38583       [
38584         "Bolivia",
38585         "bo",
38586         "591"
38587       ],
38588       [
38589         "Bosnia and Herzegovina (Босна и Херцеговина)",
38590         "ba",
38591         "387"
38592       ],
38593       [
38594         "Botswana",
38595         "bw",
38596         "267"
38597       ],
38598       [
38599         "Brazil (Brasil)",
38600         "br",
38601         "55"
38602       ],
38603       [
38604         "British Indian Ocean Territory",
38605         "io",
38606         "246"
38607       ],
38608       [
38609         "British Virgin Islands",
38610         "vg",
38611         "1284"
38612       ],
38613       [
38614         "Brunei",
38615         "bn",
38616         "673"
38617       ],
38618       [
38619         "Bulgaria (България)",
38620         "bg",
38621         "359"
38622       ],
38623       [
38624         "Burkina Faso",
38625         "bf",
38626         "226"
38627       ],
38628       [
38629         "Burundi (Uburundi)",
38630         "bi",
38631         "257"
38632       ],
38633       [
38634         "Cambodia (កម្ពុជា)",
38635         "kh",
38636         "855"
38637       ],
38638       [
38639         "Cameroon (Cameroun)",
38640         "cm",
38641         "237"
38642       ],
38643       [
38644         "Canada",
38645         "ca",
38646         "1",
38647         1,
38648         ["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"]
38649       ],
38650       [
38651         "Cape Verde (Kabu Verdi)",
38652         "cv",
38653         "238"
38654       ],
38655       [
38656         "Caribbean Netherlands",
38657         "bq",
38658         "599",
38659         1
38660       ],
38661       [
38662         "Cayman Islands",
38663         "ky",
38664         "1345"
38665       ],
38666       [
38667         "Central African Republic (République centrafricaine)",
38668         "cf",
38669         "236"
38670       ],
38671       [
38672         "Chad (Tchad)",
38673         "td",
38674         "235"
38675       ],
38676       [
38677         "Chile",
38678         "cl",
38679         "56"
38680       ],
38681       [
38682         "China (中国)",
38683         "cn",
38684         "86"
38685       ],
38686       [
38687         "Christmas Island",
38688         "cx",
38689         "61",
38690         2
38691       ],
38692       [
38693         "Cocos (Keeling) Islands",
38694         "cc",
38695         "61",
38696         1
38697       ],
38698       [
38699         "Colombia",
38700         "co",
38701         "57"
38702       ],
38703       [
38704         "Comoros (‫جزر القمر‬‎)",
38705         "km",
38706         "269"
38707       ],
38708       [
38709         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38710         "cd",
38711         "243"
38712       ],
38713       [
38714         "Congo (Republic) (Congo-Brazzaville)",
38715         "cg",
38716         "242"
38717       ],
38718       [
38719         "Cook Islands",
38720         "ck",
38721         "682"
38722       ],
38723       [
38724         "Costa Rica",
38725         "cr",
38726         "506"
38727       ],
38728       [
38729         "Côte d’Ivoire",
38730         "ci",
38731         "225"
38732       ],
38733       [
38734         "Croatia (Hrvatska)",
38735         "hr",
38736         "385"
38737       ],
38738       [
38739         "Cuba",
38740         "cu",
38741         "53"
38742       ],
38743       [
38744         "Curaçao",
38745         "cw",
38746         "599",
38747         0
38748       ],
38749       [
38750         "Cyprus (Κύπρος)",
38751         "cy",
38752         "357"
38753       ],
38754       [
38755         "Czech Republic (Česká republika)",
38756         "cz",
38757         "420"
38758       ],
38759       [
38760         "Denmark (Danmark)",
38761         "dk",
38762         "45"
38763       ],
38764       [
38765         "Djibouti",
38766         "dj",
38767         "253"
38768       ],
38769       [
38770         "Dominica",
38771         "dm",
38772         "1767"
38773       ],
38774       [
38775         "Dominican Republic (República Dominicana)",
38776         "do",
38777         "1",
38778         2,
38779         ["809", "829", "849"]
38780       ],
38781       [
38782         "Ecuador",
38783         "ec",
38784         "593"
38785       ],
38786       [
38787         "Egypt (‫مصر‬‎)",
38788         "eg",
38789         "20"
38790       ],
38791       [
38792         "El Salvador",
38793         "sv",
38794         "503"
38795       ],
38796       [
38797         "Equatorial Guinea (Guinea Ecuatorial)",
38798         "gq",
38799         "240"
38800       ],
38801       [
38802         "Eritrea",
38803         "er",
38804         "291"
38805       ],
38806       [
38807         "Estonia (Eesti)",
38808         "ee",
38809         "372"
38810       ],
38811       [
38812         "Ethiopia",
38813         "et",
38814         "251"
38815       ],
38816       [
38817         "Falkland Islands (Islas Malvinas)",
38818         "fk",
38819         "500"
38820       ],
38821       [
38822         "Faroe Islands (Føroyar)",
38823         "fo",
38824         "298"
38825       ],
38826       [
38827         "Fiji",
38828         "fj",
38829         "679"
38830       ],
38831       [
38832         "Finland (Suomi)",
38833         "fi",
38834         "358",
38835         0
38836       ],
38837       [
38838         "France",
38839         "fr",
38840         "33"
38841       ],
38842       [
38843         "French Guiana (Guyane française)",
38844         "gf",
38845         "594"
38846       ],
38847       [
38848         "French Polynesia (Polynésie française)",
38849         "pf",
38850         "689"
38851       ],
38852       [
38853         "Gabon",
38854         "ga",
38855         "241"
38856       ],
38857       [
38858         "Gambia",
38859         "gm",
38860         "220"
38861       ],
38862       [
38863         "Georgia (საქართველო)",
38864         "ge",
38865         "995"
38866       ],
38867       [
38868         "Germany (Deutschland)",
38869         "de",
38870         "49"
38871       ],
38872       [
38873         "Ghana (Gaana)",
38874         "gh",
38875         "233"
38876       ],
38877       [
38878         "Gibraltar",
38879         "gi",
38880         "350"
38881       ],
38882       [
38883         "Greece (Ελλάδα)",
38884         "gr",
38885         "30"
38886       ],
38887       [
38888         "Greenland (Kalaallit Nunaat)",
38889         "gl",
38890         "299"
38891       ],
38892       [
38893         "Grenada",
38894         "gd",
38895         "1473"
38896       ],
38897       [
38898         "Guadeloupe",
38899         "gp",
38900         "590",
38901         0
38902       ],
38903       [
38904         "Guam",
38905         "gu",
38906         "1671"
38907       ],
38908       [
38909         "Guatemala",
38910         "gt",
38911         "502"
38912       ],
38913       [
38914         "Guernsey",
38915         "gg",
38916         "44",
38917         1
38918       ],
38919       [
38920         "Guinea (Guinée)",
38921         "gn",
38922         "224"
38923       ],
38924       [
38925         "Guinea-Bissau (Guiné Bissau)",
38926         "gw",
38927         "245"
38928       ],
38929       [
38930         "Guyana",
38931         "gy",
38932         "592"
38933       ],
38934       [
38935         "Haiti",
38936         "ht",
38937         "509"
38938       ],
38939       [
38940         "Honduras",
38941         "hn",
38942         "504"
38943       ],
38944       [
38945         "Hong Kong (香港)",
38946         "hk",
38947         "852"
38948       ],
38949       [
38950         "Hungary (Magyarország)",
38951         "hu",
38952         "36"
38953       ],
38954       [
38955         "Iceland (Ísland)",
38956         "is",
38957         "354"
38958       ],
38959       [
38960         "India (भारत)",
38961         "in",
38962         "91"
38963       ],
38964       [
38965         "Indonesia",
38966         "id",
38967         "62"
38968       ],
38969       [
38970         "Iran (‫ایران‬‎)",
38971         "ir",
38972         "98"
38973       ],
38974       [
38975         "Iraq (‫العراق‬‎)",
38976         "iq",
38977         "964"
38978       ],
38979       [
38980         "Ireland",
38981         "ie",
38982         "353"
38983       ],
38984       [
38985         "Isle of Man",
38986         "im",
38987         "44",
38988         2
38989       ],
38990       [
38991         "Israel (‫ישראל‬‎)",
38992         "il",
38993         "972"
38994       ],
38995       [
38996         "Italy (Italia)",
38997         "it",
38998         "39",
38999         0
39000       ],
39001       [
39002         "Jamaica",
39003         "jm",
39004         "1876"
39005       ],
39006       [
39007         "Japan (日本)",
39008         "jp",
39009         "81"
39010       ],
39011       [
39012         "Jersey",
39013         "je",
39014         "44",
39015         3
39016       ],
39017       [
39018         "Jordan (‫الأردن‬‎)",
39019         "jo",
39020         "962"
39021       ],
39022       [
39023         "Kazakhstan (Казахстан)",
39024         "kz",
39025         "7",
39026         1
39027       ],
39028       [
39029         "Kenya",
39030         "ke",
39031         "254"
39032       ],
39033       [
39034         "Kiribati",
39035         "ki",
39036         "686"
39037       ],
39038       [
39039         "Kosovo",
39040         "xk",
39041         "383"
39042       ],
39043       [
39044         "Kuwait (‫الكويت‬‎)",
39045         "kw",
39046         "965"
39047       ],
39048       [
39049         "Kyrgyzstan (Кыргызстан)",
39050         "kg",
39051         "996"
39052       ],
39053       [
39054         "Laos (ລາວ)",
39055         "la",
39056         "856"
39057       ],
39058       [
39059         "Latvia (Latvija)",
39060         "lv",
39061         "371"
39062       ],
39063       [
39064         "Lebanon (‫لبنان‬‎)",
39065         "lb",
39066         "961"
39067       ],
39068       [
39069         "Lesotho",
39070         "ls",
39071         "266"
39072       ],
39073       [
39074         "Liberia",
39075         "lr",
39076         "231"
39077       ],
39078       [
39079         "Libya (‫ليبيا‬‎)",
39080         "ly",
39081         "218"
39082       ],
39083       [
39084         "Liechtenstein",
39085         "li",
39086         "423"
39087       ],
39088       [
39089         "Lithuania (Lietuva)",
39090         "lt",
39091         "370"
39092       ],
39093       [
39094         "Luxembourg",
39095         "lu",
39096         "352"
39097       ],
39098       [
39099         "Macau (澳門)",
39100         "mo",
39101         "853"
39102       ],
39103       [
39104         "Macedonia (FYROM) (Македонија)",
39105         "mk",
39106         "389"
39107       ],
39108       [
39109         "Madagascar (Madagasikara)",
39110         "mg",
39111         "261"
39112       ],
39113       [
39114         "Malawi",
39115         "mw",
39116         "265"
39117       ],
39118       [
39119         "Malaysia",
39120         "my",
39121         "60"
39122       ],
39123       [
39124         "Maldives",
39125         "mv",
39126         "960"
39127       ],
39128       [
39129         "Mali",
39130         "ml",
39131         "223"
39132       ],
39133       [
39134         "Malta",
39135         "mt",
39136         "356"
39137       ],
39138       [
39139         "Marshall Islands",
39140         "mh",
39141         "692"
39142       ],
39143       [
39144         "Martinique",
39145         "mq",
39146         "596"
39147       ],
39148       [
39149         "Mauritania (‫موريتانيا‬‎)",
39150         "mr",
39151         "222"
39152       ],
39153       [
39154         "Mauritius (Moris)",
39155         "mu",
39156         "230"
39157       ],
39158       [
39159         "Mayotte",
39160         "yt",
39161         "262",
39162         1
39163       ],
39164       [
39165         "Mexico (México)",
39166         "mx",
39167         "52"
39168       ],
39169       [
39170         "Micronesia",
39171         "fm",
39172         "691"
39173       ],
39174       [
39175         "Moldova (Republica Moldova)",
39176         "md",
39177         "373"
39178       ],
39179       [
39180         "Monaco",
39181         "mc",
39182         "377"
39183       ],
39184       [
39185         "Mongolia (Монгол)",
39186         "mn",
39187         "976"
39188       ],
39189       [
39190         "Montenegro (Crna Gora)",
39191         "me",
39192         "382"
39193       ],
39194       [
39195         "Montserrat",
39196         "ms",
39197         "1664"
39198       ],
39199       [
39200         "Morocco (‫المغرب‬‎)",
39201         "ma",
39202         "212",
39203         0
39204       ],
39205       [
39206         "Mozambique (Moçambique)",
39207         "mz",
39208         "258"
39209       ],
39210       [
39211         "Myanmar (Burma) (မြန်မာ)",
39212         "mm",
39213         "95"
39214       ],
39215       [
39216         "Namibia (Namibië)",
39217         "na",
39218         "264"
39219       ],
39220       [
39221         "Nauru",
39222         "nr",
39223         "674"
39224       ],
39225       [
39226         "Nepal (नेपाल)",
39227         "np",
39228         "977"
39229       ],
39230       [
39231         "Netherlands (Nederland)",
39232         "nl",
39233         "31"
39234       ],
39235       [
39236         "New Caledonia (Nouvelle-Calédonie)",
39237         "nc",
39238         "687"
39239       ],
39240       [
39241         "New Zealand",
39242         "nz",
39243         "64"
39244       ],
39245       [
39246         "Nicaragua",
39247         "ni",
39248         "505"
39249       ],
39250       [
39251         "Niger (Nijar)",
39252         "ne",
39253         "227"
39254       ],
39255       [
39256         "Nigeria",
39257         "ng",
39258         "234"
39259       ],
39260       [
39261         "Niue",
39262         "nu",
39263         "683"
39264       ],
39265       [
39266         "Norfolk Island",
39267         "nf",
39268         "672"
39269       ],
39270       [
39271         "North Korea (조선 민주주의 인민 공화국)",
39272         "kp",
39273         "850"
39274       ],
39275       [
39276         "Northern Mariana Islands",
39277         "mp",
39278         "1670"
39279       ],
39280       [
39281         "Norway (Norge)",
39282         "no",
39283         "47",
39284         0
39285       ],
39286       [
39287         "Oman (‫عُمان‬‎)",
39288         "om",
39289         "968"
39290       ],
39291       [
39292         "Pakistan (‫پاکستان‬‎)",
39293         "pk",
39294         "92"
39295       ],
39296       [
39297         "Palau",
39298         "pw",
39299         "680"
39300       ],
39301       [
39302         "Palestine (‫فلسطين‬‎)",
39303         "ps",
39304         "970"
39305       ],
39306       [
39307         "Panama (Panamá)",
39308         "pa",
39309         "507"
39310       ],
39311       [
39312         "Papua New Guinea",
39313         "pg",
39314         "675"
39315       ],
39316       [
39317         "Paraguay",
39318         "py",
39319         "595"
39320       ],
39321       [
39322         "Peru (Perú)",
39323         "pe",
39324         "51"
39325       ],
39326       [
39327         "Philippines",
39328         "ph",
39329         "63"
39330       ],
39331       [
39332         "Poland (Polska)",
39333         "pl",
39334         "48"
39335       ],
39336       [
39337         "Portugal",
39338         "pt",
39339         "351"
39340       ],
39341       [
39342         "Puerto Rico",
39343         "pr",
39344         "1",
39345         3,
39346         ["787", "939"]
39347       ],
39348       [
39349         "Qatar (‫قطر‬‎)",
39350         "qa",
39351         "974"
39352       ],
39353       [
39354         "Réunion (La Réunion)",
39355         "re",
39356         "262",
39357         0
39358       ],
39359       [
39360         "Romania (România)",
39361         "ro",
39362         "40"
39363       ],
39364       [
39365         "Russia (Россия)",
39366         "ru",
39367         "7",
39368         0
39369       ],
39370       [
39371         "Rwanda",
39372         "rw",
39373         "250"
39374       ],
39375       [
39376         "Saint Barthélemy",
39377         "bl",
39378         "590",
39379         1
39380       ],
39381       [
39382         "Saint Helena",
39383         "sh",
39384         "290"
39385       ],
39386       [
39387         "Saint Kitts and Nevis",
39388         "kn",
39389         "1869"
39390       ],
39391       [
39392         "Saint Lucia",
39393         "lc",
39394         "1758"
39395       ],
39396       [
39397         "Saint Martin (Saint-Martin (partie française))",
39398         "mf",
39399         "590",
39400         2
39401       ],
39402       [
39403         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39404         "pm",
39405         "508"
39406       ],
39407       [
39408         "Saint Vincent and the Grenadines",
39409         "vc",
39410         "1784"
39411       ],
39412       [
39413         "Samoa",
39414         "ws",
39415         "685"
39416       ],
39417       [
39418         "San Marino",
39419         "sm",
39420         "378"
39421       ],
39422       [
39423         "São Tomé and Príncipe (São Tomé e Príncipe)",
39424         "st",
39425         "239"
39426       ],
39427       [
39428         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39429         "sa",
39430         "966"
39431       ],
39432       [
39433         "Senegal (Sénégal)",
39434         "sn",
39435         "221"
39436       ],
39437       [
39438         "Serbia (Србија)",
39439         "rs",
39440         "381"
39441       ],
39442       [
39443         "Seychelles",
39444         "sc",
39445         "248"
39446       ],
39447       [
39448         "Sierra Leone",
39449         "sl",
39450         "232"
39451       ],
39452       [
39453         "Singapore",
39454         "sg",
39455         "65"
39456       ],
39457       [
39458         "Sint Maarten",
39459         "sx",
39460         "1721"
39461       ],
39462       [
39463         "Slovakia (Slovensko)",
39464         "sk",
39465         "421"
39466       ],
39467       [
39468         "Slovenia (Slovenija)",
39469         "si",
39470         "386"
39471       ],
39472       [
39473         "Solomon Islands",
39474         "sb",
39475         "677"
39476       ],
39477       [
39478         "Somalia (Soomaaliya)",
39479         "so",
39480         "252"
39481       ],
39482       [
39483         "South Africa",
39484         "za",
39485         "27"
39486       ],
39487       [
39488         "South Korea (대한민국)",
39489         "kr",
39490         "82"
39491       ],
39492       [
39493         "South Sudan (‫جنوب السودان‬‎)",
39494         "ss",
39495         "211"
39496       ],
39497       [
39498         "Spain (España)",
39499         "es",
39500         "34"
39501       ],
39502       [
39503         "Sri Lanka (ශ්‍රී ලංකාව)",
39504         "lk",
39505         "94"
39506       ],
39507       [
39508         "Sudan (‫السودان‬‎)",
39509         "sd",
39510         "249"
39511       ],
39512       [
39513         "Suriname",
39514         "sr",
39515         "597"
39516       ],
39517       [
39518         "Svalbard and Jan Mayen",
39519         "sj",
39520         "47",
39521         1
39522       ],
39523       [
39524         "Swaziland",
39525         "sz",
39526         "268"
39527       ],
39528       [
39529         "Sweden (Sverige)",
39530         "se",
39531         "46"
39532       ],
39533       [
39534         "Switzerland (Schweiz)",
39535         "ch",
39536         "41"
39537       ],
39538       [
39539         "Syria (‫سوريا‬‎)",
39540         "sy",
39541         "963"
39542       ],
39543       [
39544         "Taiwan (台灣)",
39545         "tw",
39546         "886"
39547       ],
39548       [
39549         "Tajikistan",
39550         "tj",
39551         "992"
39552       ],
39553       [
39554         "Tanzania",
39555         "tz",
39556         "255"
39557       ],
39558       [
39559         "Thailand (ไทย)",
39560         "th",
39561         "66"
39562       ],
39563       [
39564         "Timor-Leste",
39565         "tl",
39566         "670"
39567       ],
39568       [
39569         "Togo",
39570         "tg",
39571         "228"
39572       ],
39573       [
39574         "Tokelau",
39575         "tk",
39576         "690"
39577       ],
39578       [
39579         "Tonga",
39580         "to",
39581         "676"
39582       ],
39583       [
39584         "Trinidad and Tobago",
39585         "tt",
39586         "1868"
39587       ],
39588       [
39589         "Tunisia (‫تونس‬‎)",
39590         "tn",
39591         "216"
39592       ],
39593       [
39594         "Turkey (Türkiye)",
39595         "tr",
39596         "90"
39597       ],
39598       [
39599         "Turkmenistan",
39600         "tm",
39601         "993"
39602       ],
39603       [
39604         "Turks and Caicos Islands",
39605         "tc",
39606         "1649"
39607       ],
39608       [
39609         "Tuvalu",
39610         "tv",
39611         "688"
39612       ],
39613       [
39614         "U.S. Virgin Islands",
39615         "vi",
39616         "1340"
39617       ],
39618       [
39619         "Uganda",
39620         "ug",
39621         "256"
39622       ],
39623       [
39624         "Ukraine (Україна)",
39625         "ua",
39626         "380"
39627       ],
39628       [
39629         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39630         "ae",
39631         "971"
39632       ],
39633       [
39634         "United Kingdom",
39635         "gb",
39636         "44",
39637         0
39638       ],
39639       [
39640         "United States",
39641         "us",
39642         "1",
39643         0
39644       ],
39645       [
39646         "Uruguay",
39647         "uy",
39648         "598"
39649       ],
39650       [
39651         "Uzbekistan (Oʻzbekiston)",
39652         "uz",
39653         "998"
39654       ],
39655       [
39656         "Vanuatu",
39657         "vu",
39658         "678"
39659       ],
39660       [
39661         "Vatican City (Città del Vaticano)",
39662         "va",
39663         "39",
39664         1
39665       ],
39666       [
39667         "Venezuela",
39668         "ve",
39669         "58"
39670       ],
39671       [
39672         "Vietnam (Việt Nam)",
39673         "vn",
39674         "84"
39675       ],
39676       [
39677         "Wallis and Futuna (Wallis-et-Futuna)",
39678         "wf",
39679         "681"
39680       ],
39681       [
39682         "Western Sahara (‫الصحراء الغربية‬‎)",
39683         "eh",
39684         "212",
39685         1
39686       ],
39687       [
39688         "Yemen (‫اليمن‬‎)",
39689         "ye",
39690         "967"
39691       ],
39692       [
39693         "Zambia",
39694         "zm",
39695         "260"
39696       ],
39697       [
39698         "Zimbabwe",
39699         "zw",
39700         "263"
39701       ],
39702       [
39703         "Åland Islands",
39704         "ax",
39705         "358",
39706         1
39707       ]
39708   ];
39709   
39710   return d;
39711 }/**
39712 *    This script refer to:
39713 *    Title: International Telephone Input
39714 *    Author: Jack O'Connor
39715 *    Code version:  v12.1.12
39716 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39717 **/
39718
39719 /**
39720  * @class Roo.bootstrap.PhoneInput
39721  * @extends Roo.bootstrap.TriggerField
39722  * An input with International dial-code selection
39723  
39724  * @cfg {String} defaultDialCode default '+852'
39725  * @cfg {Array} preferedCountries default []
39726   
39727  * @constructor
39728  * Create a new PhoneInput.
39729  * @param {Object} config Configuration options
39730  */
39731
39732 Roo.bootstrap.PhoneInput = function(config) {
39733     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39734 };
39735
39736 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39737         
39738         listWidth: undefined,
39739         
39740         selectedClass: 'active',
39741         
39742         invalidClass : "has-warning",
39743         
39744         validClass: 'has-success',
39745         
39746         allowed: '0123456789',
39747         
39748         /**
39749          * @cfg {String} defaultDialCode The default dial code when initializing the input
39750          */
39751         defaultDialCode: '+852',
39752         
39753         /**
39754          * @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
39755          */
39756         preferedCountries: false,
39757         
39758         getAutoCreate : function()
39759         {
39760             var data = Roo.bootstrap.PhoneInputData();
39761             var align = this.labelAlign || this.parentLabelAlign();
39762             var id = Roo.id();
39763             
39764             this.allCountries = [];
39765             this.dialCodeMapping = [];
39766             
39767             for (var i = 0; i < data.length; i++) {
39768               var c = data[i];
39769               this.allCountries[i] = {
39770                 name: c[0],
39771                 iso2: c[1],
39772                 dialCode: c[2],
39773                 priority: c[3] || 0,
39774                 areaCodes: c[4] || null
39775               };
39776               this.dialCodeMapping[c[2]] = {
39777                   name: c[0],
39778                   iso2: c[1],
39779                   priority: c[3] || 0,
39780                   areaCodes: c[4] || null
39781               };
39782             }
39783             
39784             var cfg = {
39785                 cls: 'form-group',
39786                 cn: []
39787             };
39788             
39789             var input =  {
39790                 tag: 'input',
39791                 id : id,
39792                 cls : 'form-control tel-input',
39793                 autocomplete: 'new-password'
39794             };
39795             
39796             var hiddenInput = {
39797                 tag: 'input',
39798                 type: 'hidden',
39799                 cls: 'hidden-tel-input'
39800             };
39801             
39802             if (this.name) {
39803                 hiddenInput.name = this.name;
39804             }
39805             
39806             if (this.disabled) {
39807                 input.disabled = true;
39808             }
39809             
39810             var flag_container = {
39811                 tag: 'div',
39812                 cls: 'flag-box',
39813                 cn: [
39814                     {
39815                         tag: 'div',
39816                         cls: 'flag'
39817                     },
39818                     {
39819                         tag: 'div',
39820                         cls: 'caret'
39821                     }
39822                 ]
39823             };
39824             
39825             var box = {
39826                 tag: 'div',
39827                 cls: this.hasFeedback ? 'has-feedback' : '',
39828                 cn: [
39829                     hiddenInput,
39830                     input,
39831                     {
39832                         tag: 'input',
39833                         cls: 'dial-code-holder',
39834                         disabled: true
39835                     }
39836                 ]
39837             };
39838             
39839             var container = {
39840                 cls: 'roo-select2-container input-group',
39841                 cn: [
39842                     flag_container,
39843                     box
39844                 ]
39845             };
39846             
39847             if (this.fieldLabel.length) {
39848                 var indicator = {
39849                     tag: 'i',
39850                     tooltip: 'This field is required'
39851                 };
39852                 
39853                 var label = {
39854                     tag: 'label',
39855                     'for':  id,
39856                     cls: 'control-label',
39857                     cn: []
39858                 };
39859                 
39860                 var label_text = {
39861                     tag: 'span',
39862                     html: this.fieldLabel
39863                 };
39864                 
39865                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39866                 label.cn = [
39867                     indicator,
39868                     label_text
39869                 ];
39870                 
39871                 if(this.indicatorpos == 'right') {
39872                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39873                     label.cn = [
39874                         label_text,
39875                         indicator
39876                     ];
39877                 }
39878                 
39879                 if(align == 'left') {
39880                     container = {
39881                         tag: 'div',
39882                         cn: [
39883                             container
39884                         ]
39885                     };
39886                     
39887                     if(this.labelWidth > 12){
39888                         label.style = "width: " + this.labelWidth + 'px';
39889                     }
39890                     if(this.labelWidth < 13 && this.labelmd == 0){
39891                         this.labelmd = this.labelWidth;
39892                     }
39893                     if(this.labellg > 0){
39894                         label.cls += ' col-lg-' + this.labellg;
39895                         input.cls += ' col-lg-' + (12 - this.labellg);
39896                     }
39897                     if(this.labelmd > 0){
39898                         label.cls += ' col-md-' + this.labelmd;
39899                         container.cls += ' col-md-' + (12 - this.labelmd);
39900                     }
39901                     if(this.labelsm > 0){
39902                         label.cls += ' col-sm-' + this.labelsm;
39903                         container.cls += ' col-sm-' + (12 - this.labelsm);
39904                     }
39905                     if(this.labelxs > 0){
39906                         label.cls += ' col-xs-' + this.labelxs;
39907                         container.cls += ' col-xs-' + (12 - this.labelxs);
39908                     }
39909                 }
39910             }
39911             
39912             cfg.cn = [
39913                 label,
39914                 container
39915             ];
39916             
39917             var settings = this;
39918             
39919             ['xs','sm','md','lg'].map(function(size){
39920                 if (settings[size]) {
39921                     cfg.cls += ' col-' + size + '-' + settings[size];
39922                 }
39923             });
39924             
39925             this.store = new Roo.data.Store({
39926                 proxy : new Roo.data.MemoryProxy({}),
39927                 reader : new Roo.data.JsonReader({
39928                     fields : [
39929                         {
39930                             'name' : 'name',
39931                             'type' : 'string'
39932                         },
39933                         {
39934                             'name' : 'iso2',
39935                             'type' : 'string'
39936                         },
39937                         {
39938                             'name' : 'dialCode',
39939                             'type' : 'string'
39940                         },
39941                         {
39942                             'name' : 'priority',
39943                             'type' : 'string'
39944                         },
39945                         {
39946                             'name' : 'areaCodes',
39947                             'type' : 'string'
39948                         }
39949                     ]
39950                 })
39951             });
39952             
39953             if(!this.preferedCountries) {
39954                 this.preferedCountries = [
39955                     'hk',
39956                     'gb',
39957                     'us'
39958                 ];
39959             }
39960             
39961             var p = this.preferedCountries.reverse();
39962             
39963             if(p) {
39964                 for (var i = 0; i < p.length; i++) {
39965                     for (var j = 0; j < this.allCountries.length; j++) {
39966                         if(this.allCountries[j].iso2 == p[i]) {
39967                             var t = this.allCountries[j];
39968                             this.allCountries.splice(j,1);
39969                             this.allCountries.unshift(t);
39970                         }
39971                     } 
39972                 }
39973             }
39974             
39975             this.store.proxy.data = {
39976                 success: true,
39977                 data: this.allCountries
39978             };
39979             
39980             return cfg;
39981         },
39982         
39983         initEvents : function()
39984         {
39985             this.createList();
39986             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39987             
39988             this.indicator = this.indicatorEl();
39989             this.flag = this.flagEl();
39990             this.dialCodeHolder = this.dialCodeHolderEl();
39991             
39992             this.trigger = this.el.select('div.flag-box',true).first();
39993             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39994             
39995             var _this = this;
39996             
39997             (function(){
39998                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39999                 _this.list.setWidth(lw);
40000             }).defer(100);
40001             
40002             this.list.on('mouseover', this.onViewOver, this);
40003             this.list.on('mousemove', this.onViewMove, this);
40004             this.inputEl().on("keyup", this.onKeyUp, this);
40005             
40006             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40007
40008             this.view = new Roo.View(this.list, this.tpl, {
40009                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40010             });
40011             
40012             this.view.on('click', this.onViewClick, this);
40013             this.setValue(this.defaultDialCode);
40014         },
40015         
40016         onTriggerClick : function(e)
40017         {
40018             Roo.log('trigger click');
40019             if(this.disabled){
40020                 return;
40021             }
40022             
40023             if(this.isExpanded()){
40024                 this.collapse();
40025                 this.hasFocus = false;
40026             }else {
40027                 this.store.load({});
40028                 this.hasFocus = true;
40029                 this.expand();
40030             }
40031         },
40032         
40033         isExpanded : function()
40034         {
40035             return this.list.isVisible();
40036         },
40037         
40038         collapse : function()
40039         {
40040             if(!this.isExpanded()){
40041                 return;
40042             }
40043             this.list.hide();
40044             Roo.get(document).un('mousedown', this.collapseIf, this);
40045             Roo.get(document).un('mousewheel', this.collapseIf, this);
40046             this.fireEvent('collapse', this);
40047             this.validate();
40048         },
40049         
40050         expand : function()
40051         {
40052             Roo.log('expand');
40053
40054             if(this.isExpanded() || !this.hasFocus){
40055                 return;
40056             }
40057             
40058             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40059             this.list.setWidth(lw);
40060             
40061             this.list.show();
40062             this.restrictHeight();
40063             
40064             Roo.get(document).on('mousedown', this.collapseIf, this);
40065             Roo.get(document).on('mousewheel', this.collapseIf, this);
40066             
40067             this.fireEvent('expand', this);
40068         },
40069         
40070         restrictHeight : function()
40071         {
40072             this.list.alignTo(this.inputEl(), this.listAlign);
40073             this.list.alignTo(this.inputEl(), this.listAlign);
40074         },
40075         
40076         onViewOver : function(e, t)
40077         {
40078             if(this.inKeyMode){
40079                 return;
40080             }
40081             var item = this.view.findItemFromChild(t);
40082             
40083             if(item){
40084                 var index = this.view.indexOf(item);
40085                 this.select(index, false);
40086             }
40087         },
40088
40089         // private
40090         onViewClick : function(view, doFocus, el, e)
40091         {
40092             var index = this.view.getSelectedIndexes()[0];
40093             
40094             var r = this.store.getAt(index);
40095             
40096             if(r){
40097                 this.onSelect(r, index);
40098             }
40099             if(doFocus !== false && !this.blockFocus){
40100                 this.inputEl().focus();
40101             }
40102         },
40103         
40104         onViewMove : function(e, t)
40105         {
40106             this.inKeyMode = false;
40107         },
40108         
40109         select : function(index, scrollIntoView)
40110         {
40111             this.selectedIndex = index;
40112             this.view.select(index);
40113             if(scrollIntoView !== false){
40114                 var el = this.view.getNode(index);
40115                 if(el){
40116                     this.list.scrollChildIntoView(el, false);
40117                 }
40118             }
40119         },
40120         
40121         createList : function()
40122         {
40123             this.list = Roo.get(document.body).createChild({
40124                 tag: 'ul',
40125                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40126                 style: 'display:none'
40127             });
40128             
40129             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40130         },
40131         
40132         collapseIf : function(e)
40133         {
40134             var in_combo  = e.within(this.el);
40135             var in_list =  e.within(this.list);
40136             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40137             
40138             if (in_combo || in_list || is_list) {
40139                 return;
40140             }
40141             this.collapse();
40142         },
40143         
40144         onSelect : function(record, index)
40145         {
40146             if(this.fireEvent('beforeselect', this, record, index) !== false){
40147                 
40148                 this.setFlagClass(record.data.iso2);
40149                 this.setDialCode(record.data.dialCode);
40150                 this.hasFocus = false;
40151                 this.collapse();
40152                 this.fireEvent('select', this, record, index);
40153             }
40154         },
40155         
40156         flagEl : function()
40157         {
40158             var flag = this.el.select('div.flag',true).first();
40159             if(!flag){
40160                 return false;
40161             }
40162             return flag;
40163         },
40164         
40165         dialCodeHolderEl : function()
40166         {
40167             var d = this.el.select('input.dial-code-holder',true).first();
40168             if(!d){
40169                 return false;
40170             }
40171             return d;
40172         },
40173         
40174         setDialCode : function(v)
40175         {
40176             this.dialCodeHolder.dom.value = '+'+v;
40177         },
40178         
40179         setFlagClass : function(n)
40180         {
40181             this.flag.dom.className = 'flag '+n;
40182         },
40183         
40184         getValue : function()
40185         {
40186             var v = this.inputEl().getValue();
40187             if(this.dialCodeHolder) {
40188                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40189             }
40190             return v;
40191         },
40192         
40193         setValue : function(v)
40194         {
40195             var d = this.getDialCode(v);
40196             
40197             //invalid dial code
40198             if(v.length == 0 || !d || d.length == 0) {
40199                 if(this.rendered){
40200                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40201                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40202                 }
40203                 return;
40204             }
40205             
40206             //valid dial code
40207             this.setFlagClass(this.dialCodeMapping[d].iso2);
40208             this.setDialCode(d);
40209             this.inputEl().dom.value = v.replace('+'+d,'');
40210             this.hiddenEl().dom.value = this.getValue();
40211             
40212             this.validate();
40213         },
40214         
40215         getDialCode : function(v)
40216         {
40217             v = v ||  '';
40218             
40219             if (v.length == 0) {
40220                 return this.dialCodeHolder.dom.value;
40221             }
40222             
40223             var dialCode = "";
40224             if (v.charAt(0) != "+") {
40225                 return false;
40226             }
40227             var numericChars = "";
40228             for (var i = 1; i < v.length; i++) {
40229               var c = v.charAt(i);
40230               if (!isNaN(c)) {
40231                 numericChars += c;
40232                 if (this.dialCodeMapping[numericChars]) {
40233                   dialCode = v.substr(1, i);
40234                 }
40235                 if (numericChars.length == 4) {
40236                   break;
40237                 }
40238               }
40239             }
40240             return dialCode;
40241         },
40242         
40243         reset : function()
40244         {
40245             this.setValue(this.defaultDialCode);
40246             this.validate();
40247         },
40248         
40249         hiddenEl : function()
40250         {
40251             return this.el.select('input.hidden-tel-input',true).first();
40252         },
40253         
40254         onKeyUp : function(e){
40255             
40256             var k = e.getKey();
40257             var c = e.getCharCode();
40258             
40259             if(
40260                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40261                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40262             ){
40263                 e.stopEvent();
40264             }
40265             
40266             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40267             //     return;
40268             // }
40269             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40270                 e.stopEvent();
40271             }
40272             
40273             this.setValue(this.getValue());
40274         }
40275         
40276 });
40277 /**
40278  * @class Roo.bootstrap.MoneyField
40279  * @extends Roo.bootstrap.ComboBox
40280  * Bootstrap MoneyField class
40281  * 
40282  * @constructor
40283  * Create a new MoneyField.
40284  * @param {Object} config Configuration options
40285  */
40286
40287 Roo.bootstrap.MoneyField = function(config) {
40288     
40289     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40290     
40291 };
40292
40293 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40294     
40295     /**
40296      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40297      */
40298     allowDecimals : true,
40299     /**
40300      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40301      */
40302     decimalSeparator : ".",
40303     /**
40304      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40305      */
40306     decimalPrecision : 0,
40307     /**
40308      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40309      */
40310     allowNegative : true,
40311     /**
40312      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40313      */
40314     allowZero: true,
40315     /**
40316      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40317      */
40318     minValue : Number.NEGATIVE_INFINITY,
40319     /**
40320      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40321      */
40322     maxValue : Number.MAX_VALUE,
40323     /**
40324      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40325      */
40326     minText : "The minimum value for this field is {0}",
40327     /**
40328      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40329      */
40330     maxText : "The maximum value for this field is {0}",
40331     /**
40332      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40333      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40334      */
40335     nanText : "{0} is not a valid number",
40336     /**
40337      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40338      */
40339     castInt : true,
40340     /**
40341      * @cfg {String} defaults currency of the MoneyField
40342      * value should be in lkey
40343      */
40344     defaultCurrency : false,
40345     /**
40346      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40347      */
40348     thousandsDelimiter : false,
40349     
40350     
40351     inputlg : 9,
40352     inputmd : 9,
40353     inputsm : 9,
40354     inputxs : 6,
40355     
40356     store : false,
40357     
40358     getAutoCreate : function()
40359     {
40360         var align = this.labelAlign || this.parentLabelAlign();
40361         
40362         var id = Roo.id();
40363
40364         var cfg = {
40365             cls: 'form-group',
40366             cn: []
40367         };
40368
40369         var input =  {
40370             tag: 'input',
40371             id : id,
40372             cls : 'form-control roo-money-amount-input',
40373             autocomplete: 'new-password'
40374         };
40375         
40376         var hiddenInput = {
40377             tag: 'input',
40378             type: 'hidden',
40379             id: Roo.id(),
40380             cls: 'hidden-number-input'
40381         };
40382         
40383         if (this.name) {
40384             hiddenInput.name = this.name;
40385         }
40386
40387         if (this.disabled) {
40388             input.disabled = true;
40389         }
40390
40391         var clg = 12 - this.inputlg;
40392         var cmd = 12 - this.inputmd;
40393         var csm = 12 - this.inputsm;
40394         var cxs = 12 - this.inputxs;
40395         
40396         var container = {
40397             tag : 'div',
40398             cls : 'row roo-money-field',
40399             cn : [
40400                 {
40401                     tag : 'div',
40402                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40403                     cn : [
40404                         {
40405                             tag : 'div',
40406                             cls: 'roo-select2-container input-group',
40407                             cn: [
40408                                 {
40409                                     tag : 'input',
40410                                     cls : 'form-control roo-money-currency-input',
40411                                     autocomplete: 'new-password',
40412                                     readOnly : 1,
40413                                     name : this.currencyName
40414                                 },
40415                                 {
40416                                     tag :'span',
40417                                     cls : 'input-group-addon',
40418                                     cn : [
40419                                         {
40420                                             tag: 'span',
40421                                             cls: 'caret'
40422                                         }
40423                                     ]
40424                                 }
40425                             ]
40426                         }
40427                     ]
40428                 },
40429                 {
40430                     tag : 'div',
40431                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40432                     cn : [
40433                         {
40434                             tag: 'div',
40435                             cls: this.hasFeedback ? 'has-feedback' : '',
40436                             cn: [
40437                                 input
40438                             ]
40439                         }
40440                     ]
40441                 }
40442             ]
40443             
40444         };
40445         
40446         if (this.fieldLabel.length) {
40447             var indicator = {
40448                 tag: 'i',
40449                 tooltip: 'This field is required'
40450             };
40451
40452             var label = {
40453                 tag: 'label',
40454                 'for':  id,
40455                 cls: 'control-label',
40456                 cn: []
40457             };
40458
40459             var label_text = {
40460                 tag: 'span',
40461                 html: this.fieldLabel
40462             };
40463
40464             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40465             label.cn = [
40466                 indicator,
40467                 label_text
40468             ];
40469
40470             if(this.indicatorpos == 'right') {
40471                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40472                 label.cn = [
40473                     label_text,
40474                     indicator
40475                 ];
40476             }
40477
40478             if(align == 'left') {
40479                 container = {
40480                     tag: 'div',
40481                     cn: [
40482                         container
40483                     ]
40484                 };
40485
40486                 if(this.labelWidth > 12){
40487                     label.style = "width: " + this.labelWidth + 'px';
40488                 }
40489                 if(this.labelWidth < 13 && this.labelmd == 0){
40490                     this.labelmd = this.labelWidth;
40491                 }
40492                 if(this.labellg > 0){
40493                     label.cls += ' col-lg-' + this.labellg;
40494                     input.cls += ' col-lg-' + (12 - this.labellg);
40495                 }
40496                 if(this.labelmd > 0){
40497                     label.cls += ' col-md-' + this.labelmd;
40498                     container.cls += ' col-md-' + (12 - this.labelmd);
40499                 }
40500                 if(this.labelsm > 0){
40501                     label.cls += ' col-sm-' + this.labelsm;
40502                     container.cls += ' col-sm-' + (12 - this.labelsm);
40503                 }
40504                 if(this.labelxs > 0){
40505                     label.cls += ' col-xs-' + this.labelxs;
40506                     container.cls += ' col-xs-' + (12 - this.labelxs);
40507                 }
40508             }
40509         }
40510
40511         cfg.cn = [
40512             label,
40513             container,
40514             hiddenInput
40515         ];
40516         
40517         var settings = this;
40518
40519         ['xs','sm','md','lg'].map(function(size){
40520             if (settings[size]) {
40521                 cfg.cls += ' col-' + size + '-' + settings[size];
40522             }
40523         });
40524         
40525         return cfg;
40526     },
40527     
40528     initEvents : function()
40529     {
40530         this.indicator = this.indicatorEl();
40531         
40532         this.initCurrencyEvent();
40533         
40534         this.initNumberEvent();
40535     },
40536     
40537     initCurrencyEvent : function()
40538     {
40539         if (!this.store) {
40540             throw "can not find store for combo";
40541         }
40542         
40543         this.store = Roo.factory(this.store, Roo.data);
40544         this.store.parent = this;
40545         
40546         this.createList();
40547         
40548         this.triggerEl = this.el.select('.input-group-addon', true).first();
40549         
40550         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40551         
40552         var _this = this;
40553         
40554         (function(){
40555             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40556             _this.list.setWidth(lw);
40557         }).defer(100);
40558         
40559         this.list.on('mouseover', this.onViewOver, this);
40560         this.list.on('mousemove', this.onViewMove, this);
40561         this.list.on('scroll', this.onViewScroll, this);
40562         
40563         if(!this.tpl){
40564             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40565         }
40566         
40567         this.view = new Roo.View(this.list, this.tpl, {
40568             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40569         });
40570         
40571         this.view.on('click', this.onViewClick, this);
40572         
40573         this.store.on('beforeload', this.onBeforeLoad, this);
40574         this.store.on('load', this.onLoad, this);
40575         this.store.on('loadexception', this.onLoadException, this);
40576         
40577         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40578             "up" : function(e){
40579                 this.inKeyMode = true;
40580                 this.selectPrev();
40581             },
40582
40583             "down" : function(e){
40584                 if(!this.isExpanded()){
40585                     this.onTriggerClick();
40586                 }else{
40587                     this.inKeyMode = true;
40588                     this.selectNext();
40589                 }
40590             },
40591
40592             "enter" : function(e){
40593                 this.collapse();
40594                 
40595                 if(this.fireEvent("specialkey", this, e)){
40596                     this.onViewClick(false);
40597                 }
40598                 
40599                 return true;
40600             },
40601
40602             "esc" : function(e){
40603                 this.collapse();
40604             },
40605
40606             "tab" : function(e){
40607                 this.collapse();
40608                 
40609                 if(this.fireEvent("specialkey", this, e)){
40610                     this.onViewClick(false);
40611                 }
40612                 
40613                 return true;
40614             },
40615
40616             scope : this,
40617
40618             doRelay : function(foo, bar, hname){
40619                 if(hname == 'down' || this.scope.isExpanded()){
40620                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40621                 }
40622                 return true;
40623             },
40624
40625             forceKeyDown: true
40626         });
40627         
40628         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40629         
40630     },
40631     
40632     initNumberEvent : function(e)
40633     {
40634         this.inputEl().on("keydown" , this.fireKey,  this);
40635         this.inputEl().on("focus", this.onFocus,  this);
40636         this.inputEl().on("blur", this.onBlur,  this);
40637         
40638         this.inputEl().relayEvent('keyup', this);
40639         
40640         if(this.indicator){
40641             this.indicator.addClass('invisible');
40642         }
40643  
40644         this.originalValue = this.getValue();
40645         
40646         if(this.validationEvent == 'keyup'){
40647             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40648             this.inputEl().on('keyup', this.filterValidation, this);
40649         }
40650         else if(this.validationEvent !== false){
40651             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40652         }
40653         
40654         if(this.selectOnFocus){
40655             this.on("focus", this.preFocus, this);
40656             
40657         }
40658         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40659             this.inputEl().on("keypress", this.filterKeys, this);
40660         } else {
40661             this.inputEl().relayEvent('keypress', this);
40662         }
40663         
40664         var allowed = "0123456789";
40665         
40666         if(this.allowDecimals){
40667             allowed += this.decimalSeparator;
40668         }
40669         
40670         if(this.allowNegative){
40671             allowed += "-";
40672         }
40673         
40674         if(this.thousandsDelimiter) {
40675             allowed += ",";
40676         }
40677         
40678         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40679         
40680         var keyPress = function(e){
40681             
40682             var k = e.getKey();
40683             
40684             var c = e.getCharCode();
40685             
40686             if(
40687                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40688                     allowed.indexOf(String.fromCharCode(c)) === -1
40689             ){
40690                 e.stopEvent();
40691                 return;
40692             }
40693             
40694             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40695                 return;
40696             }
40697             
40698             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40699                 e.stopEvent();
40700             }
40701         };
40702         
40703         this.inputEl().on("keypress", keyPress, this);
40704         
40705     },
40706     
40707     onTriggerClick : function(e)
40708     {   
40709         if(this.disabled){
40710             return;
40711         }
40712         
40713         this.page = 0;
40714         this.loadNext = false;
40715         
40716         if(this.isExpanded()){
40717             this.collapse();
40718             return;
40719         }
40720         
40721         this.hasFocus = true;
40722         
40723         if(this.triggerAction == 'all') {
40724             this.doQuery(this.allQuery, true);
40725             return;
40726         }
40727         
40728         this.doQuery(this.getRawValue());
40729     },
40730     
40731     getCurrency : function()
40732     {   
40733         var v = this.currencyEl().getValue();
40734         
40735         return v;
40736     },
40737     
40738     restrictHeight : function()
40739     {
40740         this.list.alignTo(this.currencyEl(), this.listAlign);
40741         this.list.alignTo(this.currencyEl(), this.listAlign);
40742     },
40743     
40744     onViewClick : function(view, doFocus, el, e)
40745     {
40746         var index = this.view.getSelectedIndexes()[0];
40747         
40748         var r = this.store.getAt(index);
40749         
40750         if(r){
40751             this.onSelect(r, index);
40752         }
40753     },
40754     
40755     onSelect : function(record, index){
40756         
40757         if(this.fireEvent('beforeselect', this, record, index) !== false){
40758         
40759             this.setFromCurrencyData(index > -1 ? record.data : false);
40760             
40761             this.collapse();
40762             
40763             this.fireEvent('select', this, record, index);
40764         }
40765     },
40766     
40767     setFromCurrencyData : function(o)
40768     {
40769         var currency = '';
40770         
40771         this.lastCurrency = o;
40772         
40773         if (this.currencyField) {
40774             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40775         } else {
40776             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40777         }
40778         
40779         this.lastSelectionText = currency;
40780         
40781         //setting default currency
40782         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40783             this.setCurrency(this.defaultCurrency);
40784             return;
40785         }
40786         
40787         this.setCurrency(currency);
40788     },
40789     
40790     setFromData : function(o)
40791     {
40792         var c = {};
40793         
40794         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40795         
40796         this.setFromCurrencyData(c);
40797         
40798         var value = '';
40799         
40800         if (this.name) {
40801             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40802         } else {
40803             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40804         }
40805         
40806         this.setValue(value);
40807         
40808     },
40809     
40810     setCurrency : function(v)
40811     {   
40812         this.currencyValue = v;
40813         
40814         if(this.rendered){
40815             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40816             this.validate();
40817         }
40818     },
40819     
40820     setValue : function(v)
40821     {
40822         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40823         
40824         this.value = v;
40825         
40826         if(this.rendered){
40827             
40828             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40829             
40830             this.inputEl().dom.value = (v == '') ? '' :
40831                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40832             
40833             if(!this.allowZero && v === '0') {
40834                 this.hiddenEl().dom.value = '';
40835                 this.inputEl().dom.value = '';
40836             }
40837             
40838             this.validate();
40839         }
40840     },
40841     
40842     getRawValue : function()
40843     {
40844         var v = this.inputEl().getValue();
40845         
40846         return v;
40847     },
40848     
40849     getValue : function()
40850     {
40851         return this.fixPrecision(this.parseValue(this.getRawValue()));
40852     },
40853     
40854     parseValue : function(value)
40855     {
40856         if(this.thousandsDelimiter) {
40857             value += "";
40858             r = new RegExp(",", "g");
40859             value = value.replace(r, "");
40860         }
40861         
40862         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40863         return isNaN(value) ? '' : value;
40864         
40865     },
40866     
40867     fixPrecision : function(value)
40868     {
40869         if(this.thousandsDelimiter) {
40870             value += "";
40871             r = new RegExp(",", "g");
40872             value = value.replace(r, "");
40873         }
40874         
40875         var nan = isNaN(value);
40876         
40877         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40878             return nan ? '' : value;
40879         }
40880         return parseFloat(value).toFixed(this.decimalPrecision);
40881     },
40882     
40883     decimalPrecisionFcn : function(v)
40884     {
40885         return Math.floor(v);
40886     },
40887     
40888     validateValue : function(value)
40889     {
40890         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40891             return false;
40892         }
40893         
40894         var num = this.parseValue(value);
40895         
40896         if(isNaN(num)){
40897             this.markInvalid(String.format(this.nanText, value));
40898             return false;
40899         }
40900         
40901         if(num < this.minValue){
40902             this.markInvalid(String.format(this.minText, this.minValue));
40903             return false;
40904         }
40905         
40906         if(num > this.maxValue){
40907             this.markInvalid(String.format(this.maxText, this.maxValue));
40908             return false;
40909         }
40910         
40911         return true;
40912     },
40913     
40914     validate : function()
40915     {
40916         if(this.disabled || this.allowBlank){
40917             this.markValid();
40918             return true;
40919         }
40920         
40921         var currency = this.getCurrency();
40922         
40923         if(this.validateValue(this.getRawValue()) && currency.length){
40924             this.markValid();
40925             return true;
40926         }
40927         
40928         this.markInvalid();
40929         return false;
40930     },
40931     
40932     getName: function()
40933     {
40934         return this.name;
40935     },
40936     
40937     beforeBlur : function()
40938     {
40939         if(!this.castInt){
40940             return;
40941         }
40942         
40943         var v = this.parseValue(this.getRawValue());
40944         
40945         if(v || v == 0){
40946             this.setValue(v);
40947         }
40948     },
40949     
40950     onBlur : function()
40951     {
40952         this.beforeBlur();
40953         
40954         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40955             //this.el.removeClass(this.focusClass);
40956         }
40957         
40958         this.hasFocus = false;
40959         
40960         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40961             this.validate();
40962         }
40963         
40964         var v = this.getValue();
40965         
40966         if(String(v) !== String(this.startValue)){
40967             this.fireEvent('change', this, v, this.startValue);
40968         }
40969         
40970         this.fireEvent("blur", this);
40971     },
40972     
40973     inputEl : function()
40974     {
40975         return this.el.select('.roo-money-amount-input', true).first();
40976     },
40977     
40978     currencyEl : function()
40979     {
40980         return this.el.select('.roo-money-currency-input', true).first();
40981     },
40982     
40983     hiddenEl : function()
40984     {
40985         return this.el.select('input.hidden-number-input',true).first();
40986     }
40987     
40988 });