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(
2826             Roo.lib.Dom.getViewWidth(true),
2827             Roo.lib.Dom.getViewHeight(true)
2828         );
2829         
2830         if (this.fitwindow) {
2831             this.setSize(
2832                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2833                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2834             );
2835             return;
2836         }
2837         
2838         if(this.max_width !== 0) {
2839             
2840             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2841             
2842             if(this.height) {
2843                 this.setSize(
2844                     w,
2845                     this.height <= Roo.lib.Dom.getViewportHeight(true) - 60 ? 
2846                         this.height : Roo.lib.Dom.getViewportHeight(true) - 60
2847                 );
2848                 return;
2849             }
2850             
2851             if(!this.fit_content) {
2852                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2853                 return;
2854             }
2855             
2856             var body_childs = this.bodyEl.dom.childNodes;
2857             // does not seem to give enough space...
2858             var full_height = 60 + this.headerEl.getHeight() + this.footerEl.getHeight();
2859             for(var i = 0; i < body_childs.length; i++) {
2860                 full_height += body_childs[i].offsetHeight;
2861             }
2862             
2863             this.setSize(w, Math.min(full_height, Roo.lib.Dom.getViewportHeight(true) - 60));
2864         }
2865         
2866     },
2867
2868     setSize : function(w,h)
2869     {
2870         if (!w && !h) {
2871             return;
2872         }
2873         
2874         Roo.log(h);
2875         
2876         this.resizeTo(w,h);
2877     },
2878
2879     show : function() {
2880
2881         if (!this.rendered) {
2882             this.render();
2883         }
2884
2885         //this.el.setStyle('display', 'block');
2886         this.el.removeClass('hideing');        
2887         this.el.addClass('show');
2888  
2889         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2890             var _this = this;
2891             (function(){
2892                 this.el.addClass('in');
2893             }).defer(50, this);
2894         }else{
2895             this.el.addClass('in');
2896         }
2897
2898         // not sure how we can show data in here..
2899         //if (this.tmpl) {
2900         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2901         //}
2902
2903         Roo.get(document.body).addClass("x-body-masked");
2904         
2905         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2906         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2907         this.maskEl.addClass('show');
2908         
2909         this.resize();
2910         
2911         this.fireEvent('show', this);
2912
2913         // set zindex here - otherwise it appears to be ignored...
2914         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2915
2916         (function () {
2917             this.items.forEach( function(e) {
2918                 e.layout ? e.layout() : false;
2919
2920             });
2921         }).defer(100,this);
2922
2923     },
2924     hide : function()
2925     {
2926         if(this.fireEvent("beforehide", this) !== false){
2927             this.maskEl.removeClass('show');
2928             Roo.get(document.body).removeClass("x-body-masked");
2929             this.el.removeClass('in');
2930             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2931
2932             if(this.animate){ // why
2933                 this.el.addClass('hideing');
2934                 (function(){
2935                     if (!this.el.hasClass('hideing')) {
2936                         return; // it's been shown again...
2937                     }
2938                     this.el.removeClass('show');
2939                     this.el.removeClass('hideing');
2940                 }).defer(150,this);
2941                 
2942             }else{
2943                  this.el.removeClass('show');
2944             }
2945             this.fireEvent('hide', this);
2946         }
2947     },
2948     isVisible : function()
2949     {
2950         
2951         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2952         
2953     },
2954
2955     addButton : function(str, cb)
2956     {
2957
2958
2959         var b = Roo.apply({}, { html : str } );
2960         b.xns = b.xns || Roo.bootstrap;
2961         b.xtype = b.xtype || 'Button';
2962         if (typeof(b.listeners) == 'undefined') {
2963             b.listeners = { click : cb.createDelegate(this)  };
2964         }
2965
2966         var btn = Roo.factory(b);
2967
2968         btn.render(this.el.select('.modal-footer div').first());
2969
2970         return btn;
2971
2972     },
2973
2974     setDefaultButton : function(btn)
2975     {
2976         //this.el.select('.modal-footer').()
2977     },
2978     diff : false,
2979
2980     resizeTo: function(w,h)
2981     {
2982         // skip.. ?? why??
2983
2984         this.dialogEl.setWidth(w);
2985         if (this.diff === false) {
2986             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2987         }
2988
2989         this.bodyEl.setHeight(h - this.diff);
2990
2991         this.fireEvent('resize', this);
2992
2993     },
2994     setContentSize  : function(w, h)
2995     {
2996
2997     },
2998     onButtonClick: function(btn,e)
2999     {
3000         //Roo.log([a,b,c]);
3001         this.fireEvent('btnclick', btn.name, e);
3002     },
3003      /**
3004      * Set the title of the Dialog
3005      * @param {String} str new Title
3006      */
3007     setTitle: function(str) {
3008         this.titleEl.dom.innerHTML = str;
3009     },
3010     /**
3011      * Set the body of the Dialog
3012      * @param {String} str new Title
3013      */
3014     setBody: function(str) {
3015         this.bodyEl.dom.innerHTML = str;
3016     },
3017     /**
3018      * Set the body of the Dialog using the template
3019      * @param {Obj} data - apply this data to the template and replace the body contents.
3020      */
3021     applyBody: function(obj)
3022     {
3023         if (!this.tmpl) {
3024             Roo.log("Error - using apply Body without a template");
3025             //code
3026         }
3027         this.tmpl.overwrite(this.bodyEl, obj);
3028     }
3029
3030 });
3031
3032
3033 Roo.apply(Roo.bootstrap.Modal,  {
3034     /**
3035          * Button config that displays a single OK button
3036          * @type Object
3037          */
3038         OK :  [{
3039             name : 'ok',
3040             weight : 'primary',
3041             html : 'OK'
3042         }],
3043         /**
3044          * Button config that displays Yes and No buttons
3045          * @type Object
3046          */
3047         YESNO : [
3048             {
3049                 name  : 'no',
3050                 html : 'No'
3051             },
3052             {
3053                 name  :'yes',
3054                 weight : 'primary',
3055                 html : 'Yes'
3056             }
3057         ],
3058
3059         /**
3060          * Button config that displays OK and Cancel buttons
3061          * @type Object
3062          */
3063         OKCANCEL : [
3064             {
3065                name : 'cancel',
3066                 html : 'Cancel'
3067             },
3068             {
3069                 name : 'ok',
3070                 weight : 'primary',
3071                 html : 'OK'
3072             }
3073         ],
3074         /**
3075          * Button config that displays Yes, No and Cancel buttons
3076          * @type Object
3077          */
3078         YESNOCANCEL : [
3079             {
3080                 name : 'yes',
3081                 weight : 'primary',
3082                 html : 'Yes'
3083             },
3084             {
3085                 name : 'no',
3086                 html : 'No'
3087             },
3088             {
3089                 name : 'cancel',
3090                 html : 'Cancel'
3091             }
3092         ],
3093         
3094         zIndex : 10001
3095 });
3096 /*
3097  * - LGPL
3098  *
3099  * messagebox - can be used as a replace
3100  * 
3101  */
3102 /**
3103  * @class Roo.MessageBox
3104  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3105  * Example usage:
3106  *<pre><code>
3107 // Basic alert:
3108 Roo.Msg.alert('Status', 'Changes saved successfully.');
3109
3110 // Prompt for user data:
3111 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3112     if (btn == 'ok'){
3113         // process text value...
3114     }
3115 });
3116
3117 // Show a dialog using config options:
3118 Roo.Msg.show({
3119    title:'Save Changes?',
3120    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3121    buttons: Roo.Msg.YESNOCANCEL,
3122    fn: processResult,
3123    animEl: 'elId'
3124 });
3125 </code></pre>
3126  * @singleton
3127  */
3128 Roo.bootstrap.MessageBox = function(){
3129     var dlg, opt, mask, waitTimer;
3130     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3131     var buttons, activeTextEl, bwidth;
3132
3133     
3134     // private
3135     var handleButton = function(button){
3136         dlg.hide();
3137         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3138     };
3139
3140     // private
3141     var handleHide = function(){
3142         if(opt && opt.cls){
3143             dlg.el.removeClass(opt.cls);
3144         }
3145         //if(waitTimer){
3146         //    Roo.TaskMgr.stop(waitTimer);
3147         //    waitTimer = null;
3148         //}
3149     };
3150
3151     // private
3152     var updateButtons = function(b){
3153         var width = 0;
3154         if(!b){
3155             buttons["ok"].hide();
3156             buttons["cancel"].hide();
3157             buttons["yes"].hide();
3158             buttons["no"].hide();
3159             //dlg.footer.dom.style.display = 'none';
3160             return width;
3161         }
3162         dlg.footerEl.dom.style.display = '';
3163         for(var k in buttons){
3164             if(typeof buttons[k] != "function"){
3165                 if(b[k]){
3166                     buttons[k].show();
3167                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3168                     width += buttons[k].el.getWidth()+15;
3169                 }else{
3170                     buttons[k].hide();
3171                 }
3172             }
3173         }
3174         return width;
3175     };
3176
3177     // private
3178     var handleEsc = function(d, k, e){
3179         if(opt && opt.closable !== false){
3180             dlg.hide();
3181         }
3182         if(e){
3183             e.stopEvent();
3184         }
3185     };
3186
3187     return {
3188         /**
3189          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3190          * @return {Roo.BasicDialog} The BasicDialog element
3191          */
3192         getDialog : function(){
3193            if(!dlg){
3194                 dlg = new Roo.bootstrap.Modal( {
3195                     //draggable: true,
3196                     //resizable:false,
3197                     //constraintoviewport:false,
3198                     //fixedcenter:true,
3199                     //collapsible : false,
3200                     //shim:true,
3201                     //modal: true,
3202                 //    width: 'auto',
3203                   //  height:100,
3204                     //buttonAlign:"center",
3205                     closeClick : function(){
3206                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3207                             handleButton("no");
3208                         }else{
3209                             handleButton("cancel");
3210                         }
3211                     }
3212                 });
3213                 dlg.render();
3214                 dlg.on("hide", handleHide);
3215                 mask = dlg.mask;
3216                 //dlg.addKeyListener(27, handleEsc);
3217                 buttons = {};
3218                 this.buttons = buttons;
3219                 var bt = this.buttonText;
3220                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3221                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3222                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3223                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3224                 //Roo.log(buttons);
3225                 bodyEl = dlg.bodyEl.createChild({
3226
3227                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3228                         '<textarea class="roo-mb-textarea"></textarea>' +
3229                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3230                 });
3231                 msgEl = bodyEl.dom.firstChild;
3232                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3233                 textboxEl.enableDisplayMode();
3234                 textboxEl.addKeyListener([10,13], function(){
3235                     if(dlg.isVisible() && opt && opt.buttons){
3236                         if(opt.buttons.ok){
3237                             handleButton("ok");
3238                         }else if(opt.buttons.yes){
3239                             handleButton("yes");
3240                         }
3241                     }
3242                 });
3243                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3244                 textareaEl.enableDisplayMode();
3245                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3246                 progressEl.enableDisplayMode();
3247                 
3248                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3249                 var pf = progressEl.dom.firstChild;
3250                 if (pf) {
3251                     pp = Roo.get(pf.firstChild);
3252                     pp.setHeight(pf.offsetHeight);
3253                 }
3254                 
3255             }
3256             return dlg;
3257         },
3258
3259         /**
3260          * Updates the message box body text
3261          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3262          * the XHTML-compliant non-breaking space character '&amp;#160;')
3263          * @return {Roo.MessageBox} This message box
3264          */
3265         updateText : function(text)
3266         {
3267             if(!dlg.isVisible() && !opt.width){
3268                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3269                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3270             }
3271             msgEl.innerHTML = text || '&#160;';
3272       
3273             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3274             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3275             var w = Math.max(
3276                     Math.min(opt.width || cw , this.maxWidth), 
3277                     Math.max(opt.minWidth || this.minWidth, bwidth)
3278             );
3279             if(opt.prompt){
3280                 activeTextEl.setWidth(w);
3281             }
3282             if(dlg.isVisible()){
3283                 dlg.fixedcenter = false;
3284             }
3285             // to big, make it scroll. = But as usual stupid IE does not support
3286             // !important..
3287             
3288             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3289                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3290                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3291             } else {
3292                 bodyEl.dom.style.height = '';
3293                 bodyEl.dom.style.overflowY = '';
3294             }
3295             if (cw > w) {
3296                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3297             } else {
3298                 bodyEl.dom.style.overflowX = '';
3299             }
3300             
3301             dlg.setContentSize(w, bodyEl.getHeight());
3302             if(dlg.isVisible()){
3303                 dlg.fixedcenter = true;
3304             }
3305             return this;
3306         },
3307
3308         /**
3309          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3310          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3311          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3312          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3313          * @return {Roo.MessageBox} This message box
3314          */
3315         updateProgress : function(value, text){
3316             if(text){
3317                 this.updateText(text);
3318             }
3319             
3320             if (pp) { // weird bug on my firefox - for some reason this is not defined
3321                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3322                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3323             }
3324             return this;
3325         },        
3326
3327         /**
3328          * Returns true if the message box is currently displayed
3329          * @return {Boolean} True if the message box is visible, else false
3330          */
3331         isVisible : function(){
3332             return dlg && dlg.isVisible();  
3333         },
3334
3335         /**
3336          * Hides the message box if it is displayed
3337          */
3338         hide : function(){
3339             if(this.isVisible()){
3340                 dlg.hide();
3341             }  
3342         },
3343
3344         /**
3345          * Displays a new message box, or reinitializes an existing message box, based on the config options
3346          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3347          * The following config object properties are supported:
3348          * <pre>
3349 Property    Type             Description
3350 ----------  ---------------  ------------------------------------------------------------------------------------
3351 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3352                                    closes (defaults to undefined)
3353 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3354                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3355 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3356                                    progress and wait dialogs will ignore this property and always hide the
3357                                    close button as they can only be closed programmatically.
3358 cls               String           A custom CSS class to apply to the message box element
3359 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3360                                    displayed (defaults to 75)
3361 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3362                                    function will be btn (the name of the button that was clicked, if applicable,
3363                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3364                                    Progress and wait dialogs will ignore this option since they do not respond to
3365                                    user actions and can only be closed programmatically, so any required function
3366                                    should be called by the same code after it closes the dialog.
3367 icon              String           A CSS class that provides a background image to be used as an icon for
3368                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3369 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3370 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3371 modal             Boolean          False to allow user interaction with the page while the message box is
3372                                    displayed (defaults to true)
3373 msg               String           A string that will replace the existing message box body text (defaults
3374                                    to the XHTML-compliant non-breaking space character '&#160;')
3375 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3376 progress          Boolean          True to display a progress bar (defaults to false)
3377 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3378 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3379 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3380 title             String           The title text
3381 value             String           The string value to set into the active textbox element if displayed
3382 wait              Boolean          True to display a progress bar (defaults to false)
3383 width             Number           The width of the dialog in pixels
3384 </pre>
3385          *
3386          * Example usage:
3387          * <pre><code>
3388 Roo.Msg.show({
3389    title: 'Address',
3390    msg: 'Please enter your address:',
3391    width: 300,
3392    buttons: Roo.MessageBox.OKCANCEL,
3393    multiline: true,
3394    fn: saveAddress,
3395    animEl: 'addAddressBtn'
3396 });
3397 </code></pre>
3398          * @param {Object} config Configuration options
3399          * @return {Roo.MessageBox} This message box
3400          */
3401         show : function(options)
3402         {
3403             
3404             // this causes nightmares if you show one dialog after another
3405             // especially on callbacks..
3406              
3407             if(this.isVisible()){
3408                 
3409                 this.hide();
3410                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3411                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3412                 Roo.log("New Dialog Message:" +  options.msg )
3413                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3414                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3415                 
3416             }
3417             var d = this.getDialog();
3418             opt = options;
3419             d.setTitle(opt.title || "&#160;");
3420             d.closeEl.setDisplayed(opt.closable !== false);
3421             activeTextEl = textboxEl;
3422             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3423             if(opt.prompt){
3424                 if(opt.multiline){
3425                     textboxEl.hide();
3426                     textareaEl.show();
3427                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3428                         opt.multiline : this.defaultTextHeight);
3429                     activeTextEl = textareaEl;
3430                 }else{
3431                     textboxEl.show();
3432                     textareaEl.hide();
3433                 }
3434             }else{
3435                 textboxEl.hide();
3436                 textareaEl.hide();
3437             }
3438             progressEl.setDisplayed(opt.progress === true);
3439             this.updateProgress(0);
3440             activeTextEl.dom.value = opt.value || "";
3441             if(opt.prompt){
3442                 dlg.setDefaultButton(activeTextEl);
3443             }else{
3444                 var bs = opt.buttons;
3445                 var db = null;
3446                 if(bs && bs.ok){
3447                     db = buttons["ok"];
3448                 }else if(bs && bs.yes){
3449                     db = buttons["yes"];
3450                 }
3451                 dlg.setDefaultButton(db);
3452             }
3453             bwidth = updateButtons(opt.buttons);
3454             this.updateText(opt.msg);
3455             if(opt.cls){
3456                 d.el.addClass(opt.cls);
3457             }
3458             d.proxyDrag = opt.proxyDrag === true;
3459             d.modal = opt.modal !== false;
3460             d.mask = opt.modal !== false ? mask : false;
3461             if(!d.isVisible()){
3462                 // force it to the end of the z-index stack so it gets a cursor in FF
3463                 document.body.appendChild(dlg.el.dom);
3464                 d.animateTarget = null;
3465                 d.show(options.animEl);
3466             }
3467             return this;
3468         },
3469
3470         /**
3471          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3472          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3473          * and closing the message box when the process is complete.
3474          * @param {String} title The title bar text
3475          * @param {String} msg The message box body text
3476          * @return {Roo.MessageBox} This message box
3477          */
3478         progress : function(title, msg){
3479             this.show({
3480                 title : title,
3481                 msg : msg,
3482                 buttons: false,
3483                 progress:true,
3484                 closable:false,
3485                 minWidth: this.minProgressWidth,
3486                 modal : true
3487             });
3488             return this;
3489         },
3490
3491         /**
3492          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3493          * If a callback function is passed it will be called after the user clicks the button, and the
3494          * id of the button that was clicked will be passed as the only parameter to the callback
3495          * (could also be the top-right close button).
3496          * @param {String} title The title bar text
3497          * @param {String} msg The message box body text
3498          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3499          * @param {Object} scope (optional) The scope of the callback function
3500          * @return {Roo.MessageBox} This message box
3501          */
3502         alert : function(title, msg, fn, scope)
3503         {
3504             this.show({
3505                 title : title,
3506                 msg : msg,
3507                 buttons: this.OK,
3508                 fn: fn,
3509                 closable : false,
3510                 scope : scope,
3511                 modal : true
3512             });
3513             return this;
3514         },
3515
3516         /**
3517          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3518          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3519          * You are responsible for closing the message box when the process is complete.
3520          * @param {String} msg The message box body text
3521          * @param {String} title (optional) The title bar text
3522          * @return {Roo.MessageBox} This message box
3523          */
3524         wait : function(msg, title){
3525             this.show({
3526                 title : title,
3527                 msg : msg,
3528                 buttons: false,
3529                 closable:false,
3530                 progress:true,
3531                 modal:true,
3532                 width:300,
3533                 wait:true
3534             });
3535             waitTimer = Roo.TaskMgr.start({
3536                 run: function(i){
3537                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3538                 },
3539                 interval: 1000
3540             });
3541             return this;
3542         },
3543
3544         /**
3545          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3546          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3547          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3548          * @param {String} title The title bar text
3549          * @param {String} msg The message box body text
3550          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3551          * @param {Object} scope (optional) The scope of the callback function
3552          * @return {Roo.MessageBox} This message box
3553          */
3554         confirm : function(title, msg, fn, scope){
3555             this.show({
3556                 title : title,
3557                 msg : msg,
3558                 buttons: this.YESNO,
3559                 fn: fn,
3560                 scope : scope,
3561                 modal : true
3562             });
3563             return this;
3564         },
3565
3566         /**
3567          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3568          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3569          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3570          * (could also be the top-right close button) and the text that was entered will be passed as the two
3571          * parameters to the callback.
3572          * @param {String} title The title bar text
3573          * @param {String} msg The message box body text
3574          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3575          * @param {Object} scope (optional) The scope of the callback function
3576          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3577          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3578          * @return {Roo.MessageBox} This message box
3579          */
3580         prompt : function(title, msg, fn, scope, multiline){
3581             this.show({
3582                 title : title,
3583                 msg : msg,
3584                 buttons: this.OKCANCEL,
3585                 fn: fn,
3586                 minWidth:250,
3587                 scope : scope,
3588                 prompt:true,
3589                 multiline: multiline,
3590                 modal : true
3591             });
3592             return this;
3593         },
3594
3595         /**
3596          * Button config that displays a single OK button
3597          * @type Object
3598          */
3599         OK : {ok:true},
3600         /**
3601          * Button config that displays Yes and No buttons
3602          * @type Object
3603          */
3604         YESNO : {yes:true, no:true},
3605         /**
3606          * Button config that displays OK and Cancel buttons
3607          * @type Object
3608          */
3609         OKCANCEL : {ok:true, cancel:true},
3610         /**
3611          * Button config that displays Yes, No and Cancel buttons
3612          * @type Object
3613          */
3614         YESNOCANCEL : {yes:true, no:true, cancel:true},
3615
3616         /**
3617          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3618          * @type Number
3619          */
3620         defaultTextHeight : 75,
3621         /**
3622          * The maximum width in pixels of the message box (defaults to 600)
3623          * @type Number
3624          */
3625         maxWidth : 600,
3626         /**
3627          * The minimum width in pixels of the message box (defaults to 100)
3628          * @type Number
3629          */
3630         minWidth : 100,
3631         /**
3632          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3633          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3634          * @type Number
3635          */
3636         minProgressWidth : 250,
3637         /**
3638          * An object containing the default button text strings that can be overriden for localized language support.
3639          * Supported properties are: ok, cancel, yes and no.
3640          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3641          * @type Object
3642          */
3643         buttonText : {
3644             ok : "OK",
3645             cancel : "Cancel",
3646             yes : "Yes",
3647             no : "No"
3648         }
3649     };
3650 }();
3651
3652 /**
3653  * Shorthand for {@link Roo.MessageBox}
3654  */
3655 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3656 Roo.Msg = Roo.Msg || Roo.MessageBox;
3657 /*
3658  * - LGPL
3659  *
3660  * navbar
3661  * 
3662  */
3663
3664 /**
3665  * @class Roo.bootstrap.Navbar
3666  * @extends Roo.bootstrap.Component
3667  * Bootstrap Navbar class
3668
3669  * @constructor
3670  * Create a new Navbar
3671  * @param {Object} config The config object
3672  */
3673
3674
3675 Roo.bootstrap.Navbar = function(config){
3676     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3677     this.addEvents({
3678         // raw events
3679         /**
3680          * @event beforetoggle
3681          * Fire before toggle the menu
3682          * @param {Roo.EventObject} e
3683          */
3684         "beforetoggle" : true
3685     });
3686 };
3687
3688 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3689     
3690     
3691    
3692     // private
3693     navItems : false,
3694     loadMask : false,
3695     
3696     
3697     getAutoCreate : function(){
3698         
3699         
3700         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3701         
3702     },
3703     
3704     initEvents :function ()
3705     {
3706         //Roo.log(this.el.select('.navbar-toggle',true));
3707         this.el.select('.navbar-toggle',true).on('click', function() {
3708             if(this.fireEvent('beforetoggle', this) !== false){
3709                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3710             }
3711             
3712         }, this);
3713         
3714         var mark = {
3715             tag: "div",
3716             cls:"x-dlg-mask"
3717         };
3718         
3719         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3720         
3721         var size = this.el.getSize();
3722         this.maskEl.setSize(size.width, size.height);
3723         this.maskEl.enableDisplayMode("block");
3724         this.maskEl.hide();
3725         
3726         if(this.loadMask){
3727             this.maskEl.show();
3728         }
3729     },
3730     
3731     
3732     getChildContainer : function()
3733     {
3734         if (this.el.select('.collapse').getCount()) {
3735             return this.el.select('.collapse',true).first();
3736         }
3737         
3738         return this.el;
3739     },
3740     
3741     mask : function()
3742     {
3743         this.maskEl.show();
3744     },
3745     
3746     unmask : function()
3747     {
3748         this.maskEl.hide();
3749     } 
3750     
3751     
3752     
3753     
3754 });
3755
3756
3757
3758  
3759
3760  /*
3761  * - LGPL
3762  *
3763  * navbar
3764  * 
3765  */
3766
3767 /**
3768  * @class Roo.bootstrap.NavSimplebar
3769  * @extends Roo.bootstrap.Navbar
3770  * Bootstrap Sidebar class
3771  *
3772  * @cfg {Boolean} inverse is inverted color
3773  * 
3774  * @cfg {String} type (nav | pills | tabs)
3775  * @cfg {Boolean} arrangement stacked | justified
3776  * @cfg {String} align (left | right) alignment
3777  * 
3778  * @cfg {Boolean} main (true|false) main nav bar? default false
3779  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3780  * 
3781  * @cfg {String} tag (header|footer|nav|div) default is nav 
3782
3783  * 
3784  * 
3785  * 
3786  * @constructor
3787  * Create a new Sidebar
3788  * @param {Object} config The config object
3789  */
3790
3791
3792 Roo.bootstrap.NavSimplebar = function(config){
3793     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3794 };
3795
3796 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3797     
3798     inverse: false,
3799     
3800     type: false,
3801     arrangement: '',
3802     align : false,
3803     
3804     
3805     
3806     main : false,
3807     
3808     
3809     tag : false,
3810     
3811     
3812     getAutoCreate : function(){
3813         
3814         
3815         var cfg = {
3816             tag : this.tag || 'div',
3817             cls : 'navbar'
3818         };
3819           
3820         
3821         cfg.cn = [
3822             {
3823                 cls: 'nav',
3824                 tag : 'ul'
3825             }
3826         ];
3827         
3828          
3829         this.type = this.type || 'nav';
3830         if (['tabs','pills'].indexOf(this.type)!==-1) {
3831             cfg.cn[0].cls += ' nav-' + this.type
3832         
3833         
3834         } else {
3835             if (this.type!=='nav') {
3836                 Roo.log('nav type must be nav/tabs/pills')
3837             }
3838             cfg.cn[0].cls += ' navbar-nav'
3839         }
3840         
3841         
3842         
3843         
3844         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3845             cfg.cn[0].cls += ' nav-' + this.arrangement;
3846         }
3847         
3848         
3849         if (this.align === 'right') {
3850             cfg.cn[0].cls += ' navbar-right';
3851         }
3852         
3853         if (this.inverse) {
3854             cfg.cls += ' navbar-inverse';
3855             
3856         }
3857         
3858         
3859         return cfg;
3860     
3861         
3862     }
3863     
3864     
3865     
3866 });
3867
3868
3869
3870  
3871
3872  
3873        /*
3874  * - LGPL
3875  *
3876  * navbar
3877  * 
3878  */
3879
3880 /**
3881  * @class Roo.bootstrap.NavHeaderbar
3882  * @extends Roo.bootstrap.NavSimplebar
3883  * Bootstrap Sidebar class
3884  *
3885  * @cfg {String} brand what is brand
3886  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3887  * @cfg {String} brand_href href of the brand
3888  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3889  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3890  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3891  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3892  * 
3893  * @constructor
3894  * Create a new Sidebar
3895  * @param {Object} config The config object
3896  */
3897
3898
3899 Roo.bootstrap.NavHeaderbar = function(config){
3900     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3901       
3902 };
3903
3904 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3905     
3906     position: '',
3907     brand: '',
3908     brand_href: false,
3909     srButton : true,
3910     autohide : false,
3911     desktopCenter : false,
3912    
3913     
3914     getAutoCreate : function(){
3915         
3916         var   cfg = {
3917             tag: this.nav || 'nav',
3918             cls: 'navbar',
3919             role: 'navigation',
3920             cn: []
3921         };
3922         
3923         var cn = cfg.cn;
3924         if (this.desktopCenter) {
3925             cn.push({cls : 'container', cn : []});
3926             cn = cn[0].cn;
3927         }
3928         
3929         if(this.srButton){
3930             cn.push({
3931                 tag: 'div',
3932                 cls: 'navbar-header',
3933                 cn: [
3934                     {
3935                         tag: 'button',
3936                         type: 'button',
3937                         cls: 'navbar-toggle',
3938                         'data-toggle': 'collapse',
3939                         cn: [
3940                             {
3941                                 tag: 'span',
3942                                 cls: 'sr-only',
3943                                 html: 'Toggle navigation'
3944                             },
3945                             {
3946                                 tag: 'span',
3947                                 cls: 'icon-bar'
3948                             },
3949                             {
3950                                 tag: 'span',
3951                                 cls: 'icon-bar'
3952                             },
3953                             {
3954                                 tag: 'span',
3955                                 cls: 'icon-bar'
3956                             }
3957                         ]
3958                     }
3959                 ]
3960             });
3961         }
3962         
3963         cn.push({
3964             tag: 'div',
3965             cls: 'collapse navbar-collapse',
3966             cn : []
3967         });
3968         
3969         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3970         
3971         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3972             cfg.cls += ' navbar-' + this.position;
3973             
3974             // tag can override this..
3975             
3976             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3977         }
3978         
3979         if (this.brand !== '') {
3980             cn[0].cn.push({
3981                 tag: 'a',
3982                 href: this.brand_href ? this.brand_href : '#',
3983                 cls: 'navbar-brand',
3984                 cn: [
3985                 this.brand
3986                 ]
3987             });
3988         }
3989         
3990         if(this.main){
3991             cfg.cls += ' main-nav';
3992         }
3993         
3994         
3995         return cfg;
3996
3997         
3998     },
3999     getHeaderChildContainer : function()
4000     {
4001         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4002             return this.el.select('.navbar-header',true).first();
4003         }
4004         
4005         return this.getChildContainer();
4006     },
4007     
4008     
4009     initEvents : function()
4010     {
4011         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4012         
4013         if (this.autohide) {
4014             
4015             var prevScroll = 0;
4016             var ft = this.el;
4017             
4018             Roo.get(document).on('scroll',function(e) {
4019                 var ns = Roo.get(document).getScroll().top;
4020                 var os = prevScroll;
4021                 prevScroll = ns;
4022                 
4023                 if(ns > os){
4024                     ft.removeClass('slideDown');
4025                     ft.addClass('slideUp');
4026                     return;
4027                 }
4028                 ft.removeClass('slideUp');
4029                 ft.addClass('slideDown');
4030                  
4031               
4032           },this);
4033         }
4034     }    
4035     
4036 });
4037
4038
4039
4040  
4041
4042  /*
4043  * - LGPL
4044  *
4045  * navbar
4046  * 
4047  */
4048
4049 /**
4050  * @class Roo.bootstrap.NavSidebar
4051  * @extends Roo.bootstrap.Navbar
4052  * Bootstrap Sidebar class
4053  * 
4054  * @constructor
4055  * Create a new Sidebar
4056  * @param {Object} config The config object
4057  */
4058
4059
4060 Roo.bootstrap.NavSidebar = function(config){
4061     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4062 };
4063
4064 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4065     
4066     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4067     
4068     getAutoCreate : function(){
4069         
4070         
4071         return  {
4072             tag: 'div',
4073             cls: 'sidebar sidebar-nav'
4074         };
4075     
4076         
4077     }
4078     
4079     
4080     
4081 });
4082
4083
4084
4085  
4086
4087  /*
4088  * - LGPL
4089  *
4090  * nav group
4091  * 
4092  */
4093
4094 /**
4095  * @class Roo.bootstrap.NavGroup
4096  * @extends Roo.bootstrap.Component
4097  * Bootstrap NavGroup class
4098  * @cfg {String} align (left|right)
4099  * @cfg {Boolean} inverse
4100  * @cfg {String} type (nav|pills|tab) default nav
4101  * @cfg {String} navId - reference Id for navbar.
4102
4103  * 
4104  * @constructor
4105  * Create a new nav group
4106  * @param {Object} config The config object
4107  */
4108
4109 Roo.bootstrap.NavGroup = function(config){
4110     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4111     this.navItems = [];
4112    
4113     Roo.bootstrap.NavGroup.register(this);
4114      this.addEvents({
4115         /**
4116              * @event changed
4117              * Fires when the active item changes
4118              * @param {Roo.bootstrap.NavGroup} this
4119              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4120              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4121          */
4122         'changed': true
4123      });
4124     
4125 };
4126
4127 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4128     
4129     align: '',
4130     inverse: false,
4131     form: false,
4132     type: 'nav',
4133     navId : '',
4134     // private
4135     
4136     navItems : false, 
4137     
4138     getAutoCreate : function()
4139     {
4140         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4141         
4142         cfg = {
4143             tag : 'ul',
4144             cls: 'nav' 
4145         };
4146         
4147         if (['tabs','pills'].indexOf(this.type)!==-1) {
4148             cfg.cls += ' nav-' + this.type
4149         } else {
4150             if (this.type!=='nav') {
4151                 Roo.log('nav type must be nav/tabs/pills')
4152             }
4153             cfg.cls += ' navbar-nav'
4154         }
4155         
4156         if (this.parent() && this.parent().sidebar) {
4157             cfg = {
4158                 tag: 'ul',
4159                 cls: 'dashboard-menu sidebar-menu'
4160             };
4161             
4162             return cfg;
4163         }
4164         
4165         if (this.form === true) {
4166             cfg = {
4167                 tag: 'form',
4168                 cls: 'navbar-form'
4169             };
4170             
4171             if (this.align === 'right') {
4172                 cfg.cls += ' navbar-right';
4173             } else {
4174                 cfg.cls += ' navbar-left';
4175             }
4176         }
4177         
4178         if (this.align === 'right') {
4179             cfg.cls += ' navbar-right';
4180         }
4181         
4182         if (this.inverse) {
4183             cfg.cls += ' navbar-inverse';
4184             
4185         }
4186         
4187         
4188         return cfg;
4189     },
4190     /**
4191     * sets the active Navigation item
4192     * @param {Roo.bootstrap.NavItem} the new current navitem
4193     */
4194     setActiveItem : function(item)
4195     {
4196         var prev = false;
4197         Roo.each(this.navItems, function(v){
4198             if (v == item) {
4199                 return ;
4200             }
4201             if (v.isActive()) {
4202                 v.setActive(false, true);
4203                 prev = v;
4204                 
4205             }
4206             
4207         });
4208
4209         item.setActive(true, true);
4210         this.fireEvent('changed', this, item, prev);
4211         
4212         
4213     },
4214     /**
4215     * gets the active Navigation item
4216     * @return {Roo.bootstrap.NavItem} the current navitem
4217     */
4218     getActive : function()
4219     {
4220         
4221         var prev = false;
4222         Roo.each(this.navItems, function(v){
4223             
4224             if (v.isActive()) {
4225                 prev = v;
4226                 
4227             }
4228             
4229         });
4230         return prev;
4231     },
4232     
4233     indexOfNav : function()
4234     {
4235         
4236         var prev = false;
4237         Roo.each(this.navItems, function(v,i){
4238             
4239             if (v.isActive()) {
4240                 prev = i;
4241                 
4242             }
4243             
4244         });
4245         return prev;
4246     },
4247     /**
4248     * adds a Navigation item
4249     * @param {Roo.bootstrap.NavItem} the navitem to add
4250     */
4251     addItem : function(cfg)
4252     {
4253         var cn = new Roo.bootstrap.NavItem(cfg);
4254         this.register(cn);
4255         cn.parentId = this.id;
4256         cn.onRender(this.el, null);
4257         return cn;
4258     },
4259     /**
4260     * register a Navigation item
4261     * @param {Roo.bootstrap.NavItem} the navitem to add
4262     */
4263     register : function(item)
4264     {
4265         this.navItems.push( item);
4266         item.navId = this.navId;
4267     
4268     },
4269     
4270     /**
4271     * clear all the Navigation item
4272     */
4273    
4274     clearAll : function()
4275     {
4276         this.navItems = [];
4277         this.el.dom.innerHTML = '';
4278     },
4279     
4280     getNavItem: function(tabId)
4281     {
4282         var ret = false;
4283         Roo.each(this.navItems, function(e) {
4284             if (e.tabId == tabId) {
4285                ret =  e;
4286                return false;
4287             }
4288             return true;
4289             
4290         });
4291         return ret;
4292     },
4293     
4294     setActiveNext : function()
4295     {
4296         var i = this.indexOfNav(this.getActive());
4297         if (i > this.navItems.length) {
4298             return;
4299         }
4300         this.setActiveItem(this.navItems[i+1]);
4301     },
4302     setActivePrev : function()
4303     {
4304         var i = this.indexOfNav(this.getActive());
4305         if (i  < 1) {
4306             return;
4307         }
4308         this.setActiveItem(this.navItems[i-1]);
4309     },
4310     clearWasActive : function(except) {
4311         Roo.each(this.navItems, function(e) {
4312             if (e.tabId != except.tabId && e.was_active) {
4313                e.was_active = false;
4314                return false;
4315             }
4316             return true;
4317             
4318         });
4319     },
4320     getWasActive : function ()
4321     {
4322         var r = false;
4323         Roo.each(this.navItems, function(e) {
4324             if (e.was_active) {
4325                r = e;
4326                return false;
4327             }
4328             return true;
4329             
4330         });
4331         return r;
4332     }
4333     
4334     
4335 });
4336
4337  
4338 Roo.apply(Roo.bootstrap.NavGroup, {
4339     
4340     groups: {},
4341      /**
4342     * register a Navigation Group
4343     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4344     */
4345     register : function(navgrp)
4346     {
4347         this.groups[navgrp.navId] = navgrp;
4348         
4349     },
4350     /**
4351     * fetch a Navigation Group based on the navigation ID
4352     * @param {string} the navgroup to add
4353     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4354     */
4355     get: function(navId) {
4356         if (typeof(this.groups[navId]) == 'undefined') {
4357             return false;
4358             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4359         }
4360         return this.groups[navId] ;
4361     }
4362     
4363     
4364     
4365 });
4366
4367  /*
4368  * - LGPL
4369  *
4370  * row
4371  * 
4372  */
4373
4374 /**
4375  * @class Roo.bootstrap.NavItem
4376  * @extends Roo.bootstrap.Component
4377  * Bootstrap Navbar.NavItem class
4378  * @cfg {String} href  link to
4379  * @cfg {String} html content of button
4380  * @cfg {String} badge text inside badge
4381  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4382  * @cfg {String} glyphicon name of glyphicon
4383  * @cfg {String} icon name of font awesome icon
4384  * @cfg {Boolean} active Is item active
4385  * @cfg {Boolean} disabled Is item disabled
4386  
4387  * @cfg {Boolean} preventDefault (true | false) default false
4388  * @cfg {String} tabId the tab that this item activates.
4389  * @cfg {String} tagtype (a|span) render as a href or span?
4390  * @cfg {Boolean} animateRef (true|false) link to element default false  
4391   
4392  * @constructor
4393  * Create a new Navbar Item
4394  * @param {Object} config The config object
4395  */
4396 Roo.bootstrap.NavItem = function(config){
4397     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4398     this.addEvents({
4399         // raw events
4400         /**
4401          * @event click
4402          * The raw click event for the entire grid.
4403          * @param {Roo.EventObject} e
4404          */
4405         "click" : true,
4406          /**
4407             * @event changed
4408             * Fires when the active item active state changes
4409             * @param {Roo.bootstrap.NavItem} this
4410             * @param {boolean} state the new state
4411              
4412          */
4413         'changed': true,
4414         /**
4415             * @event scrollto
4416             * Fires when scroll to element
4417             * @param {Roo.bootstrap.NavItem} this
4418             * @param {Object} options
4419             * @param {Roo.EventObject} e
4420              
4421          */
4422         'scrollto': true
4423     });
4424    
4425 };
4426
4427 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4428     
4429     href: false,
4430     html: '',
4431     badge: '',
4432     icon: false,
4433     glyphicon: false,
4434     active: false,
4435     preventDefault : false,
4436     tabId : false,
4437     tagtype : 'a',
4438     disabled : false,
4439     animateRef : false,
4440     was_active : false,
4441     
4442     getAutoCreate : function(){
4443          
4444         var cfg = {
4445             tag: 'li',
4446             cls: 'nav-item'
4447             
4448         };
4449         
4450         if (this.active) {
4451             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4452         }
4453         if (this.disabled) {
4454             cfg.cls += ' disabled';
4455         }
4456         
4457         if (this.href || this.html || this.glyphicon || this.icon) {
4458             cfg.cn = [
4459                 {
4460                     tag: this.tagtype,
4461                     href : this.href || "#",
4462                     html: this.html || ''
4463                 }
4464             ];
4465             
4466             if (this.icon) {
4467                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4468             }
4469
4470             if(this.glyphicon) {
4471                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4472             }
4473             
4474             if (this.menu) {
4475                 
4476                 cfg.cn[0].html += " <span class='caret'></span>";
4477              
4478             }
4479             
4480             if (this.badge !== '') {
4481                  
4482                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4483             }
4484         }
4485         
4486         
4487         
4488         return cfg;
4489     },
4490     initEvents: function() 
4491     {
4492         if (typeof (this.menu) != 'undefined') {
4493             this.menu.parentType = this.xtype;
4494             this.menu.triggerEl = this.el;
4495             this.menu = this.addxtype(Roo.apply({}, this.menu));
4496         }
4497         
4498         this.el.select('a',true).on('click', this.onClick, this);
4499         
4500         if(this.tagtype == 'span'){
4501             this.el.select('span',true).on('click', this.onClick, this);
4502         }
4503        
4504         // at this point parent should be available..
4505         this.parent().register(this);
4506     },
4507     
4508     onClick : function(e)
4509     {
4510         if (e.getTarget('.dropdown-menu-item')) {
4511             // did you click on a menu itemm.... - then don't trigger onclick..
4512             return;
4513         }
4514         
4515         if(
4516                 this.preventDefault || 
4517                 this.href == '#' 
4518         ){
4519             Roo.log("NavItem - prevent Default?");
4520             e.preventDefault();
4521         }
4522         
4523         if (this.disabled) {
4524             return;
4525         }
4526         
4527         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4528         if (tg && tg.transition) {
4529             Roo.log("waiting for the transitionend");
4530             return;
4531         }
4532         
4533         
4534         
4535         //Roo.log("fire event clicked");
4536         if(this.fireEvent('click', this, e) === false){
4537             return;
4538         };
4539         
4540         if(this.tagtype == 'span'){
4541             return;
4542         }
4543         
4544         //Roo.log(this.href);
4545         var ael = this.el.select('a',true).first();
4546         //Roo.log(ael);
4547         
4548         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4549             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4550             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4551                 return; // ignore... - it's a 'hash' to another page.
4552             }
4553             Roo.log("NavItem - prevent Default?");
4554             e.preventDefault();
4555             this.scrollToElement(e);
4556         }
4557         
4558         
4559         var p =  this.parent();
4560    
4561         if (['tabs','pills'].indexOf(p.type)!==-1) {
4562             if (typeof(p.setActiveItem) !== 'undefined') {
4563                 p.setActiveItem(this);
4564             }
4565         }
4566         
4567         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4568         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4569             // remove the collapsed menu expand...
4570             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4571         }
4572     },
4573     
4574     isActive: function () {
4575         return this.active
4576     },
4577     setActive : function(state, fire, is_was_active)
4578     {
4579         if (this.active && !state && this.navId) {
4580             this.was_active = true;
4581             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4582             if (nv) {
4583                 nv.clearWasActive(this);
4584             }
4585             
4586         }
4587         this.active = state;
4588         
4589         if (!state ) {
4590             this.el.removeClass('active');
4591         } else if (!this.el.hasClass('active')) {
4592             this.el.addClass('active');
4593         }
4594         if (fire) {
4595             this.fireEvent('changed', this, state);
4596         }
4597         
4598         // show a panel if it's registered and related..
4599         
4600         if (!this.navId || !this.tabId || !state || is_was_active) {
4601             return;
4602         }
4603         
4604         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4605         if (!tg) {
4606             return;
4607         }
4608         var pan = tg.getPanelByName(this.tabId);
4609         if (!pan) {
4610             return;
4611         }
4612         // if we can not flip to new panel - go back to old nav highlight..
4613         if (false == tg.showPanel(pan)) {
4614             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4615             if (nv) {
4616                 var onav = nv.getWasActive();
4617                 if (onav) {
4618                     onav.setActive(true, false, true);
4619                 }
4620             }
4621             
4622         }
4623         
4624         
4625         
4626     },
4627      // this should not be here...
4628     setDisabled : function(state)
4629     {
4630         this.disabled = state;
4631         if (!state ) {
4632             this.el.removeClass('disabled');
4633         } else if (!this.el.hasClass('disabled')) {
4634             this.el.addClass('disabled');
4635         }
4636         
4637     },
4638     
4639     /**
4640      * Fetch the element to display the tooltip on.
4641      * @return {Roo.Element} defaults to this.el
4642      */
4643     tooltipEl : function()
4644     {
4645         return this.el.select('' + this.tagtype + '', true).first();
4646     },
4647     
4648     scrollToElement : function(e)
4649     {
4650         var c = document.body;
4651         
4652         /*
4653          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4654          */
4655         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4656             c = document.documentElement;
4657         }
4658         
4659         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4660         
4661         if(!target){
4662             return;
4663         }
4664
4665         var o = target.calcOffsetsTo(c);
4666         
4667         var options = {
4668             target : target,
4669             value : o[1]
4670         };
4671         
4672         this.fireEvent('scrollto', this, options, e);
4673         
4674         Roo.get(c).scrollTo('top', options.value, true);
4675         
4676         return;
4677     }
4678 });
4679  
4680
4681  /*
4682  * - LGPL
4683  *
4684  * sidebar item
4685  *
4686  *  li
4687  *    <span> icon </span>
4688  *    <span> text </span>
4689  *    <span>badge </span>
4690  */
4691
4692 /**
4693  * @class Roo.bootstrap.NavSidebarItem
4694  * @extends Roo.bootstrap.NavItem
4695  * Bootstrap Navbar.NavSidebarItem class
4696  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4697  * {Boolean} open is the menu open
4698  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4699  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4700  * {String} buttonSize (sm|md|lg)the extra classes for the button
4701  * {Boolean} showArrow show arrow next to the text (default true)
4702  * @constructor
4703  * Create a new Navbar Button
4704  * @param {Object} config The config object
4705  */
4706 Roo.bootstrap.NavSidebarItem = function(config){
4707     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4708     this.addEvents({
4709         // raw events
4710         /**
4711          * @event click
4712          * The raw click event for the entire grid.
4713          * @param {Roo.EventObject} e
4714          */
4715         "click" : true,
4716          /**
4717             * @event changed
4718             * Fires when the active item active state changes
4719             * @param {Roo.bootstrap.NavSidebarItem} this
4720             * @param {boolean} state the new state
4721              
4722          */
4723         'changed': true
4724     });
4725    
4726 };
4727
4728 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4729     
4730     badgeWeight : 'default',
4731     
4732     open: false,
4733     
4734     buttonView : false,
4735     
4736     buttonWeight : 'default',
4737     
4738     buttonSize : 'md',
4739     
4740     showArrow : true,
4741     
4742     getAutoCreate : function(){
4743         
4744         
4745         var a = {
4746                 tag: 'a',
4747                 href : this.href || '#',
4748                 cls: '',
4749                 html : '',
4750                 cn : []
4751         };
4752         
4753         if(this.buttonView){
4754             a = {
4755                 tag: 'button',
4756                 href : this.href || '#',
4757                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4758                 html : this.html,
4759                 cn : []
4760             };
4761         }
4762         
4763         var cfg = {
4764             tag: 'li',
4765             cls: '',
4766             cn: [ a ]
4767         };
4768         
4769         if (this.active) {
4770             cfg.cls += ' active';
4771         }
4772         
4773         if (this.disabled) {
4774             cfg.cls += ' disabled';
4775         }
4776         if (this.open) {
4777             cfg.cls += ' open x-open';
4778         }
4779         // left icon..
4780         if (this.glyphicon || this.icon) {
4781             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4782             a.cn.push({ tag : 'i', cls : c }) ;
4783         }
4784         
4785         if(!this.buttonView){
4786             var span = {
4787                 tag: 'span',
4788                 html : this.html || ''
4789             };
4790
4791             a.cn.push(span);
4792             
4793         }
4794         
4795         if (this.badge !== '') {
4796             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4797         }
4798         
4799         if (this.menu) {
4800             
4801             if(this.showArrow){
4802                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4803             }
4804             
4805             a.cls += ' dropdown-toggle treeview' ;
4806         }
4807         
4808         return cfg;
4809     },
4810     
4811     initEvents : function()
4812     { 
4813         if (typeof (this.menu) != 'undefined') {
4814             this.menu.parentType = this.xtype;
4815             this.menu.triggerEl = this.el;
4816             this.menu = this.addxtype(Roo.apply({}, this.menu));
4817         }
4818         
4819         this.el.on('click', this.onClick, this);
4820         
4821         if(this.badge !== ''){
4822             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4823         }
4824         
4825     },
4826     
4827     onClick : function(e)
4828     {
4829         if(this.disabled){
4830             e.preventDefault();
4831             return;
4832         }
4833         
4834         if(this.preventDefault){
4835             e.preventDefault();
4836         }
4837         
4838         this.fireEvent('click', this);
4839     },
4840     
4841     disable : function()
4842     {
4843         this.setDisabled(true);
4844     },
4845     
4846     enable : function()
4847     {
4848         this.setDisabled(false);
4849     },
4850     
4851     setDisabled : function(state)
4852     {
4853         if(this.disabled == state){
4854             return;
4855         }
4856         
4857         this.disabled = state;
4858         
4859         if (state) {
4860             this.el.addClass('disabled');
4861             return;
4862         }
4863         
4864         this.el.removeClass('disabled');
4865         
4866         return;
4867     },
4868     
4869     setActive : function(state)
4870     {
4871         if(this.active == state){
4872             return;
4873         }
4874         
4875         this.active = state;
4876         
4877         if (state) {
4878             this.el.addClass('active');
4879             return;
4880         }
4881         
4882         this.el.removeClass('active');
4883         
4884         return;
4885     },
4886     
4887     isActive: function () 
4888     {
4889         return this.active;
4890     },
4891     
4892     setBadge : function(str)
4893     {
4894         if(!this.badgeEl){
4895             return;
4896         }
4897         
4898         this.badgeEl.dom.innerHTML = str;
4899     }
4900     
4901    
4902      
4903  
4904 });
4905  
4906
4907  /*
4908  * - LGPL
4909  *
4910  * row
4911  * 
4912  */
4913
4914 /**
4915  * @class Roo.bootstrap.Row
4916  * @extends Roo.bootstrap.Component
4917  * Bootstrap Row class (contains columns...)
4918  * 
4919  * @constructor
4920  * Create a new Row
4921  * @param {Object} config The config object
4922  */
4923
4924 Roo.bootstrap.Row = function(config){
4925     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4926 };
4927
4928 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4929     
4930     getAutoCreate : function(){
4931        return {
4932             cls: 'row clearfix'
4933        };
4934     }
4935     
4936     
4937 });
4938
4939  
4940
4941  /*
4942  * - LGPL
4943  *
4944  * element
4945  * 
4946  */
4947
4948 /**
4949  * @class Roo.bootstrap.Element
4950  * @extends Roo.bootstrap.Component
4951  * Bootstrap Element class
4952  * @cfg {String} html contents of the element
4953  * @cfg {String} tag tag of the element
4954  * @cfg {String} cls class of the element
4955  * @cfg {Boolean} preventDefault (true|false) default false
4956  * @cfg {Boolean} clickable (true|false) default false
4957  * 
4958  * @constructor
4959  * Create a new Element
4960  * @param {Object} config The config object
4961  */
4962
4963 Roo.bootstrap.Element = function(config){
4964     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4965     
4966     this.addEvents({
4967         // raw events
4968         /**
4969          * @event click
4970          * When a element is chick
4971          * @param {Roo.bootstrap.Element} this
4972          * @param {Roo.EventObject} e
4973          */
4974         "click" : true
4975     });
4976 };
4977
4978 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4979     
4980     tag: 'div',
4981     cls: '',
4982     html: '',
4983     preventDefault: false, 
4984     clickable: false,
4985     
4986     getAutoCreate : function(){
4987         
4988         var cfg = {
4989             tag: this.tag,
4990             // cls: this.cls, double assign in parent class Component.js :: onRender
4991             html: this.html
4992         };
4993         
4994         return cfg;
4995     },
4996     
4997     initEvents: function() 
4998     {
4999         Roo.bootstrap.Element.superclass.initEvents.call(this);
5000         
5001         if(this.clickable){
5002             this.el.on('click', this.onClick, this);
5003         }
5004         
5005     },
5006     
5007     onClick : function(e)
5008     {
5009         if(this.preventDefault){
5010             e.preventDefault();
5011         }
5012         
5013         this.fireEvent('click', this, e);
5014     },
5015     
5016     getValue : function()
5017     {
5018         return this.el.dom.innerHTML;
5019     },
5020     
5021     setValue : function(value)
5022     {
5023         this.el.dom.innerHTML = value;
5024     }
5025    
5026 });
5027
5028  
5029
5030  /*
5031  * - LGPL
5032  *
5033  * pagination
5034  * 
5035  */
5036
5037 /**
5038  * @class Roo.bootstrap.Pagination
5039  * @extends Roo.bootstrap.Component
5040  * Bootstrap Pagination class
5041  * @cfg {String} size xs | sm | md | lg
5042  * @cfg {Boolean} inverse false | true
5043  * 
5044  * @constructor
5045  * Create a new Pagination
5046  * @param {Object} config The config object
5047  */
5048
5049 Roo.bootstrap.Pagination = function(config){
5050     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5051 };
5052
5053 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5054     
5055     cls: false,
5056     size: false,
5057     inverse: false,
5058     
5059     getAutoCreate : function(){
5060         var cfg = {
5061             tag: 'ul',
5062                 cls: 'pagination'
5063         };
5064         if (this.inverse) {
5065             cfg.cls += ' inverse';
5066         }
5067         if (this.html) {
5068             cfg.html=this.html;
5069         }
5070         if (this.cls) {
5071             cfg.cls += " " + this.cls;
5072         }
5073         return cfg;
5074     }
5075    
5076 });
5077
5078  
5079
5080  /*
5081  * - LGPL
5082  *
5083  * Pagination item
5084  * 
5085  */
5086
5087
5088 /**
5089  * @class Roo.bootstrap.PaginationItem
5090  * @extends Roo.bootstrap.Component
5091  * Bootstrap PaginationItem class
5092  * @cfg {String} html text
5093  * @cfg {String} href the link
5094  * @cfg {Boolean} preventDefault (true | false) default true
5095  * @cfg {Boolean} active (true | false) default false
5096  * @cfg {Boolean} disabled default false
5097  * 
5098  * 
5099  * @constructor
5100  * Create a new PaginationItem
5101  * @param {Object} config The config object
5102  */
5103
5104
5105 Roo.bootstrap.PaginationItem = function(config){
5106     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5107     this.addEvents({
5108         // raw events
5109         /**
5110          * @event click
5111          * The raw click event for the entire grid.
5112          * @param {Roo.EventObject} e
5113          */
5114         "click" : true
5115     });
5116 };
5117
5118 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5119     
5120     href : false,
5121     html : false,
5122     preventDefault: true,
5123     active : false,
5124     cls : false,
5125     disabled: false,
5126     
5127     getAutoCreate : function(){
5128         var cfg= {
5129             tag: 'li',
5130             cn: [
5131                 {
5132                     tag : 'a',
5133                     href : this.href ? this.href : '#',
5134                     html : this.html ? this.html : ''
5135                 }
5136             ]
5137         };
5138         
5139         if(this.cls){
5140             cfg.cls = this.cls;
5141         }
5142         
5143         if(this.disabled){
5144             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5145         }
5146         
5147         if(this.active){
5148             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5149         }
5150         
5151         return cfg;
5152     },
5153     
5154     initEvents: function() {
5155         
5156         this.el.on('click', this.onClick, this);
5157         
5158     },
5159     onClick : function(e)
5160     {
5161         Roo.log('PaginationItem on click ');
5162         if(this.preventDefault){
5163             e.preventDefault();
5164         }
5165         
5166         if(this.disabled){
5167             return;
5168         }
5169         
5170         this.fireEvent('click', this, e);
5171     }
5172    
5173 });
5174
5175  
5176
5177  /*
5178  * - LGPL
5179  *
5180  * slider
5181  * 
5182  */
5183
5184
5185 /**
5186  * @class Roo.bootstrap.Slider
5187  * @extends Roo.bootstrap.Component
5188  * Bootstrap Slider class
5189  *    
5190  * @constructor
5191  * Create a new Slider
5192  * @param {Object} config The config object
5193  */
5194
5195 Roo.bootstrap.Slider = function(config){
5196     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5197 };
5198
5199 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5200     
5201     getAutoCreate : function(){
5202         
5203         var cfg = {
5204             tag: 'div',
5205             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5206             cn: [
5207                 {
5208                     tag: 'a',
5209                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5210                 }
5211             ]
5212         };
5213         
5214         return cfg;
5215     }
5216    
5217 });
5218
5219  /*
5220  * Based on:
5221  * Ext JS Library 1.1.1
5222  * Copyright(c) 2006-2007, Ext JS, LLC.
5223  *
5224  * Originally Released Under LGPL - original licence link has changed is not relivant.
5225  *
5226  * Fork - LGPL
5227  * <script type="text/javascript">
5228  */
5229  
5230
5231 /**
5232  * @class Roo.grid.ColumnModel
5233  * @extends Roo.util.Observable
5234  * This is the default implementation of a ColumnModel used by the Grid. It defines
5235  * the columns in the grid.
5236  * <br>Usage:<br>
5237  <pre><code>
5238  var colModel = new Roo.grid.ColumnModel([
5239         {header: "Ticker", width: 60, sortable: true, locked: true},
5240         {header: "Company Name", width: 150, sortable: true},
5241         {header: "Market Cap.", width: 100, sortable: true},
5242         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5243         {header: "Employees", width: 100, sortable: true, resizable: false}
5244  ]);
5245  </code></pre>
5246  * <p>
5247  
5248  * The config options listed for this class are options which may appear in each
5249  * individual column definition.
5250  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5251  * @constructor
5252  * @param {Object} config An Array of column config objects. See this class's
5253  * config objects for details.
5254 */
5255 Roo.grid.ColumnModel = function(config){
5256         /**
5257      * The config passed into the constructor
5258      */
5259     this.config = config;
5260     this.lookup = {};
5261
5262     // if no id, create one
5263     // if the column does not have a dataIndex mapping,
5264     // map it to the order it is in the config
5265     for(var i = 0, len = config.length; i < len; i++){
5266         var c = config[i];
5267         if(typeof c.dataIndex == "undefined"){
5268             c.dataIndex = i;
5269         }
5270         if(typeof c.renderer == "string"){
5271             c.renderer = Roo.util.Format[c.renderer];
5272         }
5273         if(typeof c.id == "undefined"){
5274             c.id = Roo.id();
5275         }
5276         if(c.editor && c.editor.xtype){
5277             c.editor  = Roo.factory(c.editor, Roo.grid);
5278         }
5279         if(c.editor && c.editor.isFormField){
5280             c.editor = new Roo.grid.GridEditor(c.editor);
5281         }
5282         this.lookup[c.id] = c;
5283     }
5284
5285     /**
5286      * The width of columns which have no width specified (defaults to 100)
5287      * @type Number
5288      */
5289     this.defaultWidth = 100;
5290
5291     /**
5292      * Default sortable of columns which have no sortable specified (defaults to false)
5293      * @type Boolean
5294      */
5295     this.defaultSortable = false;
5296
5297     this.addEvents({
5298         /**
5299              * @event widthchange
5300              * Fires when the width of a column changes.
5301              * @param {ColumnModel} this
5302              * @param {Number} columnIndex The column index
5303              * @param {Number} newWidth The new width
5304              */
5305             "widthchange": true,
5306         /**
5307              * @event headerchange
5308              * Fires when the text of a header changes.
5309              * @param {ColumnModel} this
5310              * @param {Number} columnIndex The column index
5311              * @param {Number} newText The new header text
5312              */
5313             "headerchange": true,
5314         /**
5315              * @event hiddenchange
5316              * Fires when a column is hidden or "unhidden".
5317              * @param {ColumnModel} this
5318              * @param {Number} columnIndex The column index
5319              * @param {Boolean} hidden true if hidden, false otherwise
5320              */
5321             "hiddenchange": true,
5322             /**
5323          * @event columnmoved
5324          * Fires when a column is moved.
5325          * @param {ColumnModel} this
5326          * @param {Number} oldIndex
5327          * @param {Number} newIndex
5328          */
5329         "columnmoved" : true,
5330         /**
5331          * @event columlockchange
5332          * Fires when a column's locked state is changed
5333          * @param {ColumnModel} this
5334          * @param {Number} colIndex
5335          * @param {Boolean} locked true if locked
5336          */
5337         "columnlockchange" : true
5338     });
5339     Roo.grid.ColumnModel.superclass.constructor.call(this);
5340 };
5341 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5342     /**
5343      * @cfg {String} header The header text to display in the Grid view.
5344      */
5345     /**
5346      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5347      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5348      * specified, the column's index is used as an index into the Record's data Array.
5349      */
5350     /**
5351      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5352      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5353      */
5354     /**
5355      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5356      * Defaults to the value of the {@link #defaultSortable} property.
5357      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5358      */
5359     /**
5360      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5361      */
5362     /**
5363      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5364      */
5365     /**
5366      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5367      */
5368     /**
5369      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5370      */
5371     /**
5372      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5373      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5374      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5375      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5376      */
5377        /**
5378      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5379      */
5380     /**
5381      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5382      */
5383     /**
5384      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5385      */
5386     /**
5387      * @cfg {String} cursor (Optional)
5388      */
5389     /**
5390      * @cfg {String} tooltip (Optional)
5391      */
5392     /**
5393      * @cfg {Number} xs (Optional)
5394      */
5395     /**
5396      * @cfg {Number} sm (Optional)
5397      */
5398     /**
5399      * @cfg {Number} md (Optional)
5400      */
5401     /**
5402      * @cfg {Number} lg (Optional)
5403      */
5404     /**
5405      * Returns the id of the column at the specified index.
5406      * @param {Number} index The column index
5407      * @return {String} the id
5408      */
5409     getColumnId : function(index){
5410         return this.config[index].id;
5411     },
5412
5413     /**
5414      * Returns the column for a specified id.
5415      * @param {String} id The column id
5416      * @return {Object} the column
5417      */
5418     getColumnById : function(id){
5419         return this.lookup[id];
5420     },
5421
5422     
5423     /**
5424      * Returns the column for a specified dataIndex.
5425      * @param {String} dataIndex The column dataIndex
5426      * @return {Object|Boolean} the column or false if not found
5427      */
5428     getColumnByDataIndex: function(dataIndex){
5429         var index = this.findColumnIndex(dataIndex);
5430         return index > -1 ? this.config[index] : false;
5431     },
5432     
5433     /**
5434      * Returns the index for a specified column id.
5435      * @param {String} id The column id
5436      * @return {Number} the index, or -1 if not found
5437      */
5438     getIndexById : function(id){
5439         for(var i = 0, len = this.config.length; i < len; i++){
5440             if(this.config[i].id == id){
5441                 return i;
5442             }
5443         }
5444         return -1;
5445     },
5446     
5447     /**
5448      * Returns the index for a specified column dataIndex.
5449      * @param {String} dataIndex The column dataIndex
5450      * @return {Number} the index, or -1 if not found
5451      */
5452     
5453     findColumnIndex : function(dataIndex){
5454         for(var i = 0, len = this.config.length; i < len; i++){
5455             if(this.config[i].dataIndex == dataIndex){
5456                 return i;
5457             }
5458         }
5459         return -1;
5460     },
5461     
5462     
5463     moveColumn : function(oldIndex, newIndex){
5464         var c = this.config[oldIndex];
5465         this.config.splice(oldIndex, 1);
5466         this.config.splice(newIndex, 0, c);
5467         this.dataMap = null;
5468         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5469     },
5470
5471     isLocked : function(colIndex){
5472         return this.config[colIndex].locked === true;
5473     },
5474
5475     setLocked : function(colIndex, value, suppressEvent){
5476         if(this.isLocked(colIndex) == value){
5477             return;
5478         }
5479         this.config[colIndex].locked = value;
5480         if(!suppressEvent){
5481             this.fireEvent("columnlockchange", this, colIndex, value);
5482         }
5483     },
5484
5485     getTotalLockedWidth : function(){
5486         var totalWidth = 0;
5487         for(var i = 0; i < this.config.length; i++){
5488             if(this.isLocked(i) && !this.isHidden(i)){
5489                 this.totalWidth += this.getColumnWidth(i);
5490             }
5491         }
5492         return totalWidth;
5493     },
5494
5495     getLockedCount : function(){
5496         for(var i = 0, len = this.config.length; i < len; i++){
5497             if(!this.isLocked(i)){
5498                 return i;
5499             }
5500         }
5501         
5502         return this.config.length;
5503     },
5504
5505     /**
5506      * Returns the number of columns.
5507      * @return {Number}
5508      */
5509     getColumnCount : function(visibleOnly){
5510         if(visibleOnly === true){
5511             var c = 0;
5512             for(var i = 0, len = this.config.length; i < len; i++){
5513                 if(!this.isHidden(i)){
5514                     c++;
5515                 }
5516             }
5517             return c;
5518         }
5519         return this.config.length;
5520     },
5521
5522     /**
5523      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5524      * @param {Function} fn
5525      * @param {Object} scope (optional)
5526      * @return {Array} result
5527      */
5528     getColumnsBy : function(fn, scope){
5529         var r = [];
5530         for(var i = 0, len = this.config.length; i < len; i++){
5531             var c = this.config[i];
5532             if(fn.call(scope||this, c, i) === true){
5533                 r[r.length] = c;
5534             }
5535         }
5536         return r;
5537     },
5538
5539     /**
5540      * Returns true if the specified column is sortable.
5541      * @param {Number} col The column index
5542      * @return {Boolean}
5543      */
5544     isSortable : function(col){
5545         if(typeof this.config[col].sortable == "undefined"){
5546             return this.defaultSortable;
5547         }
5548         return this.config[col].sortable;
5549     },
5550
5551     /**
5552      * Returns the rendering (formatting) function defined for the column.
5553      * @param {Number} col The column index.
5554      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5555      */
5556     getRenderer : function(col){
5557         if(!this.config[col].renderer){
5558             return Roo.grid.ColumnModel.defaultRenderer;
5559         }
5560         return this.config[col].renderer;
5561     },
5562
5563     /**
5564      * Sets the rendering (formatting) function for a column.
5565      * @param {Number} col The column index
5566      * @param {Function} fn The function to use to process the cell's raw data
5567      * to return HTML markup for the grid view. The render function is called with
5568      * the following parameters:<ul>
5569      * <li>Data value.</li>
5570      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5571      * <li>css A CSS style string to apply to the table cell.</li>
5572      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5573      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5574      * <li>Row index</li>
5575      * <li>Column index</li>
5576      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5577      */
5578     setRenderer : function(col, fn){
5579         this.config[col].renderer = fn;
5580     },
5581
5582     /**
5583      * Returns the width for the specified column.
5584      * @param {Number} col The column index
5585      * @return {Number}
5586      */
5587     getColumnWidth : function(col){
5588         return this.config[col].width * 1 || this.defaultWidth;
5589     },
5590
5591     /**
5592      * Sets the width for a column.
5593      * @param {Number} col The column index
5594      * @param {Number} width The new width
5595      */
5596     setColumnWidth : function(col, width, suppressEvent){
5597         this.config[col].width = width;
5598         this.totalWidth = null;
5599         if(!suppressEvent){
5600              this.fireEvent("widthchange", this, col, width);
5601         }
5602     },
5603
5604     /**
5605      * Returns the total width of all columns.
5606      * @param {Boolean} includeHidden True to include hidden column widths
5607      * @return {Number}
5608      */
5609     getTotalWidth : function(includeHidden){
5610         if(!this.totalWidth){
5611             this.totalWidth = 0;
5612             for(var i = 0, len = this.config.length; i < len; i++){
5613                 if(includeHidden || !this.isHidden(i)){
5614                     this.totalWidth += this.getColumnWidth(i);
5615                 }
5616             }
5617         }
5618         return this.totalWidth;
5619     },
5620
5621     /**
5622      * Returns the header for the specified column.
5623      * @param {Number} col The column index
5624      * @return {String}
5625      */
5626     getColumnHeader : function(col){
5627         return this.config[col].header;
5628     },
5629
5630     /**
5631      * Sets the header for a column.
5632      * @param {Number} col The column index
5633      * @param {String} header The new header
5634      */
5635     setColumnHeader : function(col, header){
5636         this.config[col].header = header;
5637         this.fireEvent("headerchange", this, col, header);
5638     },
5639
5640     /**
5641      * Returns the tooltip for the specified column.
5642      * @param {Number} col The column index
5643      * @return {String}
5644      */
5645     getColumnTooltip : function(col){
5646             return this.config[col].tooltip;
5647     },
5648     /**
5649      * Sets the tooltip for a column.
5650      * @param {Number} col The column index
5651      * @param {String} tooltip The new tooltip
5652      */
5653     setColumnTooltip : function(col, tooltip){
5654             this.config[col].tooltip = tooltip;
5655     },
5656
5657     /**
5658      * Returns the dataIndex for the specified column.
5659      * @param {Number} col The column index
5660      * @return {Number}
5661      */
5662     getDataIndex : function(col){
5663         return this.config[col].dataIndex;
5664     },
5665
5666     /**
5667      * Sets the dataIndex for a column.
5668      * @param {Number} col The column index
5669      * @param {Number} dataIndex The new dataIndex
5670      */
5671     setDataIndex : function(col, dataIndex){
5672         this.config[col].dataIndex = dataIndex;
5673     },
5674
5675     
5676     
5677     /**
5678      * Returns true if the cell is editable.
5679      * @param {Number} colIndex The column index
5680      * @param {Number} rowIndex The row index - this is nto actually used..?
5681      * @return {Boolean}
5682      */
5683     isCellEditable : function(colIndex, rowIndex){
5684         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5685     },
5686
5687     /**
5688      * Returns the editor defined for the cell/column.
5689      * return false or null to disable editing.
5690      * @param {Number} colIndex The column index
5691      * @param {Number} rowIndex The row index
5692      * @return {Object}
5693      */
5694     getCellEditor : function(colIndex, rowIndex){
5695         return this.config[colIndex].editor;
5696     },
5697
5698     /**
5699      * Sets if a column is editable.
5700      * @param {Number} col The column index
5701      * @param {Boolean} editable True if the column is editable
5702      */
5703     setEditable : function(col, editable){
5704         this.config[col].editable = editable;
5705     },
5706
5707
5708     /**
5709      * Returns true if the column is hidden.
5710      * @param {Number} colIndex The column index
5711      * @return {Boolean}
5712      */
5713     isHidden : function(colIndex){
5714         return this.config[colIndex].hidden;
5715     },
5716
5717
5718     /**
5719      * Returns true if the column width cannot be changed
5720      */
5721     isFixed : function(colIndex){
5722         return this.config[colIndex].fixed;
5723     },
5724
5725     /**
5726      * Returns true if the column can be resized
5727      * @return {Boolean}
5728      */
5729     isResizable : function(colIndex){
5730         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5731     },
5732     /**
5733      * Sets if a column is hidden.
5734      * @param {Number} colIndex The column index
5735      * @param {Boolean} hidden True if the column is hidden
5736      */
5737     setHidden : function(colIndex, hidden){
5738         this.config[colIndex].hidden = hidden;
5739         this.totalWidth = null;
5740         this.fireEvent("hiddenchange", this, colIndex, hidden);
5741     },
5742
5743     /**
5744      * Sets the editor for a column.
5745      * @param {Number} col The column index
5746      * @param {Object} editor The editor object
5747      */
5748     setEditor : function(col, editor){
5749         this.config[col].editor = editor;
5750     }
5751 });
5752
5753 Roo.grid.ColumnModel.defaultRenderer = function(value)
5754 {
5755     if(typeof value == "object") {
5756         return value;
5757     }
5758         if(typeof value == "string" && value.length < 1){
5759             return "&#160;";
5760         }
5761     
5762         return String.format("{0}", value);
5763 };
5764
5765 // Alias for backwards compatibility
5766 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5767 /*
5768  * Based on:
5769  * Ext JS Library 1.1.1
5770  * Copyright(c) 2006-2007, Ext JS, LLC.
5771  *
5772  * Originally Released Under LGPL - original licence link has changed is not relivant.
5773  *
5774  * Fork - LGPL
5775  * <script type="text/javascript">
5776  */
5777  
5778 /**
5779  * @class Roo.LoadMask
5780  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5781  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5782  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5783  * element's UpdateManager load indicator and will be destroyed after the initial load.
5784  * @constructor
5785  * Create a new LoadMask
5786  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5787  * @param {Object} config The config object
5788  */
5789 Roo.LoadMask = function(el, config){
5790     this.el = Roo.get(el);
5791     Roo.apply(this, config);
5792     if(this.store){
5793         this.store.on('beforeload', this.onBeforeLoad, this);
5794         this.store.on('load', this.onLoad, this);
5795         this.store.on('loadexception', this.onLoadException, this);
5796         this.removeMask = false;
5797     }else{
5798         var um = this.el.getUpdateManager();
5799         um.showLoadIndicator = false; // disable the default indicator
5800         um.on('beforeupdate', this.onBeforeLoad, this);
5801         um.on('update', this.onLoad, this);
5802         um.on('failure', this.onLoad, this);
5803         this.removeMask = true;
5804     }
5805 };
5806
5807 Roo.LoadMask.prototype = {
5808     /**
5809      * @cfg {Boolean} removeMask
5810      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5811      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5812      */
5813     /**
5814      * @cfg {String} msg
5815      * The text to display in a centered loading message box (defaults to 'Loading...')
5816      */
5817     msg : 'Loading...',
5818     /**
5819      * @cfg {String} msgCls
5820      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5821      */
5822     msgCls : 'x-mask-loading',
5823
5824     /**
5825      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5826      * @type Boolean
5827      */
5828     disabled: false,
5829
5830     /**
5831      * Disables the mask to prevent it from being displayed
5832      */
5833     disable : function(){
5834        this.disabled = true;
5835     },
5836
5837     /**
5838      * Enables the mask so that it can be displayed
5839      */
5840     enable : function(){
5841         this.disabled = false;
5842     },
5843     
5844     onLoadException : function()
5845     {
5846         Roo.log(arguments);
5847         
5848         if (typeof(arguments[3]) != 'undefined') {
5849             Roo.MessageBox.alert("Error loading",arguments[3]);
5850         } 
5851         /*
5852         try {
5853             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5854                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5855             }   
5856         } catch(e) {
5857             
5858         }
5859         */
5860     
5861         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5862     },
5863     // private
5864     onLoad : function()
5865     {
5866         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5867     },
5868
5869     // private
5870     onBeforeLoad : function(){
5871         if(!this.disabled){
5872             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5873         }
5874     },
5875
5876     // private
5877     destroy : function(){
5878         if(this.store){
5879             this.store.un('beforeload', this.onBeforeLoad, this);
5880             this.store.un('load', this.onLoad, this);
5881             this.store.un('loadexception', this.onLoadException, this);
5882         }else{
5883             var um = this.el.getUpdateManager();
5884             um.un('beforeupdate', this.onBeforeLoad, this);
5885             um.un('update', this.onLoad, this);
5886             um.un('failure', this.onLoad, this);
5887         }
5888     }
5889 };/*
5890  * - LGPL
5891  *
5892  * table
5893  * 
5894  */
5895
5896 /**
5897  * @class Roo.bootstrap.Table
5898  * @extends Roo.bootstrap.Component
5899  * Bootstrap Table class
5900  * @cfg {String} cls table class
5901  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5902  * @cfg {String} bgcolor Specifies the background color for a table
5903  * @cfg {Number} border Specifies whether the table cells should have borders or not
5904  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5905  * @cfg {Number} cellspacing Specifies the space between cells
5906  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5907  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5908  * @cfg {String} sortable Specifies that the table should be sortable
5909  * @cfg {String} summary Specifies a summary of the content of a table
5910  * @cfg {Number} width Specifies the width of a table
5911  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5912  * 
5913  * @cfg {boolean} striped Should the rows be alternative striped
5914  * @cfg {boolean} bordered Add borders to the table
5915  * @cfg {boolean} hover Add hover highlighting
5916  * @cfg {boolean} condensed Format condensed
5917  * @cfg {boolean} responsive Format condensed
5918  * @cfg {Boolean} loadMask (true|false) default false
5919  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5920  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5921  * @cfg {Boolean} rowSelection (true|false) default false
5922  * @cfg {Boolean} cellSelection (true|false) default false
5923  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5924  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5925  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5926  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
5927  
5928  * 
5929  * @constructor
5930  * Create a new Table
5931  * @param {Object} config The config object
5932  */
5933
5934 Roo.bootstrap.Table = function(config){
5935     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5936     
5937   
5938     
5939     // BC...
5940     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5941     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5942     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5943     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5944     
5945     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5946     if (this.sm) {
5947         this.sm.grid = this;
5948         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5949         this.sm = this.selModel;
5950         this.sm.xmodule = this.xmodule || false;
5951     }
5952     
5953     if (this.cm && typeof(this.cm.config) == 'undefined') {
5954         this.colModel = new Roo.grid.ColumnModel(this.cm);
5955         this.cm = this.colModel;
5956         this.cm.xmodule = this.xmodule || false;
5957     }
5958     if (this.store) {
5959         this.store= Roo.factory(this.store, Roo.data);
5960         this.ds = this.store;
5961         this.ds.xmodule = this.xmodule || false;
5962          
5963     }
5964     if (this.footer && this.store) {
5965         this.footer.dataSource = this.ds;
5966         this.footer = Roo.factory(this.footer);
5967     }
5968     
5969     /** @private */
5970     this.addEvents({
5971         /**
5972          * @event cellclick
5973          * Fires when a cell is clicked
5974          * @param {Roo.bootstrap.Table} this
5975          * @param {Roo.Element} el
5976          * @param {Number} rowIndex
5977          * @param {Number} columnIndex
5978          * @param {Roo.EventObject} e
5979          */
5980         "cellclick" : true,
5981         /**
5982          * @event celldblclick
5983          * Fires when a cell is double clicked
5984          * @param {Roo.bootstrap.Table} this
5985          * @param {Roo.Element} el
5986          * @param {Number} rowIndex
5987          * @param {Number} columnIndex
5988          * @param {Roo.EventObject} e
5989          */
5990         "celldblclick" : true,
5991         /**
5992          * @event rowclick
5993          * Fires when a row is clicked
5994          * @param {Roo.bootstrap.Table} this
5995          * @param {Roo.Element} el
5996          * @param {Number} rowIndex
5997          * @param {Roo.EventObject} e
5998          */
5999         "rowclick" : true,
6000         /**
6001          * @event rowdblclick
6002          * Fires when a row is double clicked
6003          * @param {Roo.bootstrap.Table} this
6004          * @param {Roo.Element} el
6005          * @param {Number} rowIndex
6006          * @param {Roo.EventObject} e
6007          */
6008         "rowdblclick" : true,
6009         /**
6010          * @event mouseover
6011          * Fires when a mouseover occur
6012          * @param {Roo.bootstrap.Table} this
6013          * @param {Roo.Element} el
6014          * @param {Number} rowIndex
6015          * @param {Number} columnIndex
6016          * @param {Roo.EventObject} e
6017          */
6018         "mouseover" : true,
6019         /**
6020          * @event mouseout
6021          * Fires when a mouseout occur
6022          * @param {Roo.bootstrap.Table} this
6023          * @param {Roo.Element} el
6024          * @param {Number} rowIndex
6025          * @param {Number} columnIndex
6026          * @param {Roo.EventObject} e
6027          */
6028         "mouseout" : true,
6029         /**
6030          * @event rowclass
6031          * Fires when a row is rendered, so you can change add a style to it.
6032          * @param {Roo.bootstrap.Table} this
6033          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6034          */
6035         'rowclass' : true,
6036           /**
6037          * @event rowsrendered
6038          * Fires when all the  rows have been rendered
6039          * @param {Roo.bootstrap.Table} this
6040          */
6041         'rowsrendered' : true,
6042         /**
6043          * @event contextmenu
6044          * The raw contextmenu event for the entire grid.
6045          * @param {Roo.EventObject} e
6046          */
6047         "contextmenu" : true,
6048         /**
6049          * @event rowcontextmenu
6050          * Fires when a row is right clicked
6051          * @param {Roo.bootstrap.Table} this
6052          * @param {Number} rowIndex
6053          * @param {Roo.EventObject} e
6054          */
6055         "rowcontextmenu" : true,
6056         /**
6057          * @event cellcontextmenu
6058          * Fires when a cell is right clicked
6059          * @param {Roo.bootstrap.Table} this
6060          * @param {Number} rowIndex
6061          * @param {Number} cellIndex
6062          * @param {Roo.EventObject} e
6063          */
6064          "cellcontextmenu" : true,
6065          /**
6066          * @event headercontextmenu
6067          * Fires when a header is right clicked
6068          * @param {Roo.bootstrap.Table} this
6069          * @param {Number} columnIndex
6070          * @param {Roo.EventObject} e
6071          */
6072         "headercontextmenu" : true
6073     });
6074 };
6075
6076 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6077     
6078     cls: false,
6079     align: false,
6080     bgcolor: false,
6081     border: false,
6082     cellpadding: false,
6083     cellspacing: false,
6084     frame: false,
6085     rules: false,
6086     sortable: false,
6087     summary: false,
6088     width: false,
6089     striped : false,
6090     scrollBody : false,
6091     bordered: false,
6092     hover:  false,
6093     condensed : false,
6094     responsive : false,
6095     sm : false,
6096     cm : false,
6097     store : false,
6098     loadMask : false,
6099     footerShow : true,
6100     headerShow : true,
6101   
6102     rowSelection : false,
6103     cellSelection : false,
6104     layout : false,
6105     
6106     // Roo.Element - the tbody
6107     mainBody: false,
6108     // Roo.Element - thead element
6109     mainHead: false,
6110     
6111     container: false, // used by gridpanel...
6112     
6113     lazyLoad : false,
6114     
6115     CSS : Roo.util.CSS,
6116     
6117     auto_hide_footer : false,
6118     
6119     getAutoCreate : function()
6120     {
6121         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6122         
6123         cfg = {
6124             tag: 'table',
6125             cls : 'table',
6126             cn : []
6127         };
6128         if (this.scrollBody) {
6129             cfg.cls += ' table-body-fixed';
6130         }    
6131         if (this.striped) {
6132             cfg.cls += ' table-striped';
6133         }
6134         
6135         if (this.hover) {
6136             cfg.cls += ' table-hover';
6137         }
6138         if (this.bordered) {
6139             cfg.cls += ' table-bordered';
6140         }
6141         if (this.condensed) {
6142             cfg.cls += ' table-condensed';
6143         }
6144         if (this.responsive) {
6145             cfg.cls += ' table-responsive';
6146         }
6147         
6148         if (this.cls) {
6149             cfg.cls+=  ' ' +this.cls;
6150         }
6151         
6152         // this lot should be simplifed...
6153         var _t = this;
6154         var cp = [
6155             'align',
6156             'bgcolor',
6157             'border',
6158             'cellpadding',
6159             'cellspacing',
6160             'frame',
6161             'rules',
6162             'sortable',
6163             'summary',
6164             'width'
6165         ].forEach(function(k) {
6166             if (_t[k]) {
6167                 cfg[k] = _t[k];
6168             }
6169         });
6170         
6171         
6172         if (this.layout) {
6173             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6174         }
6175         
6176         if(this.store || this.cm){
6177             if(this.headerShow){
6178                 cfg.cn.push(this.renderHeader());
6179             }
6180             
6181             cfg.cn.push(this.renderBody());
6182             
6183             if(this.footerShow){
6184                 cfg.cn.push(this.renderFooter());
6185             }
6186             // where does this come from?
6187             //cfg.cls+=  ' TableGrid';
6188         }
6189         
6190         return { cn : [ cfg ] };
6191     },
6192     
6193     initEvents : function()
6194     {   
6195         if(!this.store || !this.cm){
6196             return;
6197         }
6198         if (this.selModel) {
6199             this.selModel.initEvents();
6200         }
6201         
6202         
6203         //Roo.log('initEvents with ds!!!!');
6204         
6205         this.mainBody = this.el.select('tbody', true).first();
6206         this.mainHead = this.el.select('thead', true).first();
6207         this.mainFoot = this.el.select('tfoot', true).first();
6208         
6209         
6210         
6211         var _this = this;
6212         
6213         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6214             e.on('click', _this.sort, _this);
6215         });
6216         
6217         this.mainBody.on("click", this.onClick, this);
6218         this.mainBody.on("dblclick", this.onDblClick, this);
6219         
6220         // why is this done????? = it breaks dialogs??
6221         //this.parent().el.setStyle('position', 'relative');
6222         
6223         
6224         if (this.footer) {
6225             this.footer.parentId = this.id;
6226             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6227             
6228             if(this.lazyLoad){
6229                 this.el.select('tfoot tr td').first().addClass('hide');
6230             }
6231         } 
6232         
6233         if(this.loadMask) {
6234             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6235         }
6236         
6237         this.store.on('load', this.onLoad, this);
6238         this.store.on('beforeload', this.onBeforeLoad, this);
6239         this.store.on('update', this.onUpdate, this);
6240         this.store.on('add', this.onAdd, this);
6241         this.store.on("clear", this.clear, this);
6242         
6243         this.el.on("contextmenu", this.onContextMenu, this);
6244         
6245         this.mainBody.on('scroll', this.onBodyScroll, this);
6246         
6247         this.cm.on("headerchange", this.onHeaderChange, this);
6248         
6249         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6250         
6251     },
6252     
6253     onContextMenu : function(e, t)
6254     {
6255         this.processEvent("contextmenu", e);
6256     },
6257     
6258     processEvent : function(name, e)
6259     {
6260         if (name != 'touchstart' ) {
6261             this.fireEvent(name, e);    
6262         }
6263         
6264         var t = e.getTarget();
6265         
6266         var cell = Roo.get(t);
6267         
6268         if(!cell){
6269             return;
6270         }
6271         
6272         if(cell.findParent('tfoot', false, true)){
6273             return;
6274         }
6275         
6276         if(cell.findParent('thead', false, true)){
6277             
6278             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6279                 cell = Roo.get(t).findParent('th', false, true);
6280                 if (!cell) {
6281                     Roo.log("failed to find th in thead?");
6282                     Roo.log(e.getTarget());
6283                     return;
6284                 }
6285             }
6286             
6287             var cellIndex = cell.dom.cellIndex;
6288             
6289             var ename = name == 'touchstart' ? 'click' : name;
6290             this.fireEvent("header" + ename, this, cellIndex, e);
6291             
6292             return;
6293         }
6294         
6295         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6296             cell = Roo.get(t).findParent('td', false, true);
6297             if (!cell) {
6298                 Roo.log("failed to find th in tbody?");
6299                 Roo.log(e.getTarget());
6300                 return;
6301             }
6302         }
6303         
6304         var row = cell.findParent('tr', false, true);
6305         var cellIndex = cell.dom.cellIndex;
6306         var rowIndex = row.dom.rowIndex - 1;
6307         
6308         if(row !== false){
6309             
6310             this.fireEvent("row" + name, this, rowIndex, e);
6311             
6312             if(cell !== false){
6313             
6314                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6315             }
6316         }
6317         
6318     },
6319     
6320     onMouseover : function(e, el)
6321     {
6322         var cell = Roo.get(el);
6323         
6324         if(!cell){
6325             return;
6326         }
6327         
6328         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6329             cell = cell.findParent('td', false, true);
6330         }
6331         
6332         var row = cell.findParent('tr', false, true);
6333         var cellIndex = cell.dom.cellIndex;
6334         var rowIndex = row.dom.rowIndex - 1; // start from 0
6335         
6336         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6337         
6338     },
6339     
6340     onMouseout : function(e, el)
6341     {
6342         var cell = Roo.get(el);
6343         
6344         if(!cell){
6345             return;
6346         }
6347         
6348         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6349             cell = cell.findParent('td', false, true);
6350         }
6351         
6352         var row = cell.findParent('tr', false, true);
6353         var cellIndex = cell.dom.cellIndex;
6354         var rowIndex = row.dom.rowIndex - 1; // start from 0
6355         
6356         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6357         
6358     },
6359     
6360     onClick : function(e, el)
6361     {
6362         var cell = Roo.get(el);
6363         
6364         if(!cell || (!this.cellSelection && !this.rowSelection)){
6365             return;
6366         }
6367         
6368         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6369             cell = cell.findParent('td', false, true);
6370         }
6371         
6372         if(!cell || typeof(cell) == 'undefined'){
6373             return;
6374         }
6375         
6376         var row = cell.findParent('tr', false, true);
6377         
6378         if(!row || typeof(row) == 'undefined'){
6379             return;
6380         }
6381         
6382         var cellIndex = cell.dom.cellIndex;
6383         var rowIndex = this.getRowIndex(row);
6384         
6385         // why??? - should these not be based on SelectionModel?
6386         if(this.cellSelection){
6387             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6388         }
6389         
6390         if(this.rowSelection){
6391             this.fireEvent('rowclick', this, row, rowIndex, e);
6392         }
6393         
6394         
6395     },
6396         
6397     onDblClick : function(e,el)
6398     {
6399         var cell = Roo.get(el);
6400         
6401         if(!cell || (!this.cellSelection && !this.rowSelection)){
6402             return;
6403         }
6404         
6405         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6406             cell = cell.findParent('td', false, true);
6407         }
6408         
6409         if(!cell || typeof(cell) == 'undefined'){
6410             return;
6411         }
6412         
6413         var row = cell.findParent('tr', false, true);
6414         
6415         if(!row || typeof(row) == 'undefined'){
6416             return;
6417         }
6418         
6419         var cellIndex = cell.dom.cellIndex;
6420         var rowIndex = this.getRowIndex(row);
6421         
6422         if(this.cellSelection){
6423             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6424         }
6425         
6426         if(this.rowSelection){
6427             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6428         }
6429     },
6430     
6431     sort : function(e,el)
6432     {
6433         var col = Roo.get(el);
6434         
6435         if(!col.hasClass('sortable')){
6436             return;
6437         }
6438         
6439         var sort = col.attr('sort');
6440         var dir = 'ASC';
6441         
6442         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6443             dir = 'DESC';
6444         }
6445         
6446         this.store.sortInfo = {field : sort, direction : dir};
6447         
6448         if (this.footer) {
6449             Roo.log("calling footer first");
6450             this.footer.onClick('first');
6451         } else {
6452         
6453             this.store.load({ params : { start : 0 } });
6454         }
6455     },
6456     
6457     renderHeader : function()
6458     {
6459         var header = {
6460             tag: 'thead',
6461             cn : []
6462         };
6463         
6464         var cm = this.cm;
6465         this.totalWidth = 0;
6466         
6467         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6468             
6469             var config = cm.config[i];
6470             
6471             var c = {
6472                 tag: 'th',
6473                 cls : 'x-hcol-' + i,
6474                 style : '',
6475                 html: cm.getColumnHeader(i)
6476             };
6477             
6478             var hh = '';
6479             
6480             if(typeof(config.sortable) != 'undefined' && config.sortable){
6481                 c.cls = 'sortable';
6482                 c.html = '<i class="glyphicon"></i>' + c.html;
6483             }
6484             
6485             if(typeof(config.lgHeader) != 'undefined'){
6486                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6487             }
6488             
6489             if(typeof(config.mdHeader) != 'undefined'){
6490                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6491             }
6492             
6493             if(typeof(config.smHeader) != 'undefined'){
6494                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6495             }
6496             
6497             if(typeof(config.xsHeader) != 'undefined'){
6498                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6499             }
6500             
6501             if(hh.length){
6502                 c.html = hh;
6503             }
6504             
6505             if(typeof(config.tooltip) != 'undefined'){
6506                 c.tooltip = config.tooltip;
6507             }
6508             
6509             if(typeof(config.colspan) != 'undefined'){
6510                 c.colspan = config.colspan;
6511             }
6512             
6513             if(typeof(config.hidden) != 'undefined' && config.hidden){
6514                 c.style += ' display:none;';
6515             }
6516             
6517             if(typeof(config.dataIndex) != 'undefined'){
6518                 c.sort = config.dataIndex;
6519             }
6520             
6521            
6522             
6523             if(typeof(config.align) != 'undefined' && config.align.length){
6524                 c.style += ' text-align:' + config.align + ';';
6525             }
6526             
6527             if(typeof(config.width) != 'undefined'){
6528                 c.style += ' width:' + config.width + 'px;';
6529                 this.totalWidth += config.width;
6530             } else {
6531                 this.totalWidth += 100; // assume minimum of 100 per column?
6532             }
6533             
6534             if(typeof(config.cls) != 'undefined'){
6535                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6536             }
6537             
6538             ['xs','sm','md','lg'].map(function(size){
6539                 
6540                 if(typeof(config[size]) == 'undefined'){
6541                     return;
6542                 }
6543                 
6544                 if (!config[size]) { // 0 = hidden
6545                     c.cls += ' hidden-' + size;
6546                     return;
6547                 }
6548                 
6549                 c.cls += ' col-' + size + '-' + config[size];
6550
6551             });
6552             
6553             header.cn.push(c)
6554         }
6555         
6556         return header;
6557     },
6558     
6559     renderBody : function()
6560     {
6561         var body = {
6562             tag: 'tbody',
6563             cn : [
6564                 {
6565                     tag: 'tr',
6566                     cn : [
6567                         {
6568                             tag : 'td',
6569                             colspan :  this.cm.getColumnCount()
6570                         }
6571                     ]
6572                 }
6573             ]
6574         };
6575         
6576         return body;
6577     },
6578     
6579     renderFooter : function()
6580     {
6581         var footer = {
6582             tag: 'tfoot',
6583             cn : [
6584                 {
6585                     tag: 'tr',
6586                     cn : [
6587                         {
6588                             tag : 'td',
6589                             colspan :  this.cm.getColumnCount()
6590                         }
6591                     ]
6592                 }
6593             ]
6594         };
6595         
6596         return footer;
6597     },
6598     
6599     
6600     
6601     onLoad : function()
6602     {
6603 //        Roo.log('ds onload');
6604         this.clear();
6605         
6606         var _this = this;
6607         var cm = this.cm;
6608         var ds = this.store;
6609         
6610         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6611             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6612             if (_this.store.sortInfo) {
6613                     
6614                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6615                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6616                 }
6617                 
6618                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6619                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6620                 }
6621             }
6622         });
6623         
6624         var tbody =  this.mainBody;
6625               
6626         if(ds.getCount() > 0){
6627             ds.data.each(function(d,rowIndex){
6628                 var row =  this.renderRow(cm, ds, rowIndex);
6629                 
6630                 tbody.createChild(row);
6631                 
6632                 var _this = this;
6633                 
6634                 if(row.cellObjects.length){
6635                     Roo.each(row.cellObjects, function(r){
6636                         _this.renderCellObject(r);
6637                     })
6638                 }
6639                 
6640             }, this);
6641         }
6642         
6643         var tfoot = this.el.select('tfoot', true).first();
6644         
6645         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6646             
6647             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6648             
6649             var total = this.ds.getTotalCount();
6650             
6651             if(this.footer.pageSize < total){
6652                 this.mainFoot.show();
6653             }
6654         }
6655         
6656         Roo.each(this.el.select('tbody td', true).elements, function(e){
6657             e.on('mouseover', _this.onMouseover, _this);
6658         });
6659         
6660         Roo.each(this.el.select('tbody td', true).elements, function(e){
6661             e.on('mouseout', _this.onMouseout, _this);
6662         });
6663         this.fireEvent('rowsrendered', this);
6664         
6665         this.autoSize();
6666     },
6667     
6668     
6669     onUpdate : function(ds,record)
6670     {
6671         this.refreshRow(record);
6672         this.autoSize();
6673     },
6674     
6675     onRemove : function(ds, record, index, isUpdate){
6676         if(isUpdate !== true){
6677             this.fireEvent("beforerowremoved", this, index, record);
6678         }
6679         var bt = this.mainBody.dom;
6680         
6681         var rows = this.el.select('tbody > tr', true).elements;
6682         
6683         if(typeof(rows[index]) != 'undefined'){
6684             bt.removeChild(rows[index].dom);
6685         }
6686         
6687 //        if(bt.rows[index]){
6688 //            bt.removeChild(bt.rows[index]);
6689 //        }
6690         
6691         if(isUpdate !== true){
6692             //this.stripeRows(index);
6693             //this.syncRowHeights(index, index);
6694             //this.layout();
6695             this.fireEvent("rowremoved", this, index, record);
6696         }
6697     },
6698     
6699     onAdd : function(ds, records, rowIndex)
6700     {
6701         //Roo.log('on Add called');
6702         // - note this does not handle multiple adding very well..
6703         var bt = this.mainBody.dom;
6704         for (var i =0 ; i < records.length;i++) {
6705             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6706             //Roo.log(records[i]);
6707             //Roo.log(this.store.getAt(rowIndex+i));
6708             this.insertRow(this.store, rowIndex + i, false);
6709             return;
6710         }
6711         
6712     },
6713     
6714     
6715     refreshRow : function(record){
6716         var ds = this.store, index;
6717         if(typeof record == 'number'){
6718             index = record;
6719             record = ds.getAt(index);
6720         }else{
6721             index = ds.indexOf(record);
6722         }
6723         this.insertRow(ds, index, true);
6724         this.autoSize();
6725         this.onRemove(ds, record, index+1, true);
6726         this.autoSize();
6727         //this.syncRowHeights(index, index);
6728         //this.layout();
6729         this.fireEvent("rowupdated", this, index, record);
6730     },
6731     
6732     insertRow : function(dm, rowIndex, isUpdate){
6733         
6734         if(!isUpdate){
6735             this.fireEvent("beforerowsinserted", this, rowIndex);
6736         }
6737             //var s = this.getScrollState();
6738         var row = this.renderRow(this.cm, this.store, rowIndex);
6739         // insert before rowIndex..
6740         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6741         
6742         var _this = this;
6743                 
6744         if(row.cellObjects.length){
6745             Roo.each(row.cellObjects, function(r){
6746                 _this.renderCellObject(r);
6747             })
6748         }
6749             
6750         if(!isUpdate){
6751             this.fireEvent("rowsinserted", this, rowIndex);
6752             //this.syncRowHeights(firstRow, lastRow);
6753             //this.stripeRows(firstRow);
6754             //this.layout();
6755         }
6756         
6757     },
6758     
6759     
6760     getRowDom : function(rowIndex)
6761     {
6762         var rows = this.el.select('tbody > tr', true).elements;
6763         
6764         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6765         
6766     },
6767     // returns the object tree for a tr..
6768   
6769     
6770     renderRow : function(cm, ds, rowIndex) 
6771     {
6772         var d = ds.getAt(rowIndex);
6773         
6774         var row = {
6775             tag : 'tr',
6776             cls : 'x-row-' + rowIndex,
6777             cn : []
6778         };
6779             
6780         var cellObjects = [];
6781         
6782         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6783             var config = cm.config[i];
6784             
6785             var renderer = cm.getRenderer(i);
6786             var value = '';
6787             var id = false;
6788             
6789             if(typeof(renderer) !== 'undefined'){
6790                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6791             }
6792             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6793             // and are rendered into the cells after the row is rendered - using the id for the element.
6794             
6795             if(typeof(value) === 'object'){
6796                 id = Roo.id();
6797                 cellObjects.push({
6798                     container : id,
6799                     cfg : value 
6800                 })
6801             }
6802             
6803             var rowcfg = {
6804                 record: d,
6805                 rowIndex : rowIndex,
6806                 colIndex : i,
6807                 rowClass : ''
6808             };
6809
6810             this.fireEvent('rowclass', this, rowcfg);
6811             
6812             var td = {
6813                 tag: 'td',
6814                 cls : rowcfg.rowClass + ' x-col-' + i,
6815                 style: '',
6816                 html: (typeof(value) === 'object') ? '' : value
6817             };
6818             
6819             if (id) {
6820                 td.id = id;
6821             }
6822             
6823             if(typeof(config.colspan) != 'undefined'){
6824                 td.colspan = config.colspan;
6825             }
6826             
6827             if(typeof(config.hidden) != 'undefined' && config.hidden){
6828                 td.style += ' display:none;';
6829             }
6830             
6831             if(typeof(config.align) != 'undefined' && config.align.length){
6832                 td.style += ' text-align:' + config.align + ';';
6833             }
6834             if(typeof(config.valign) != 'undefined' && config.valign.length){
6835                 td.style += ' vertical-align:' + config.valign + ';';
6836             }
6837             
6838             if(typeof(config.width) != 'undefined'){
6839                 td.style += ' width:' +  config.width + 'px;';
6840             }
6841             
6842             if(typeof(config.cursor) != 'undefined'){
6843                 td.style += ' cursor:' +  config.cursor + ';';
6844             }
6845             
6846             if(typeof(config.cls) != 'undefined'){
6847                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6848             }
6849             
6850             ['xs','sm','md','lg'].map(function(size){
6851                 
6852                 if(typeof(config[size]) == 'undefined'){
6853                     return;
6854                 }
6855                 
6856                 if (!config[size]) { // 0 = hidden
6857                     td.cls += ' hidden-' + size;
6858                     return;
6859                 }
6860                 
6861                 td.cls += ' col-' + size + '-' + config[size];
6862
6863             });
6864             
6865             row.cn.push(td);
6866            
6867         }
6868         
6869         row.cellObjects = cellObjects;
6870         
6871         return row;
6872           
6873     },
6874     
6875     
6876     
6877     onBeforeLoad : function()
6878     {
6879         
6880     },
6881      /**
6882      * Remove all rows
6883      */
6884     clear : function()
6885     {
6886         this.el.select('tbody', true).first().dom.innerHTML = '';
6887     },
6888     /**
6889      * Show or hide a row.
6890      * @param {Number} rowIndex to show or hide
6891      * @param {Boolean} state hide
6892      */
6893     setRowVisibility : function(rowIndex, state)
6894     {
6895         var bt = this.mainBody.dom;
6896         
6897         var rows = this.el.select('tbody > tr', true).elements;
6898         
6899         if(typeof(rows[rowIndex]) == 'undefined'){
6900             return;
6901         }
6902         rows[rowIndex].dom.style.display = state ? '' : 'none';
6903     },
6904     
6905     
6906     getSelectionModel : function(){
6907         if(!this.selModel){
6908             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6909         }
6910         return this.selModel;
6911     },
6912     /*
6913      * Render the Roo.bootstrap object from renderder
6914      */
6915     renderCellObject : function(r)
6916     {
6917         var _this = this;
6918         
6919         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6920         
6921         var t = r.cfg.render(r.container);
6922         
6923         if(r.cfg.cn){
6924             Roo.each(r.cfg.cn, function(c){
6925                 var child = {
6926                     container: t.getChildContainer(),
6927                     cfg: c
6928                 };
6929                 _this.renderCellObject(child);
6930             })
6931         }
6932     },
6933     
6934     getRowIndex : function(row)
6935     {
6936         var rowIndex = -1;
6937         
6938         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6939             if(el != row){
6940                 return;
6941             }
6942             
6943             rowIndex = index;
6944         });
6945         
6946         return rowIndex;
6947     },
6948      /**
6949      * Returns the grid's underlying element = used by panel.Grid
6950      * @return {Element} The element
6951      */
6952     getGridEl : function(){
6953         return this.el;
6954     },
6955      /**
6956      * Forces a resize - used by panel.Grid
6957      * @return {Element} The element
6958      */
6959     autoSize : function()
6960     {
6961         //var ctr = Roo.get(this.container.dom.parentElement);
6962         var ctr = Roo.get(this.el.dom);
6963         
6964         var thd = this.getGridEl().select('thead',true).first();
6965         var tbd = this.getGridEl().select('tbody', true).first();
6966         var tfd = this.getGridEl().select('tfoot', true).first();
6967         
6968         var cw = ctr.getWidth();
6969         
6970         if (tbd) {
6971             
6972             tbd.setSize(ctr.getWidth(),
6973                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6974             );
6975             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6976             cw -= barsize;
6977         }
6978         cw = Math.max(cw, this.totalWidth);
6979         this.getGridEl().select('tr',true).setWidth(cw);
6980         // resize 'expandable coloumn?
6981         
6982         return; // we doe not have a view in this design..
6983         
6984     },
6985     onBodyScroll: function()
6986     {
6987         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6988         if(this.mainHead){
6989             this.mainHead.setStyle({
6990                 'position' : 'relative',
6991                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6992             });
6993         }
6994         
6995         if(this.lazyLoad){
6996             
6997             var scrollHeight = this.mainBody.dom.scrollHeight;
6998             
6999             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7000             
7001             var height = this.mainBody.getHeight();
7002             
7003             if(scrollHeight - height == scrollTop) {
7004                 
7005                 var total = this.ds.getTotalCount();
7006                 
7007                 if(this.footer.cursor + this.footer.pageSize < total){
7008                     
7009                     this.footer.ds.load({
7010                         params : {
7011                             start : this.footer.cursor + this.footer.pageSize,
7012                             limit : this.footer.pageSize
7013                         },
7014                         add : true
7015                     });
7016                 }
7017             }
7018             
7019         }
7020     },
7021     
7022     onHeaderChange : function()
7023     {
7024         var header = this.renderHeader();
7025         var table = this.el.select('table', true).first();
7026         
7027         this.mainHead.remove();
7028         this.mainHead = table.createChild(header, this.mainBody, false);
7029     },
7030     
7031     onHiddenChange : function(colModel, colIndex, hidden)
7032     {
7033         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7034         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7035         
7036         this.CSS.updateRule(thSelector, "display", "");
7037         this.CSS.updateRule(tdSelector, "display", "");
7038         
7039         if(hidden){
7040             this.CSS.updateRule(thSelector, "display", "none");
7041             this.CSS.updateRule(tdSelector, "display", "none");
7042         }
7043         
7044         this.onHeaderChange();
7045         this.onLoad();
7046         
7047     }
7048     
7049 });
7050
7051  
7052
7053  /*
7054  * - LGPL
7055  *
7056  * table cell
7057  * 
7058  */
7059
7060 /**
7061  * @class Roo.bootstrap.TableCell
7062  * @extends Roo.bootstrap.Component
7063  * Bootstrap TableCell class
7064  * @cfg {String} html cell contain text
7065  * @cfg {String} cls cell class
7066  * @cfg {String} tag cell tag (td|th) default td
7067  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7068  * @cfg {String} align Aligns the content in a cell
7069  * @cfg {String} axis Categorizes cells
7070  * @cfg {String} bgcolor Specifies the background color of a cell
7071  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7072  * @cfg {Number} colspan Specifies the number of columns a cell should span
7073  * @cfg {String} headers Specifies one or more header cells a cell is related to
7074  * @cfg {Number} height Sets the height of a cell
7075  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7076  * @cfg {Number} rowspan Sets the number of rows a cell should span
7077  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7078  * @cfg {String} valign Vertical aligns the content in a cell
7079  * @cfg {Number} width Specifies the width of a cell
7080  * 
7081  * @constructor
7082  * Create a new TableCell
7083  * @param {Object} config The config object
7084  */
7085
7086 Roo.bootstrap.TableCell = function(config){
7087     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7088 };
7089
7090 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7091     
7092     html: false,
7093     cls: false,
7094     tag: false,
7095     abbr: false,
7096     align: false,
7097     axis: false,
7098     bgcolor: false,
7099     charoff: false,
7100     colspan: false,
7101     headers: false,
7102     height: false,
7103     nowrap: false,
7104     rowspan: false,
7105     scope: false,
7106     valign: false,
7107     width: false,
7108     
7109     
7110     getAutoCreate : function(){
7111         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7112         
7113         cfg = {
7114             tag: 'td'
7115         };
7116         
7117         if(this.tag){
7118             cfg.tag = this.tag;
7119         }
7120         
7121         if (this.html) {
7122             cfg.html=this.html
7123         }
7124         if (this.cls) {
7125             cfg.cls=this.cls
7126         }
7127         if (this.abbr) {
7128             cfg.abbr=this.abbr
7129         }
7130         if (this.align) {
7131             cfg.align=this.align
7132         }
7133         if (this.axis) {
7134             cfg.axis=this.axis
7135         }
7136         if (this.bgcolor) {
7137             cfg.bgcolor=this.bgcolor
7138         }
7139         if (this.charoff) {
7140             cfg.charoff=this.charoff
7141         }
7142         if (this.colspan) {
7143             cfg.colspan=this.colspan
7144         }
7145         if (this.headers) {
7146             cfg.headers=this.headers
7147         }
7148         if (this.height) {
7149             cfg.height=this.height
7150         }
7151         if (this.nowrap) {
7152             cfg.nowrap=this.nowrap
7153         }
7154         if (this.rowspan) {
7155             cfg.rowspan=this.rowspan
7156         }
7157         if (this.scope) {
7158             cfg.scope=this.scope
7159         }
7160         if (this.valign) {
7161             cfg.valign=this.valign
7162         }
7163         if (this.width) {
7164             cfg.width=this.width
7165         }
7166         
7167         
7168         return cfg;
7169     }
7170    
7171 });
7172
7173  
7174
7175  /*
7176  * - LGPL
7177  *
7178  * table row
7179  * 
7180  */
7181
7182 /**
7183  * @class Roo.bootstrap.TableRow
7184  * @extends Roo.bootstrap.Component
7185  * Bootstrap TableRow class
7186  * @cfg {String} cls row class
7187  * @cfg {String} align Aligns the content in a table row
7188  * @cfg {String} bgcolor Specifies a background color for a table row
7189  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7190  * @cfg {String} valign Vertical aligns the content in a table row
7191  * 
7192  * @constructor
7193  * Create a new TableRow
7194  * @param {Object} config The config object
7195  */
7196
7197 Roo.bootstrap.TableRow = function(config){
7198     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7199 };
7200
7201 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7202     
7203     cls: false,
7204     align: false,
7205     bgcolor: false,
7206     charoff: false,
7207     valign: false,
7208     
7209     getAutoCreate : function(){
7210         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7211         
7212         cfg = {
7213             tag: 'tr'
7214         };
7215             
7216         if(this.cls){
7217             cfg.cls = this.cls;
7218         }
7219         if(this.align){
7220             cfg.align = this.align;
7221         }
7222         if(this.bgcolor){
7223             cfg.bgcolor = this.bgcolor;
7224         }
7225         if(this.charoff){
7226             cfg.charoff = this.charoff;
7227         }
7228         if(this.valign){
7229             cfg.valign = this.valign;
7230         }
7231         
7232         return cfg;
7233     }
7234    
7235 });
7236
7237  
7238
7239  /*
7240  * - LGPL
7241  *
7242  * table body
7243  * 
7244  */
7245
7246 /**
7247  * @class Roo.bootstrap.TableBody
7248  * @extends Roo.bootstrap.Component
7249  * Bootstrap TableBody class
7250  * @cfg {String} cls element class
7251  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7252  * @cfg {String} align Aligns the content inside the element
7253  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7254  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7255  * 
7256  * @constructor
7257  * Create a new TableBody
7258  * @param {Object} config The config object
7259  */
7260
7261 Roo.bootstrap.TableBody = function(config){
7262     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7263 };
7264
7265 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7266     
7267     cls: false,
7268     tag: false,
7269     align: false,
7270     charoff: false,
7271     valign: false,
7272     
7273     getAutoCreate : function(){
7274         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7275         
7276         cfg = {
7277             tag: 'tbody'
7278         };
7279             
7280         if (this.cls) {
7281             cfg.cls=this.cls
7282         }
7283         if(this.tag){
7284             cfg.tag = this.tag;
7285         }
7286         
7287         if(this.align){
7288             cfg.align = this.align;
7289         }
7290         if(this.charoff){
7291             cfg.charoff = this.charoff;
7292         }
7293         if(this.valign){
7294             cfg.valign = this.valign;
7295         }
7296         
7297         return cfg;
7298     }
7299     
7300     
7301 //    initEvents : function()
7302 //    {
7303 //        
7304 //        if(!this.store){
7305 //            return;
7306 //        }
7307 //        
7308 //        this.store = Roo.factory(this.store, Roo.data);
7309 //        this.store.on('load', this.onLoad, this);
7310 //        
7311 //        this.store.load();
7312 //        
7313 //    },
7314 //    
7315 //    onLoad: function () 
7316 //    {   
7317 //        this.fireEvent('load', this);
7318 //    }
7319 //    
7320 //   
7321 });
7322
7323  
7324
7325  /*
7326  * Based on:
7327  * Ext JS Library 1.1.1
7328  * Copyright(c) 2006-2007, Ext JS, LLC.
7329  *
7330  * Originally Released Under LGPL - original licence link has changed is not relivant.
7331  *
7332  * Fork - LGPL
7333  * <script type="text/javascript">
7334  */
7335
7336 // as we use this in bootstrap.
7337 Roo.namespace('Roo.form');
7338  /**
7339  * @class Roo.form.Action
7340  * Internal Class used to handle form actions
7341  * @constructor
7342  * @param {Roo.form.BasicForm} el The form element or its id
7343  * @param {Object} config Configuration options
7344  */
7345
7346  
7347  
7348 // define the action interface
7349 Roo.form.Action = function(form, options){
7350     this.form = form;
7351     this.options = options || {};
7352 };
7353 /**
7354  * Client Validation Failed
7355  * @const 
7356  */
7357 Roo.form.Action.CLIENT_INVALID = 'client';
7358 /**
7359  * Server Validation Failed
7360  * @const 
7361  */
7362 Roo.form.Action.SERVER_INVALID = 'server';
7363  /**
7364  * Connect to Server Failed
7365  * @const 
7366  */
7367 Roo.form.Action.CONNECT_FAILURE = 'connect';
7368 /**
7369  * Reading Data from Server Failed
7370  * @const 
7371  */
7372 Roo.form.Action.LOAD_FAILURE = 'load';
7373
7374 Roo.form.Action.prototype = {
7375     type : 'default',
7376     failureType : undefined,
7377     response : undefined,
7378     result : undefined,
7379
7380     // interface method
7381     run : function(options){
7382
7383     },
7384
7385     // interface method
7386     success : function(response){
7387
7388     },
7389
7390     // interface method
7391     handleResponse : function(response){
7392
7393     },
7394
7395     // default connection failure
7396     failure : function(response){
7397         
7398         this.response = response;
7399         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7400         this.form.afterAction(this, false);
7401     },
7402
7403     processResponse : function(response){
7404         this.response = response;
7405         if(!response.responseText){
7406             return true;
7407         }
7408         this.result = this.handleResponse(response);
7409         return this.result;
7410     },
7411
7412     // utility functions used internally
7413     getUrl : function(appendParams){
7414         var url = this.options.url || this.form.url || this.form.el.dom.action;
7415         if(appendParams){
7416             var p = this.getParams();
7417             if(p){
7418                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7419             }
7420         }
7421         return url;
7422     },
7423
7424     getMethod : function(){
7425         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7426     },
7427
7428     getParams : function(){
7429         var bp = this.form.baseParams;
7430         var p = this.options.params;
7431         if(p){
7432             if(typeof p == "object"){
7433                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7434             }else if(typeof p == 'string' && bp){
7435                 p += '&' + Roo.urlEncode(bp);
7436             }
7437         }else if(bp){
7438             p = Roo.urlEncode(bp);
7439         }
7440         return p;
7441     },
7442
7443     createCallback : function(){
7444         return {
7445             success: this.success,
7446             failure: this.failure,
7447             scope: this,
7448             timeout: (this.form.timeout*1000),
7449             upload: this.form.fileUpload ? this.success : undefined
7450         };
7451     }
7452 };
7453
7454 Roo.form.Action.Submit = function(form, options){
7455     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7456 };
7457
7458 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7459     type : 'submit',
7460
7461     haveProgress : false,
7462     uploadComplete : false,
7463     
7464     // uploadProgress indicator.
7465     uploadProgress : function()
7466     {
7467         if (!this.form.progressUrl) {
7468             return;
7469         }
7470         
7471         if (!this.haveProgress) {
7472             Roo.MessageBox.progress("Uploading", "Uploading");
7473         }
7474         if (this.uploadComplete) {
7475            Roo.MessageBox.hide();
7476            return;
7477         }
7478         
7479         this.haveProgress = true;
7480    
7481         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7482         
7483         var c = new Roo.data.Connection();
7484         c.request({
7485             url : this.form.progressUrl,
7486             params: {
7487                 id : uid
7488             },
7489             method: 'GET',
7490             success : function(req){
7491                //console.log(data);
7492                 var rdata = false;
7493                 var edata;
7494                 try  {
7495                    rdata = Roo.decode(req.responseText)
7496                 } catch (e) {
7497                     Roo.log("Invalid data from server..");
7498                     Roo.log(edata);
7499                     return;
7500                 }
7501                 if (!rdata || !rdata.success) {
7502                     Roo.log(rdata);
7503                     Roo.MessageBox.alert(Roo.encode(rdata));
7504                     return;
7505                 }
7506                 var data = rdata.data;
7507                 
7508                 if (this.uploadComplete) {
7509                    Roo.MessageBox.hide();
7510                    return;
7511                 }
7512                    
7513                 if (data){
7514                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7515                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7516                     );
7517                 }
7518                 this.uploadProgress.defer(2000,this);
7519             },
7520        
7521             failure: function(data) {
7522                 Roo.log('progress url failed ');
7523                 Roo.log(data);
7524             },
7525             scope : this
7526         });
7527            
7528     },
7529     
7530     
7531     run : function()
7532     {
7533         // run get Values on the form, so it syncs any secondary forms.
7534         this.form.getValues();
7535         
7536         var o = this.options;
7537         var method = this.getMethod();
7538         var isPost = method == 'POST';
7539         if(o.clientValidation === false || this.form.isValid()){
7540             
7541             if (this.form.progressUrl) {
7542                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7543                     (new Date() * 1) + '' + Math.random());
7544                     
7545             } 
7546             
7547             
7548             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7549                 form:this.form.el.dom,
7550                 url:this.getUrl(!isPost),
7551                 method: method,
7552                 params:isPost ? this.getParams() : null,
7553                 isUpload: this.form.fileUpload
7554             }));
7555             
7556             this.uploadProgress();
7557
7558         }else if (o.clientValidation !== false){ // client validation failed
7559             this.failureType = Roo.form.Action.CLIENT_INVALID;
7560             this.form.afterAction(this, false);
7561         }
7562     },
7563
7564     success : function(response)
7565     {
7566         this.uploadComplete= true;
7567         if (this.haveProgress) {
7568             Roo.MessageBox.hide();
7569         }
7570         
7571         
7572         var result = this.processResponse(response);
7573         if(result === true || result.success){
7574             this.form.afterAction(this, true);
7575             return;
7576         }
7577         if(result.errors){
7578             this.form.markInvalid(result.errors);
7579             this.failureType = Roo.form.Action.SERVER_INVALID;
7580         }
7581         this.form.afterAction(this, false);
7582     },
7583     failure : function(response)
7584     {
7585         this.uploadComplete= true;
7586         if (this.haveProgress) {
7587             Roo.MessageBox.hide();
7588         }
7589         
7590         this.response = response;
7591         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7592         this.form.afterAction(this, false);
7593     },
7594     
7595     handleResponse : function(response){
7596         if(this.form.errorReader){
7597             var rs = this.form.errorReader.read(response);
7598             var errors = [];
7599             if(rs.records){
7600                 for(var i = 0, len = rs.records.length; i < len; i++) {
7601                     var r = rs.records[i];
7602                     errors[i] = r.data;
7603                 }
7604             }
7605             if(errors.length < 1){
7606                 errors = null;
7607             }
7608             return {
7609                 success : rs.success,
7610                 errors : errors
7611             };
7612         }
7613         var ret = false;
7614         try {
7615             ret = Roo.decode(response.responseText);
7616         } catch (e) {
7617             ret = {
7618                 success: false,
7619                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7620                 errors : []
7621             };
7622         }
7623         return ret;
7624         
7625     }
7626 });
7627
7628
7629 Roo.form.Action.Load = function(form, options){
7630     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7631     this.reader = this.form.reader;
7632 };
7633
7634 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7635     type : 'load',
7636
7637     run : function(){
7638         
7639         Roo.Ajax.request(Roo.apply(
7640                 this.createCallback(), {
7641                     method:this.getMethod(),
7642                     url:this.getUrl(false),
7643                     params:this.getParams()
7644         }));
7645     },
7646
7647     success : function(response){
7648         
7649         var result = this.processResponse(response);
7650         if(result === true || !result.success || !result.data){
7651             this.failureType = Roo.form.Action.LOAD_FAILURE;
7652             this.form.afterAction(this, false);
7653             return;
7654         }
7655         this.form.clearInvalid();
7656         this.form.setValues(result.data);
7657         this.form.afterAction(this, true);
7658     },
7659
7660     handleResponse : function(response){
7661         if(this.form.reader){
7662             var rs = this.form.reader.read(response);
7663             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7664             return {
7665                 success : rs.success,
7666                 data : data
7667             };
7668         }
7669         return Roo.decode(response.responseText);
7670     }
7671 });
7672
7673 Roo.form.Action.ACTION_TYPES = {
7674     'load' : Roo.form.Action.Load,
7675     'submit' : Roo.form.Action.Submit
7676 };/*
7677  * - LGPL
7678  *
7679  * form
7680  *
7681  */
7682
7683 /**
7684  * @class Roo.bootstrap.Form
7685  * @extends Roo.bootstrap.Component
7686  * Bootstrap Form class
7687  * @cfg {String} method  GET | POST (default POST)
7688  * @cfg {String} labelAlign top | left (default top)
7689  * @cfg {String} align left  | right - for navbars
7690  * @cfg {Boolean} loadMask load mask when submit (default true)
7691
7692  *
7693  * @constructor
7694  * Create a new Form
7695  * @param {Object} config The config object
7696  */
7697
7698
7699 Roo.bootstrap.Form = function(config){
7700     
7701     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7702     
7703     Roo.bootstrap.Form.popover.apply();
7704     
7705     this.addEvents({
7706         /**
7707          * @event clientvalidation
7708          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7709          * @param {Form} this
7710          * @param {Boolean} valid true if the form has passed client-side validation
7711          */
7712         clientvalidation: true,
7713         /**
7714          * @event beforeaction
7715          * Fires before any action is performed. Return false to cancel the action.
7716          * @param {Form} this
7717          * @param {Action} action The action to be performed
7718          */
7719         beforeaction: true,
7720         /**
7721          * @event actionfailed
7722          * Fires when an action fails.
7723          * @param {Form} this
7724          * @param {Action} action The action that failed
7725          */
7726         actionfailed : true,
7727         /**
7728          * @event actioncomplete
7729          * Fires when an action is completed.
7730          * @param {Form} this
7731          * @param {Action} action The action that completed
7732          */
7733         actioncomplete : true
7734     });
7735 };
7736
7737 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7738
7739      /**
7740      * @cfg {String} method
7741      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7742      */
7743     method : 'POST',
7744     /**
7745      * @cfg {String} url
7746      * The URL to use for form actions if one isn't supplied in the action options.
7747      */
7748     /**
7749      * @cfg {Boolean} fileUpload
7750      * Set to true if this form is a file upload.
7751      */
7752
7753     /**
7754      * @cfg {Object} baseParams
7755      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7756      */
7757
7758     /**
7759      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7760      */
7761     timeout: 30,
7762     /**
7763      * @cfg {Sting} align (left|right) for navbar forms
7764      */
7765     align : 'left',
7766
7767     // private
7768     activeAction : null,
7769
7770     /**
7771      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7772      * element by passing it or its id or mask the form itself by passing in true.
7773      * @type Mixed
7774      */
7775     waitMsgTarget : false,
7776
7777     loadMask : true,
7778     
7779     /**
7780      * @cfg {Boolean} errorMask (true|false) default false
7781      */
7782     errorMask : false,
7783     
7784     /**
7785      * @cfg {Number} maskOffset Default 100
7786      */
7787     maskOffset : 100,
7788     
7789     /**
7790      * @cfg {Boolean} maskBody
7791      */
7792     maskBody : false,
7793
7794     getAutoCreate : function(){
7795
7796         var cfg = {
7797             tag: 'form',
7798             method : this.method || 'POST',
7799             id : this.id || Roo.id(),
7800             cls : ''
7801         };
7802         if (this.parent().xtype.match(/^Nav/)) {
7803             cfg.cls = 'navbar-form navbar-' + this.align;
7804
7805         }
7806
7807         if (this.labelAlign == 'left' ) {
7808             cfg.cls += ' form-horizontal';
7809         }
7810
7811
7812         return cfg;
7813     },
7814     initEvents : function()
7815     {
7816         this.el.on('submit', this.onSubmit, this);
7817         // this was added as random key presses on the form where triggering form submit.
7818         this.el.on('keypress', function(e) {
7819             if (e.getCharCode() != 13) {
7820                 return true;
7821             }
7822             // we might need to allow it for textareas.. and some other items.
7823             // check e.getTarget().
7824
7825             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7826                 return true;
7827             }
7828
7829             Roo.log("keypress blocked");
7830
7831             e.preventDefault();
7832             return false;
7833         });
7834         
7835     },
7836     // private
7837     onSubmit : function(e){
7838         e.stopEvent();
7839     },
7840
7841      /**
7842      * Returns true if client-side validation on the form is successful.
7843      * @return Boolean
7844      */
7845     isValid : function(){
7846         var items = this.getItems();
7847         var valid = true;
7848         var target = false;
7849         
7850         items.each(function(f){
7851             
7852             if(f.validate()){
7853                 return;
7854             }
7855             
7856             Roo.log('invalid field: ' + f.name);
7857             
7858             valid = false;
7859
7860             if(!target && f.el.isVisible(true)){
7861                 target = f;
7862             }
7863            
7864         });
7865         
7866         if(this.errorMask && !valid){
7867             Roo.bootstrap.Form.popover.mask(this, target);
7868         }
7869         
7870         return valid;
7871     },
7872     
7873     /**
7874      * Returns true if any fields in this form have changed since their original load.
7875      * @return Boolean
7876      */
7877     isDirty : function(){
7878         var dirty = false;
7879         var items = this.getItems();
7880         items.each(function(f){
7881            if(f.isDirty()){
7882                dirty = true;
7883                return false;
7884            }
7885            return true;
7886         });
7887         return dirty;
7888     },
7889      /**
7890      * Performs a predefined action (submit or load) or custom actions you define on this form.
7891      * @param {String} actionName The name of the action type
7892      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7893      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7894      * accept other config options):
7895      * <pre>
7896 Property          Type             Description
7897 ----------------  ---------------  ----------------------------------------------------------------------------------
7898 url               String           The url for the action (defaults to the form's url)
7899 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7900 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7901 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7902                                    validate the form on the client (defaults to false)
7903      * </pre>
7904      * @return {BasicForm} this
7905      */
7906     doAction : function(action, options){
7907         if(typeof action == 'string'){
7908             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7909         }
7910         if(this.fireEvent('beforeaction', this, action) !== false){
7911             this.beforeAction(action);
7912             action.run.defer(100, action);
7913         }
7914         return this;
7915     },
7916
7917     // private
7918     beforeAction : function(action){
7919         var o = action.options;
7920         
7921         if(this.loadMask){
7922             
7923             if(this.maskBody){
7924                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7925             } else {
7926                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7927             }
7928         }
7929         // not really supported yet.. ??
7930
7931         //if(this.waitMsgTarget === true){
7932         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7933         //}else if(this.waitMsgTarget){
7934         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7935         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7936         //}else {
7937         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7938        // }
7939
7940     },
7941
7942     // private
7943     afterAction : function(action, success){
7944         this.activeAction = null;
7945         var o = action.options;
7946
7947         if(this.loadMask){
7948             
7949             if(this.maskBody){
7950                 Roo.get(document.body).unmask();
7951             } else {
7952                 this.el.unmask();
7953             }
7954         }
7955         
7956         //if(this.waitMsgTarget === true){
7957 //            this.el.unmask();
7958         //}else if(this.waitMsgTarget){
7959         //    this.waitMsgTarget.unmask();
7960         //}else{
7961         //    Roo.MessageBox.updateProgress(1);
7962         //    Roo.MessageBox.hide();
7963        // }
7964         //
7965         if(success){
7966             if(o.reset){
7967                 this.reset();
7968             }
7969             Roo.callback(o.success, o.scope, [this, action]);
7970             this.fireEvent('actioncomplete', this, action);
7971
7972         }else{
7973
7974             // failure condition..
7975             // we have a scenario where updates need confirming.
7976             // eg. if a locking scenario exists..
7977             // we look for { errors : { needs_confirm : true }} in the response.
7978             if (
7979                 (typeof(action.result) != 'undefined')  &&
7980                 (typeof(action.result.errors) != 'undefined')  &&
7981                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7982            ){
7983                 var _t = this;
7984                 Roo.log("not supported yet");
7985                  /*
7986
7987                 Roo.MessageBox.confirm(
7988                     "Change requires confirmation",
7989                     action.result.errorMsg,
7990                     function(r) {
7991                         if (r != 'yes') {
7992                             return;
7993                         }
7994                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7995                     }
7996
7997                 );
7998                 */
7999
8000
8001                 return;
8002             }
8003
8004             Roo.callback(o.failure, o.scope, [this, action]);
8005             // show an error message if no failed handler is set..
8006             if (!this.hasListener('actionfailed')) {
8007                 Roo.log("need to add dialog support");
8008                 /*
8009                 Roo.MessageBox.alert("Error",
8010                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8011                         action.result.errorMsg :
8012                         "Saving Failed, please check your entries or try again"
8013                 );
8014                 */
8015             }
8016
8017             this.fireEvent('actionfailed', this, action);
8018         }
8019
8020     },
8021     /**
8022      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8023      * @param {String} id The value to search for
8024      * @return Field
8025      */
8026     findField : function(id){
8027         var items = this.getItems();
8028         var field = items.get(id);
8029         if(!field){
8030              items.each(function(f){
8031                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8032                     field = f;
8033                     return false;
8034                 }
8035                 return true;
8036             });
8037         }
8038         return field || null;
8039     },
8040      /**
8041      * Mark fields in this form invalid in bulk.
8042      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8043      * @return {BasicForm} this
8044      */
8045     markInvalid : function(errors){
8046         if(errors instanceof Array){
8047             for(var i = 0, len = errors.length; i < len; i++){
8048                 var fieldError = errors[i];
8049                 var f = this.findField(fieldError.id);
8050                 if(f){
8051                     f.markInvalid(fieldError.msg);
8052                 }
8053             }
8054         }else{
8055             var field, id;
8056             for(id in errors){
8057                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8058                     field.markInvalid(errors[id]);
8059                 }
8060             }
8061         }
8062         //Roo.each(this.childForms || [], function (f) {
8063         //    f.markInvalid(errors);
8064         //});
8065
8066         return this;
8067     },
8068
8069     /**
8070      * Set values for fields in this form in bulk.
8071      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8072      * @return {BasicForm} this
8073      */
8074     setValues : function(values){
8075         if(values instanceof Array){ // array of objects
8076             for(var i = 0, len = values.length; i < len; i++){
8077                 var v = values[i];
8078                 var f = this.findField(v.id);
8079                 if(f){
8080                     f.setValue(v.value);
8081                     if(this.trackResetOnLoad){
8082                         f.originalValue = f.getValue();
8083                     }
8084                 }
8085             }
8086         }else{ // object hash
8087             var field, id;
8088             for(id in values){
8089                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8090
8091                     if (field.setFromData &&
8092                         field.valueField &&
8093                         field.displayField &&
8094                         // combos' with local stores can
8095                         // be queried via setValue()
8096                         // to set their value..
8097                         (field.store && !field.store.isLocal)
8098                         ) {
8099                         // it's a combo
8100                         var sd = { };
8101                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8102                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8103                         field.setFromData(sd);
8104
8105                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8106                         
8107                         field.setFromData(values);
8108                         
8109                     } else {
8110                         field.setValue(values[id]);
8111                     }
8112
8113
8114                     if(this.trackResetOnLoad){
8115                         field.originalValue = field.getValue();
8116                     }
8117                 }
8118             }
8119         }
8120
8121         //Roo.each(this.childForms || [], function (f) {
8122         //    f.setValues(values);
8123         //});
8124
8125         return this;
8126     },
8127
8128     /**
8129      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8130      * they are returned as an array.
8131      * @param {Boolean} asString
8132      * @return {Object}
8133      */
8134     getValues : function(asString){
8135         //if (this.childForms) {
8136             // copy values from the child forms
8137         //    Roo.each(this.childForms, function (f) {
8138         //        this.setValues(f.getValues());
8139         //    }, this);
8140         //}
8141
8142
8143
8144         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8145         if(asString === true){
8146             return fs;
8147         }
8148         return Roo.urlDecode(fs);
8149     },
8150
8151     /**
8152      * Returns the fields in this form as an object with key/value pairs.
8153      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8154      * @return {Object}
8155      */
8156     getFieldValues : function(with_hidden)
8157     {
8158         var items = this.getItems();
8159         var ret = {};
8160         items.each(function(f){
8161             
8162             if (!f.getName()) {
8163                 return;
8164             }
8165             
8166             var v = f.getValue();
8167             
8168             if (f.inputType =='radio') {
8169                 if (typeof(ret[f.getName()]) == 'undefined') {
8170                     ret[f.getName()] = ''; // empty..
8171                 }
8172
8173                 if (!f.el.dom.checked) {
8174                     return;
8175
8176                 }
8177                 v = f.el.dom.value;
8178
8179             }
8180             
8181             if(f.xtype == 'MoneyField'){
8182                 ret[f.currencyName] = f.getCurrency();
8183             }
8184
8185             // not sure if this supported any more..
8186             if ((typeof(v) == 'object') && f.getRawValue) {
8187                 v = f.getRawValue() ; // dates..
8188             }
8189             // combo boxes where name != hiddenName...
8190             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8191                 ret[f.name] = f.getRawValue();
8192             }
8193             ret[f.getName()] = v;
8194         });
8195
8196         return ret;
8197     },
8198
8199     /**
8200      * Clears all invalid messages in this form.
8201      * @return {BasicForm} this
8202      */
8203     clearInvalid : function(){
8204         var items = this.getItems();
8205
8206         items.each(function(f){
8207            f.clearInvalid();
8208         });
8209
8210         return this;
8211     },
8212
8213     /**
8214      * Resets this form.
8215      * @return {BasicForm} this
8216      */
8217     reset : function(){
8218         var items = this.getItems();
8219         items.each(function(f){
8220             f.reset();
8221         });
8222
8223         Roo.each(this.childForms || [], function (f) {
8224             f.reset();
8225         });
8226
8227
8228         return this;
8229     },
8230     
8231     getItems : function()
8232     {
8233         var r=new Roo.util.MixedCollection(false, function(o){
8234             return o.id || (o.id = Roo.id());
8235         });
8236         var iter = function(el) {
8237             if (el.inputEl) {
8238                 r.add(el);
8239             }
8240             if (!el.items) {
8241                 return;
8242             }
8243             Roo.each(el.items,function(e) {
8244                 iter(e);
8245             });
8246         };
8247
8248         iter(this);
8249         return r;
8250     },
8251     
8252     hideFields : function(items)
8253     {
8254         Roo.each(items, function(i){
8255             
8256             var f = this.findField(i);
8257             
8258             if(!f){
8259                 return;
8260             }
8261             
8262             if(f.xtype == 'DateField'){
8263                 f.setVisible(false);
8264                 return;
8265             }
8266             
8267             f.hide();
8268             
8269         }, this);
8270     },
8271     
8272     showFields : function(items)
8273     {
8274         Roo.each(items, function(i){
8275             
8276             var f = this.findField(i);
8277             
8278             if(!f){
8279                 return;
8280             }
8281             
8282             if(f.xtype == 'DateField'){
8283                 f.setVisible(true);
8284                 return;
8285             }
8286             
8287             f.show();
8288             
8289         }, this);
8290     }
8291
8292 });
8293
8294 Roo.apply(Roo.bootstrap.Form, {
8295     
8296     popover : {
8297         
8298         padding : 5,
8299         
8300         isApplied : false,
8301         
8302         isMasked : false,
8303         
8304         form : false,
8305         
8306         target : false,
8307         
8308         toolTip : false,
8309         
8310         intervalID : false,
8311         
8312         maskEl : false,
8313         
8314         apply : function()
8315         {
8316             if(this.isApplied){
8317                 return;
8318             }
8319             
8320             this.maskEl = {
8321                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8322                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8323                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8324                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8325             };
8326             
8327             this.maskEl.top.enableDisplayMode("block");
8328             this.maskEl.left.enableDisplayMode("block");
8329             this.maskEl.bottom.enableDisplayMode("block");
8330             this.maskEl.right.enableDisplayMode("block");
8331             
8332             this.toolTip = new Roo.bootstrap.Tooltip({
8333                 cls : 'roo-form-error-popover',
8334                 alignment : {
8335                     'left' : ['r-l', [-2,0], 'right'],
8336                     'right' : ['l-r', [2,0], 'left'],
8337                     'bottom' : ['tl-bl', [0,2], 'top'],
8338                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8339                 }
8340             });
8341             
8342             this.toolTip.render(Roo.get(document.body));
8343
8344             this.toolTip.el.enableDisplayMode("block");
8345             
8346             Roo.get(document.body).on('click', function(){
8347                 this.unmask();
8348             }, this);
8349             
8350             Roo.get(document.body).on('touchstart', function(){
8351                 this.unmask();
8352             }, this);
8353             
8354             this.isApplied = true
8355         },
8356         
8357         mask : function(form, target)
8358         {
8359             this.form = form;
8360             
8361             this.target = target;
8362             
8363             if(!this.form.errorMask || !target.el){
8364                 return;
8365             }
8366             
8367             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8368             
8369             Roo.log(scrollable);
8370             
8371             var ot = this.target.el.calcOffsetsTo(scrollable);
8372             
8373             var scrollTo = ot[1] - this.form.maskOffset;
8374             
8375             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8376             
8377             scrollable.scrollTo('top', scrollTo);
8378             
8379             var box = this.target.el.getBox();
8380             Roo.log(box);
8381             var zIndex = Roo.bootstrap.Modal.zIndex++;
8382
8383             
8384             this.maskEl.top.setStyle('position', 'absolute');
8385             this.maskEl.top.setStyle('z-index', zIndex);
8386             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8387             this.maskEl.top.setLeft(0);
8388             this.maskEl.top.setTop(0);
8389             this.maskEl.top.show();
8390             
8391             this.maskEl.left.setStyle('position', 'absolute');
8392             this.maskEl.left.setStyle('z-index', zIndex);
8393             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8394             this.maskEl.left.setLeft(0);
8395             this.maskEl.left.setTop(box.y - this.padding);
8396             this.maskEl.left.show();
8397
8398             this.maskEl.bottom.setStyle('position', 'absolute');
8399             this.maskEl.bottom.setStyle('z-index', zIndex);
8400             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8401             this.maskEl.bottom.setLeft(0);
8402             this.maskEl.bottom.setTop(box.bottom + this.padding);
8403             this.maskEl.bottom.show();
8404
8405             this.maskEl.right.setStyle('position', 'absolute');
8406             this.maskEl.right.setStyle('z-index', zIndex);
8407             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8408             this.maskEl.right.setLeft(box.right + this.padding);
8409             this.maskEl.right.setTop(box.y - this.padding);
8410             this.maskEl.right.show();
8411
8412             this.toolTip.bindEl = this.target.el;
8413
8414             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8415
8416             var tip = this.target.blankText;
8417
8418             if(this.target.getValue() !== '' ) {
8419                 
8420                 if (this.target.invalidText.length) {
8421                     tip = this.target.invalidText;
8422                 } else if (this.target.regexText.length){
8423                     tip = this.target.regexText;
8424                 }
8425             }
8426
8427             this.toolTip.show(tip);
8428
8429             this.intervalID = window.setInterval(function() {
8430                 Roo.bootstrap.Form.popover.unmask();
8431             }, 10000);
8432
8433             window.onwheel = function(){ return false;};
8434             
8435             (function(){ this.isMasked = true; }).defer(500, this);
8436             
8437         },
8438         
8439         unmask : function()
8440         {
8441             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8442                 return;
8443             }
8444             
8445             this.maskEl.top.setStyle('position', 'absolute');
8446             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8447             this.maskEl.top.hide();
8448
8449             this.maskEl.left.setStyle('position', 'absolute');
8450             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8451             this.maskEl.left.hide();
8452
8453             this.maskEl.bottom.setStyle('position', 'absolute');
8454             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8455             this.maskEl.bottom.hide();
8456
8457             this.maskEl.right.setStyle('position', 'absolute');
8458             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8459             this.maskEl.right.hide();
8460             
8461             this.toolTip.hide();
8462             
8463             this.toolTip.el.hide();
8464             
8465             window.onwheel = function(){ return true;};
8466             
8467             if(this.intervalID){
8468                 window.clearInterval(this.intervalID);
8469                 this.intervalID = false;
8470             }
8471             
8472             this.isMasked = false;
8473             
8474         }
8475         
8476     }
8477     
8478 });
8479
8480 /*
8481  * Based on:
8482  * Ext JS Library 1.1.1
8483  * Copyright(c) 2006-2007, Ext JS, LLC.
8484  *
8485  * Originally Released Under LGPL - original licence link has changed is not relivant.
8486  *
8487  * Fork - LGPL
8488  * <script type="text/javascript">
8489  */
8490 /**
8491  * @class Roo.form.VTypes
8492  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8493  * @singleton
8494  */
8495 Roo.form.VTypes = function(){
8496     // closure these in so they are only created once.
8497     var alpha = /^[a-zA-Z_]+$/;
8498     var alphanum = /^[a-zA-Z0-9_]+$/;
8499     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8500     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8501
8502     // All these messages and functions are configurable
8503     return {
8504         /**
8505          * The function used to validate email addresses
8506          * @param {String} value The email address
8507          */
8508         'email' : function(v){
8509             return email.test(v);
8510         },
8511         /**
8512          * The error text to display when the email validation function returns false
8513          * @type String
8514          */
8515         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8516         /**
8517          * The keystroke filter mask to be applied on email input
8518          * @type RegExp
8519          */
8520         'emailMask' : /[a-z0-9_\.\-@]/i,
8521
8522         /**
8523          * The function used to validate URLs
8524          * @param {String} value The URL
8525          */
8526         'url' : function(v){
8527             return url.test(v);
8528         },
8529         /**
8530          * The error text to display when the url validation function returns false
8531          * @type String
8532          */
8533         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8534         
8535         /**
8536          * The function used to validate alpha values
8537          * @param {String} value The value
8538          */
8539         'alpha' : function(v){
8540             return alpha.test(v);
8541         },
8542         /**
8543          * The error text to display when the alpha validation function returns false
8544          * @type String
8545          */
8546         'alphaText' : 'This field should only contain letters and _',
8547         /**
8548          * The keystroke filter mask to be applied on alpha input
8549          * @type RegExp
8550          */
8551         'alphaMask' : /[a-z_]/i,
8552
8553         /**
8554          * The function used to validate alphanumeric values
8555          * @param {String} value The value
8556          */
8557         'alphanum' : function(v){
8558             return alphanum.test(v);
8559         },
8560         /**
8561          * The error text to display when the alphanumeric validation function returns false
8562          * @type String
8563          */
8564         'alphanumText' : 'This field should only contain letters, numbers and _',
8565         /**
8566          * The keystroke filter mask to be applied on alphanumeric input
8567          * @type RegExp
8568          */
8569         'alphanumMask' : /[a-z0-9_]/i
8570     };
8571 }();/*
8572  * - LGPL
8573  *
8574  * Input
8575  * 
8576  */
8577
8578 /**
8579  * @class Roo.bootstrap.Input
8580  * @extends Roo.bootstrap.Component
8581  * Bootstrap Input class
8582  * @cfg {Boolean} disabled is it disabled
8583  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8584  * @cfg {String} name name of the input
8585  * @cfg {string} fieldLabel - the label associated
8586  * @cfg {string} placeholder - placeholder to put in text.
8587  * @cfg {string}  before - input group add on before
8588  * @cfg {string} after - input group add on after
8589  * @cfg {string} size - (lg|sm) or leave empty..
8590  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8591  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8592  * @cfg {Number} md colspan out of 12 for computer-sized screens
8593  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8594  * @cfg {string} value default value of the input
8595  * @cfg {Number} labelWidth set the width of label 
8596  * @cfg {Number} labellg set the width of label (1-12)
8597  * @cfg {Number} labelmd set the width of label (1-12)
8598  * @cfg {Number} labelsm set the width of label (1-12)
8599  * @cfg {Number} labelxs set the width of label (1-12)
8600  * @cfg {String} labelAlign (top|left)
8601  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8602  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8603  * @cfg {String} indicatorpos (left|right) default left
8604  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8605  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8606
8607  * @cfg {String} align (left|center|right) Default left
8608  * @cfg {Boolean} forceFeedback (true|false) Default false
8609  * 
8610  * @constructor
8611  * Create a new Input
8612  * @param {Object} config The config object
8613  */
8614
8615 Roo.bootstrap.Input = function(config){
8616     
8617     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8618     
8619     this.addEvents({
8620         /**
8621          * @event focus
8622          * Fires when this field receives input focus.
8623          * @param {Roo.form.Field} this
8624          */
8625         focus : true,
8626         /**
8627          * @event blur
8628          * Fires when this field loses input focus.
8629          * @param {Roo.form.Field} this
8630          */
8631         blur : true,
8632         /**
8633          * @event specialkey
8634          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8635          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8636          * @param {Roo.form.Field} this
8637          * @param {Roo.EventObject} e The event object
8638          */
8639         specialkey : true,
8640         /**
8641          * @event change
8642          * Fires just before the field blurs if the field value has changed.
8643          * @param {Roo.form.Field} this
8644          * @param {Mixed} newValue The new value
8645          * @param {Mixed} oldValue The original value
8646          */
8647         change : true,
8648         /**
8649          * @event invalid
8650          * Fires after the field has been marked as invalid.
8651          * @param {Roo.form.Field} this
8652          * @param {String} msg The validation message
8653          */
8654         invalid : true,
8655         /**
8656          * @event valid
8657          * Fires after the field has been validated with no errors.
8658          * @param {Roo.form.Field} this
8659          */
8660         valid : true,
8661          /**
8662          * @event keyup
8663          * Fires after the key up
8664          * @param {Roo.form.Field} this
8665          * @param {Roo.EventObject}  e The event Object
8666          */
8667         keyup : true
8668     });
8669 };
8670
8671 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8672      /**
8673      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8674       automatic validation (defaults to "keyup").
8675      */
8676     validationEvent : "keyup",
8677      /**
8678      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8679      */
8680     validateOnBlur : true,
8681     /**
8682      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8683      */
8684     validationDelay : 250,
8685      /**
8686      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8687      */
8688     focusClass : "x-form-focus",  // not needed???
8689     
8690        
8691     /**
8692      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8693      */
8694     invalidClass : "has-warning",
8695     
8696     /**
8697      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8698      */
8699     validClass : "has-success",
8700     
8701     /**
8702      * @cfg {Boolean} hasFeedback (true|false) default true
8703      */
8704     hasFeedback : true,
8705     
8706     /**
8707      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8708      */
8709     invalidFeedbackClass : "glyphicon-warning-sign",
8710     
8711     /**
8712      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8713      */
8714     validFeedbackClass : "glyphicon-ok",
8715     
8716     /**
8717      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8718      */
8719     selectOnFocus : false,
8720     
8721      /**
8722      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8723      */
8724     maskRe : null,
8725        /**
8726      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8727      */
8728     vtype : null,
8729     
8730       /**
8731      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8732      */
8733     disableKeyFilter : false,
8734     
8735        /**
8736      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8737      */
8738     disabled : false,
8739      /**
8740      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8741      */
8742     allowBlank : true,
8743     /**
8744      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8745      */
8746     blankText : "Please complete this mandatory field",
8747     
8748      /**
8749      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8750      */
8751     minLength : 0,
8752     /**
8753      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8754      */
8755     maxLength : Number.MAX_VALUE,
8756     /**
8757      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8758      */
8759     minLengthText : "The minimum length for this field is {0}",
8760     /**
8761      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8762      */
8763     maxLengthText : "The maximum length for this field is {0}",
8764   
8765     
8766     /**
8767      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8768      * If available, this function will be called only after the basic validators all return true, and will be passed the
8769      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8770      */
8771     validator : null,
8772     /**
8773      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8774      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8775      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8776      */
8777     regex : null,
8778     /**
8779      * @cfg {String} regexText -- Depricated - use Invalid Text
8780      */
8781     regexText : "",
8782     
8783     /**
8784      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8785      */
8786     invalidText : "",
8787     
8788     
8789     
8790     autocomplete: false,
8791     
8792     
8793     fieldLabel : '',
8794     inputType : 'text',
8795     
8796     name : false,
8797     placeholder: false,
8798     before : false,
8799     after : false,
8800     size : false,
8801     hasFocus : false,
8802     preventMark: false,
8803     isFormField : true,
8804     value : '',
8805     labelWidth : 2,
8806     labelAlign : false,
8807     readOnly : false,
8808     align : false,
8809     formatedValue : false,
8810     forceFeedback : false,
8811     
8812     indicatorpos : 'left',
8813     
8814     labellg : 0,
8815     labelmd : 0,
8816     labelsm : 0,
8817     labelxs : 0,
8818     
8819     capture : '',
8820     accept : '',
8821     
8822     parentLabelAlign : function()
8823     {
8824         var parent = this;
8825         while (parent.parent()) {
8826             parent = parent.parent();
8827             if (typeof(parent.labelAlign) !='undefined') {
8828                 return parent.labelAlign;
8829             }
8830         }
8831         return 'left';
8832         
8833     },
8834     
8835     getAutoCreate : function()
8836     {
8837         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8838         
8839         var id = Roo.id();
8840         
8841         var cfg = {};
8842         
8843         if(this.inputType != 'hidden'){
8844             cfg.cls = 'form-group' //input-group
8845         }
8846         
8847         var input =  {
8848             tag: 'input',
8849             id : id,
8850             type : this.inputType,
8851             value : this.value,
8852             cls : 'form-control',
8853             placeholder : this.placeholder || '',
8854             autocomplete : this.autocomplete || 'new-password'
8855         };
8856         
8857         if(this.capture.length){
8858             input.capture = this.capture;
8859         }
8860         
8861         if(this.accept.length){
8862             input.accept = this.accept + "/*";
8863         }
8864         
8865         if(this.align){
8866             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8867         }
8868         
8869         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8870             input.maxLength = this.maxLength;
8871         }
8872         
8873         if (this.disabled) {
8874             input.disabled=true;
8875         }
8876         
8877         if (this.readOnly) {
8878             input.readonly=true;
8879         }
8880         
8881         if (this.name) {
8882             input.name = this.name;
8883         }
8884         
8885         if (this.size) {
8886             input.cls += ' input-' + this.size;
8887         }
8888         
8889         var settings=this;
8890         ['xs','sm','md','lg'].map(function(size){
8891             if (settings[size]) {
8892                 cfg.cls += ' col-' + size + '-' + settings[size];
8893             }
8894         });
8895         
8896         var inputblock = input;
8897         
8898         var feedback = {
8899             tag: 'span',
8900             cls: 'glyphicon form-control-feedback'
8901         };
8902             
8903         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8904             
8905             inputblock = {
8906                 cls : 'has-feedback',
8907                 cn :  [
8908                     input,
8909                     feedback
8910                 ] 
8911             };  
8912         }
8913         
8914         if (this.before || this.after) {
8915             
8916             inputblock = {
8917                 cls : 'input-group',
8918                 cn :  [] 
8919             };
8920             
8921             if (this.before && typeof(this.before) == 'string') {
8922                 
8923                 inputblock.cn.push({
8924                     tag :'span',
8925                     cls : 'roo-input-before input-group-addon',
8926                     html : this.before
8927                 });
8928             }
8929             if (this.before && typeof(this.before) == 'object') {
8930                 this.before = Roo.factory(this.before);
8931                 
8932                 inputblock.cn.push({
8933                     tag :'span',
8934                     cls : 'roo-input-before input-group-' +
8935                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8936                 });
8937             }
8938             
8939             inputblock.cn.push(input);
8940             
8941             if (this.after && typeof(this.after) == 'string') {
8942                 inputblock.cn.push({
8943                     tag :'span',
8944                     cls : 'roo-input-after input-group-addon',
8945                     html : this.after
8946                 });
8947             }
8948             if (this.after && typeof(this.after) == 'object') {
8949                 this.after = Roo.factory(this.after);
8950                 
8951                 inputblock.cn.push({
8952                     tag :'span',
8953                     cls : 'roo-input-after input-group-' +
8954                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8955                 });
8956             }
8957             
8958             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8959                 inputblock.cls += ' has-feedback';
8960                 inputblock.cn.push(feedback);
8961             }
8962         };
8963         
8964         if (align ==='left' && this.fieldLabel.length) {
8965             
8966             cfg.cls += ' roo-form-group-label-left';
8967             
8968             cfg.cn = [
8969                 {
8970                     tag : 'i',
8971                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8972                     tooltip : 'This field is required'
8973                 },
8974                 {
8975                     tag: 'label',
8976                     'for' :  id,
8977                     cls : 'control-label',
8978                     html : this.fieldLabel
8979
8980                 },
8981                 {
8982                     cls : "", 
8983                     cn: [
8984                         inputblock
8985                     ]
8986                 }
8987             ];
8988             
8989             var labelCfg = cfg.cn[1];
8990             var contentCfg = cfg.cn[2];
8991             
8992             if(this.indicatorpos == 'right'){
8993                 cfg.cn = [
8994                     {
8995                         tag: 'label',
8996                         'for' :  id,
8997                         cls : 'control-label',
8998                         cn : [
8999                             {
9000                                 tag : 'span',
9001                                 html : this.fieldLabel
9002                             },
9003                             {
9004                                 tag : 'i',
9005                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9006                                 tooltip : 'This field is required'
9007                             }
9008                         ]
9009                     },
9010                     {
9011                         cls : "",
9012                         cn: [
9013                             inputblock
9014                         ]
9015                     }
9016
9017                 ];
9018                 
9019                 labelCfg = cfg.cn[0];
9020                 contentCfg = cfg.cn[1];
9021             
9022             }
9023             
9024             if(this.labelWidth > 12){
9025                 labelCfg.style = "width: " + this.labelWidth + 'px';
9026             }
9027             
9028             if(this.labelWidth < 13 && this.labelmd == 0){
9029                 this.labelmd = this.labelWidth;
9030             }
9031             
9032             if(this.labellg > 0){
9033                 labelCfg.cls += ' col-lg-' + this.labellg;
9034                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9035             }
9036             
9037             if(this.labelmd > 0){
9038                 labelCfg.cls += ' col-md-' + this.labelmd;
9039                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9040             }
9041             
9042             if(this.labelsm > 0){
9043                 labelCfg.cls += ' col-sm-' + this.labelsm;
9044                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9045             }
9046             
9047             if(this.labelxs > 0){
9048                 labelCfg.cls += ' col-xs-' + this.labelxs;
9049                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9050             }
9051             
9052             
9053         } else if ( this.fieldLabel.length) {
9054                 
9055             cfg.cn = [
9056                 {
9057                     tag : 'i',
9058                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9059                     tooltip : 'This field is required'
9060                 },
9061                 {
9062                     tag: 'label',
9063                    //cls : 'input-group-addon',
9064                     html : this.fieldLabel
9065
9066                 },
9067
9068                inputblock
9069
9070            ];
9071            
9072            if(this.indicatorpos == 'right'){
9073                 
9074                 cfg.cn = [
9075                     {
9076                         tag: 'label',
9077                        //cls : 'input-group-addon',
9078                         html : this.fieldLabel
9079
9080                     },
9081                     {
9082                         tag : 'i',
9083                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9084                         tooltip : 'This field is required'
9085                     },
9086
9087                    inputblock
9088
9089                ];
9090
9091             }
9092
9093         } else {
9094             
9095             cfg.cn = [
9096
9097                     inputblock
9098
9099             ];
9100                 
9101                 
9102         };
9103         
9104         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9105            cfg.cls += ' navbar-form';
9106         }
9107         
9108         if (this.parentType === 'NavGroup') {
9109            cfg.cls += ' navbar-form';
9110            cfg.tag = 'li';
9111         }
9112         
9113         return cfg;
9114         
9115     },
9116     /**
9117      * return the real input element.
9118      */
9119     inputEl: function ()
9120     {
9121         return this.el.select('input.form-control',true).first();
9122     },
9123     
9124     tooltipEl : function()
9125     {
9126         return this.inputEl();
9127     },
9128     
9129     indicatorEl : function()
9130     {
9131         var indicator = this.el.select('i.roo-required-indicator',true).first();
9132         
9133         if(!indicator){
9134             return false;
9135         }
9136         
9137         return indicator;
9138         
9139     },
9140     
9141     setDisabled : function(v)
9142     {
9143         var i  = this.inputEl().dom;
9144         if (!v) {
9145             i.removeAttribute('disabled');
9146             return;
9147             
9148         }
9149         i.setAttribute('disabled','true');
9150     },
9151     initEvents : function()
9152     {
9153           
9154         this.inputEl().on("keydown" , this.fireKey,  this);
9155         this.inputEl().on("focus", this.onFocus,  this);
9156         this.inputEl().on("blur", this.onBlur,  this);
9157         
9158         this.inputEl().relayEvent('keyup', this);
9159         
9160         this.indicator = this.indicatorEl();
9161         
9162         if(this.indicator){
9163             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9164         }
9165  
9166         // reference to original value for reset
9167         this.originalValue = this.getValue();
9168         //Roo.form.TextField.superclass.initEvents.call(this);
9169         if(this.validationEvent == 'keyup'){
9170             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9171             this.inputEl().on('keyup', this.filterValidation, this);
9172         }
9173         else if(this.validationEvent !== false){
9174             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9175         }
9176         
9177         if(this.selectOnFocus){
9178             this.on("focus", this.preFocus, this);
9179             
9180         }
9181         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9182             this.inputEl().on("keypress", this.filterKeys, this);
9183         } else {
9184             this.inputEl().relayEvent('keypress', this);
9185         }
9186        /* if(this.grow){
9187             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9188             this.el.on("click", this.autoSize,  this);
9189         }
9190         */
9191         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9192             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9193         }
9194         
9195         if (typeof(this.before) == 'object') {
9196             this.before.render(this.el.select('.roo-input-before',true).first());
9197         }
9198         if (typeof(this.after) == 'object') {
9199             this.after.render(this.el.select('.roo-input-after',true).first());
9200         }
9201         
9202         this.inputEl().on('change', this.onChange, this);
9203         
9204     },
9205     filterValidation : function(e){
9206         if(!e.isNavKeyPress()){
9207             this.validationTask.delay(this.validationDelay);
9208         }
9209     },
9210      /**
9211      * Validates the field value
9212      * @return {Boolean} True if the value is valid, else false
9213      */
9214     validate : function(){
9215         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9216         if(this.disabled || this.validateValue(this.getRawValue())){
9217             this.markValid();
9218             return true;
9219         }
9220         
9221         this.markInvalid();
9222         return false;
9223     },
9224     
9225     
9226     /**
9227      * Validates a value according to the field's validation rules and marks the field as invalid
9228      * if the validation fails
9229      * @param {Mixed} value The value to validate
9230      * @return {Boolean} True if the value is valid, else false
9231      */
9232     validateValue : function(value)
9233     {
9234         if(this.getVisibilityEl().hasClass('hidden')){
9235             return true;
9236         }
9237         
9238         if(value.length < 1)  { // if it's blank
9239             if(this.allowBlank){
9240                 return true;
9241             }
9242             return false;
9243         }
9244         
9245         if(value.length < this.minLength){
9246             return false;
9247         }
9248         if(value.length > this.maxLength){
9249             return false;
9250         }
9251         if(this.vtype){
9252             var vt = Roo.form.VTypes;
9253             if(!vt[this.vtype](value, this)){
9254                 return false;
9255             }
9256         }
9257         if(typeof this.validator == "function"){
9258             var msg = this.validator(value);
9259             if(msg !== true){
9260                 return false;
9261             }
9262             if (typeof(msg) == 'string') {
9263                 this.invalidText = msg;
9264             }
9265         }
9266         
9267         if(this.regex && !this.regex.test(value)){
9268             return false;
9269         }
9270         
9271         return true;
9272     },
9273     
9274      // private
9275     fireKey : function(e){
9276         //Roo.log('field ' + e.getKey());
9277         if(e.isNavKeyPress()){
9278             this.fireEvent("specialkey", this, e);
9279         }
9280     },
9281     focus : function (selectText){
9282         if(this.rendered){
9283             this.inputEl().focus();
9284             if(selectText === true){
9285                 this.inputEl().dom.select();
9286             }
9287         }
9288         return this;
9289     } ,
9290     
9291     onFocus : function(){
9292         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9293            // this.el.addClass(this.focusClass);
9294         }
9295         if(!this.hasFocus){
9296             this.hasFocus = true;
9297             this.startValue = this.getValue();
9298             this.fireEvent("focus", this);
9299         }
9300     },
9301     
9302     beforeBlur : Roo.emptyFn,
9303
9304     
9305     // private
9306     onBlur : function(){
9307         this.beforeBlur();
9308         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9309             //this.el.removeClass(this.focusClass);
9310         }
9311         this.hasFocus = false;
9312         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9313             this.validate();
9314         }
9315         var v = this.getValue();
9316         if(String(v) !== String(this.startValue)){
9317             this.fireEvent('change', this, v, this.startValue);
9318         }
9319         this.fireEvent("blur", this);
9320     },
9321     
9322     onChange : function(e)
9323     {
9324         var v = this.getValue();
9325         if(String(v) !== String(this.startValue)){
9326             this.fireEvent('change', this, v, this.startValue);
9327         }
9328         
9329     },
9330     
9331     /**
9332      * Resets the current field value to the originally loaded value and clears any validation messages
9333      */
9334     reset : function(){
9335         this.setValue(this.originalValue);
9336         this.validate();
9337     },
9338      /**
9339      * Returns the name of the field
9340      * @return {Mixed} name The name field
9341      */
9342     getName: function(){
9343         return this.name;
9344     },
9345      /**
9346      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9347      * @return {Mixed} value The field value
9348      */
9349     getValue : function(){
9350         
9351         var v = this.inputEl().getValue();
9352         
9353         return v;
9354     },
9355     /**
9356      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9357      * @return {Mixed} value The field value
9358      */
9359     getRawValue : function(){
9360         var v = this.inputEl().getValue();
9361         
9362         return v;
9363     },
9364     
9365     /**
9366      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9367      * @param {Mixed} value The value to set
9368      */
9369     setRawValue : function(v){
9370         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9371     },
9372     
9373     selectText : function(start, end){
9374         var v = this.getRawValue();
9375         if(v.length > 0){
9376             start = start === undefined ? 0 : start;
9377             end = end === undefined ? v.length : end;
9378             var d = this.inputEl().dom;
9379             if(d.setSelectionRange){
9380                 d.setSelectionRange(start, end);
9381             }else if(d.createTextRange){
9382                 var range = d.createTextRange();
9383                 range.moveStart("character", start);
9384                 range.moveEnd("character", v.length-end);
9385                 range.select();
9386             }
9387         }
9388     },
9389     
9390     /**
9391      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9392      * @param {Mixed} value The value to set
9393      */
9394     setValue : function(v){
9395         this.value = v;
9396         if(this.rendered){
9397             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9398             this.validate();
9399         }
9400     },
9401     
9402     /*
9403     processValue : function(value){
9404         if(this.stripCharsRe){
9405             var newValue = value.replace(this.stripCharsRe, '');
9406             if(newValue !== value){
9407                 this.setRawValue(newValue);
9408                 return newValue;
9409             }
9410         }
9411         return value;
9412     },
9413   */
9414     preFocus : function(){
9415         
9416         if(this.selectOnFocus){
9417             this.inputEl().dom.select();
9418         }
9419     },
9420     filterKeys : function(e){
9421         var k = e.getKey();
9422         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9423             return;
9424         }
9425         var c = e.getCharCode(), cc = String.fromCharCode(c);
9426         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9427             return;
9428         }
9429         if(!this.maskRe.test(cc)){
9430             e.stopEvent();
9431         }
9432     },
9433      /**
9434      * Clear any invalid styles/messages for this field
9435      */
9436     clearInvalid : function(){
9437         
9438         if(!this.el || this.preventMark){ // not rendered
9439             return;
9440         }
9441         
9442      
9443         this.el.removeClass(this.invalidClass);
9444         
9445         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9446             
9447             var feedback = this.el.select('.form-control-feedback', true).first();
9448             
9449             if(feedback){
9450                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9451             }
9452             
9453         }
9454         
9455         if(this.indicator){
9456             this.indicator.removeClass('visible');
9457             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9458         }
9459         
9460         this.fireEvent('valid', this);
9461     },
9462     
9463      /**
9464      * Mark this field as valid
9465      */
9466     markValid : function()
9467     {
9468         if(!this.el  || this.preventMark){ // not rendered...
9469             return;
9470         }
9471         
9472         this.el.removeClass([this.invalidClass, this.validClass]);
9473         
9474         var feedback = this.el.select('.form-control-feedback', true).first();
9475             
9476         if(feedback){
9477             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9478         }
9479         
9480         if(this.indicator){
9481             this.indicator.removeClass('visible');
9482             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9483         }
9484         
9485         if(this.disabled){
9486             return;
9487         }
9488         
9489         if(this.allowBlank && !this.getRawValue().length){
9490             return;
9491         }
9492         
9493         this.el.addClass(this.validClass);
9494         
9495         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9496             
9497             var feedback = this.el.select('.form-control-feedback', true).first();
9498             
9499             if(feedback){
9500                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9501                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9502             }
9503             
9504         }
9505         
9506         this.fireEvent('valid', this);
9507     },
9508     
9509      /**
9510      * Mark this field as invalid
9511      * @param {String} msg The validation message
9512      */
9513     markInvalid : function(msg)
9514     {
9515         if(!this.el  || this.preventMark){ // not rendered
9516             return;
9517         }
9518         
9519         this.el.removeClass([this.invalidClass, this.validClass]);
9520         
9521         var feedback = this.el.select('.form-control-feedback', true).first();
9522             
9523         if(feedback){
9524             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9525         }
9526
9527         if(this.disabled){
9528             return;
9529         }
9530         
9531         if(this.allowBlank && !this.getRawValue().length){
9532             return;
9533         }
9534         
9535         if(this.indicator){
9536             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9537             this.indicator.addClass('visible');
9538         }
9539         
9540         this.el.addClass(this.invalidClass);
9541         
9542         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9543             
9544             var feedback = this.el.select('.form-control-feedback', true).first();
9545             
9546             if(feedback){
9547                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9548                 
9549                 if(this.getValue().length || this.forceFeedback){
9550                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9551                 }
9552                 
9553             }
9554             
9555         }
9556         
9557         this.fireEvent('invalid', this, msg);
9558     },
9559     // private
9560     SafariOnKeyDown : function(event)
9561     {
9562         // this is a workaround for a password hang bug on chrome/ webkit.
9563         if (this.inputEl().dom.type != 'password') {
9564             return;
9565         }
9566         
9567         var isSelectAll = false;
9568         
9569         if(this.inputEl().dom.selectionEnd > 0){
9570             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9571         }
9572         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9573             event.preventDefault();
9574             this.setValue('');
9575             return;
9576         }
9577         
9578         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9579             
9580             event.preventDefault();
9581             // this is very hacky as keydown always get's upper case.
9582             //
9583             var cc = String.fromCharCode(event.getCharCode());
9584             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9585             
9586         }
9587     },
9588     adjustWidth : function(tag, w){
9589         tag = tag.toLowerCase();
9590         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9591             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9592                 if(tag == 'input'){
9593                     return w + 2;
9594                 }
9595                 if(tag == 'textarea'){
9596                     return w-2;
9597                 }
9598             }else if(Roo.isOpera){
9599                 if(tag == 'input'){
9600                     return w + 2;
9601                 }
9602                 if(tag == 'textarea'){
9603                     return w-2;
9604                 }
9605             }
9606         }
9607         return w;
9608     },
9609     
9610     setFieldLabel : function(v)
9611     {
9612         if(!this.rendered){
9613             return;
9614         }
9615         
9616         if(this.indicator){
9617             var ar = this.el.select('label > span',true);
9618             
9619             if (ar.elements.length) {
9620                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9621                 this.fieldLabel = v;
9622                 return;
9623             }
9624             
9625             var br = this.el.select('label',true);
9626             
9627             if(br.elements.length) {
9628                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9629                 this.fieldLabel = v;
9630                 return;
9631             }
9632             
9633             Roo.log('Cannot Found any of label > span || label in input');
9634             return;
9635         }
9636         
9637         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9638         this.fieldLabel = v;
9639         
9640         
9641     }
9642 });
9643
9644  
9645 /*
9646  * - LGPL
9647  *
9648  * Input
9649  * 
9650  */
9651
9652 /**
9653  * @class Roo.bootstrap.TextArea
9654  * @extends Roo.bootstrap.Input
9655  * Bootstrap TextArea class
9656  * @cfg {Number} cols Specifies the visible width of a text area
9657  * @cfg {Number} rows Specifies the visible number of lines in a text area
9658  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9659  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9660  * @cfg {string} html text
9661  * 
9662  * @constructor
9663  * Create a new TextArea
9664  * @param {Object} config The config object
9665  */
9666
9667 Roo.bootstrap.TextArea = function(config){
9668     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9669    
9670 };
9671
9672 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9673      
9674     cols : false,
9675     rows : 5,
9676     readOnly : false,
9677     warp : 'soft',
9678     resize : false,
9679     value: false,
9680     html: false,
9681     
9682     getAutoCreate : function(){
9683         
9684         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9685         
9686         var id = Roo.id();
9687         
9688         var cfg = {};
9689         
9690         if(this.inputType != 'hidden'){
9691             cfg.cls = 'form-group' //input-group
9692         }
9693         
9694         var input =  {
9695             tag: 'textarea',
9696             id : id,
9697             warp : this.warp,
9698             rows : this.rows,
9699             value : this.value || '',
9700             html: this.html || '',
9701             cls : 'form-control',
9702             placeholder : this.placeholder || '' 
9703             
9704         };
9705         
9706         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9707             input.maxLength = this.maxLength;
9708         }
9709         
9710         if(this.resize){
9711             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9712         }
9713         
9714         if(this.cols){
9715             input.cols = this.cols;
9716         }
9717         
9718         if (this.readOnly) {
9719             input.readonly = true;
9720         }
9721         
9722         if (this.name) {
9723             input.name = this.name;
9724         }
9725         
9726         if (this.size) {
9727             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9728         }
9729         
9730         var settings=this;
9731         ['xs','sm','md','lg'].map(function(size){
9732             if (settings[size]) {
9733                 cfg.cls += ' col-' + size + '-' + settings[size];
9734             }
9735         });
9736         
9737         var inputblock = input;
9738         
9739         if(this.hasFeedback && !this.allowBlank){
9740             
9741             var feedback = {
9742                 tag: 'span',
9743                 cls: 'glyphicon form-control-feedback'
9744             };
9745
9746             inputblock = {
9747                 cls : 'has-feedback',
9748                 cn :  [
9749                     input,
9750                     feedback
9751                 ] 
9752             };  
9753         }
9754         
9755         
9756         if (this.before || this.after) {
9757             
9758             inputblock = {
9759                 cls : 'input-group',
9760                 cn :  [] 
9761             };
9762             if (this.before) {
9763                 inputblock.cn.push({
9764                     tag :'span',
9765                     cls : 'input-group-addon',
9766                     html : this.before
9767                 });
9768             }
9769             
9770             inputblock.cn.push(input);
9771             
9772             if(this.hasFeedback && !this.allowBlank){
9773                 inputblock.cls += ' has-feedback';
9774                 inputblock.cn.push(feedback);
9775             }
9776             
9777             if (this.after) {
9778                 inputblock.cn.push({
9779                     tag :'span',
9780                     cls : 'input-group-addon',
9781                     html : this.after
9782                 });
9783             }
9784             
9785         }
9786         
9787         if (align ==='left' && this.fieldLabel.length) {
9788             cfg.cn = [
9789                 {
9790                     tag: 'label',
9791                     'for' :  id,
9792                     cls : 'control-label',
9793                     html : this.fieldLabel
9794                 },
9795                 {
9796                     cls : "",
9797                     cn: [
9798                         inputblock
9799                     ]
9800                 }
9801
9802             ];
9803             
9804             if(this.labelWidth > 12){
9805                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9806             }
9807
9808             if(this.labelWidth < 13 && this.labelmd == 0){
9809                 this.labelmd = this.labelWidth;
9810             }
9811
9812             if(this.labellg > 0){
9813                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9814                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9815             }
9816
9817             if(this.labelmd > 0){
9818                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9819                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9820             }
9821
9822             if(this.labelsm > 0){
9823                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9824                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9825             }
9826
9827             if(this.labelxs > 0){
9828                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9829                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9830             }
9831             
9832         } else if ( this.fieldLabel.length) {
9833             cfg.cn = [
9834
9835                {
9836                    tag: 'label',
9837                    //cls : 'input-group-addon',
9838                    html : this.fieldLabel
9839
9840                },
9841
9842                inputblock
9843
9844            ];
9845
9846         } else {
9847
9848             cfg.cn = [
9849
9850                 inputblock
9851
9852             ];
9853                 
9854         }
9855         
9856         if (this.disabled) {
9857             input.disabled=true;
9858         }
9859         
9860         return cfg;
9861         
9862     },
9863     /**
9864      * return the real textarea element.
9865      */
9866     inputEl: function ()
9867     {
9868         return this.el.select('textarea.form-control',true).first();
9869     },
9870     
9871     /**
9872      * Clear any invalid styles/messages for this field
9873      */
9874     clearInvalid : function()
9875     {
9876         
9877         if(!this.el || this.preventMark){ // not rendered
9878             return;
9879         }
9880         
9881         var label = this.el.select('label', true).first();
9882         var icon = this.el.select('i.fa-star', true).first();
9883         
9884         if(label && icon){
9885             icon.remove();
9886         }
9887         
9888         this.el.removeClass(this.invalidClass);
9889         
9890         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9891             
9892             var feedback = this.el.select('.form-control-feedback', true).first();
9893             
9894             if(feedback){
9895                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9896             }
9897             
9898         }
9899         
9900         this.fireEvent('valid', this);
9901     },
9902     
9903      /**
9904      * Mark this field as valid
9905      */
9906     markValid : function()
9907     {
9908         if(!this.el  || this.preventMark){ // not rendered
9909             return;
9910         }
9911         
9912         this.el.removeClass([this.invalidClass, this.validClass]);
9913         
9914         var feedback = this.el.select('.form-control-feedback', true).first();
9915             
9916         if(feedback){
9917             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9918         }
9919
9920         if(this.disabled || this.allowBlank){
9921             return;
9922         }
9923         
9924         var label = this.el.select('label', true).first();
9925         var icon = this.el.select('i.fa-star', true).first();
9926         
9927         if(label && icon){
9928             icon.remove();
9929         }
9930         
9931         this.el.addClass(this.validClass);
9932         
9933         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9934             
9935             var feedback = this.el.select('.form-control-feedback', true).first();
9936             
9937             if(feedback){
9938                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9939                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9940             }
9941             
9942         }
9943         
9944         this.fireEvent('valid', this);
9945     },
9946     
9947      /**
9948      * Mark this field as invalid
9949      * @param {String} msg The validation message
9950      */
9951     markInvalid : function(msg)
9952     {
9953         if(!this.el  || this.preventMark){ // not rendered
9954             return;
9955         }
9956         
9957         this.el.removeClass([this.invalidClass, this.validClass]);
9958         
9959         var feedback = this.el.select('.form-control-feedback', true).first();
9960             
9961         if(feedback){
9962             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9963         }
9964
9965         if(this.disabled || this.allowBlank){
9966             return;
9967         }
9968         
9969         var label = this.el.select('label', true).first();
9970         var icon = this.el.select('i.fa-star', true).first();
9971         
9972         if(!this.getValue().length && label && !icon){
9973             this.el.createChild({
9974                 tag : 'i',
9975                 cls : 'text-danger fa fa-lg fa-star',
9976                 tooltip : 'This field is required',
9977                 style : 'margin-right:5px;'
9978             }, label, true);
9979         }
9980
9981         this.el.addClass(this.invalidClass);
9982         
9983         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9984             
9985             var feedback = this.el.select('.form-control-feedback', true).first();
9986             
9987             if(feedback){
9988                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9989                 
9990                 if(this.getValue().length || this.forceFeedback){
9991                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9992                 }
9993                 
9994             }
9995             
9996         }
9997         
9998         this.fireEvent('invalid', this, msg);
9999     }
10000 });
10001
10002  
10003 /*
10004  * - LGPL
10005  *
10006  * trigger field - base class for combo..
10007  * 
10008  */
10009  
10010 /**
10011  * @class Roo.bootstrap.TriggerField
10012  * @extends Roo.bootstrap.Input
10013  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10014  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10015  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10016  * for which you can provide a custom implementation.  For example:
10017  * <pre><code>
10018 var trigger = new Roo.bootstrap.TriggerField();
10019 trigger.onTriggerClick = myTriggerFn;
10020 trigger.applyTo('my-field');
10021 </code></pre>
10022  *
10023  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10024  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10025  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10026  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10027  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10028
10029  * @constructor
10030  * Create a new TriggerField.
10031  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10032  * to the base TextField)
10033  */
10034 Roo.bootstrap.TriggerField = function(config){
10035     this.mimicing = false;
10036     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10037 };
10038
10039 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10040     /**
10041      * @cfg {String} triggerClass A CSS class to apply to the trigger
10042      */
10043      /**
10044      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10045      */
10046     hideTrigger:false,
10047
10048     /**
10049      * @cfg {Boolean} removable (true|false) special filter default false
10050      */
10051     removable : false,
10052     
10053     /** @cfg {Boolean} grow @hide */
10054     /** @cfg {Number} growMin @hide */
10055     /** @cfg {Number} growMax @hide */
10056
10057     /**
10058      * @hide 
10059      * @method
10060      */
10061     autoSize: Roo.emptyFn,
10062     // private
10063     monitorTab : true,
10064     // private
10065     deferHeight : true,
10066
10067     
10068     actionMode : 'wrap',
10069     
10070     caret : false,
10071     
10072     
10073     getAutoCreate : function(){
10074        
10075         var align = this.labelAlign || this.parentLabelAlign();
10076         
10077         var id = Roo.id();
10078         
10079         var cfg = {
10080             cls: 'form-group' //input-group
10081         };
10082         
10083         
10084         var input =  {
10085             tag: 'input',
10086             id : id,
10087             type : this.inputType,
10088             cls : 'form-control',
10089             autocomplete: 'new-password',
10090             placeholder : this.placeholder || '' 
10091             
10092         };
10093         if (this.name) {
10094             input.name = this.name;
10095         }
10096         if (this.size) {
10097             input.cls += ' input-' + this.size;
10098         }
10099         
10100         if (this.disabled) {
10101             input.disabled=true;
10102         }
10103         
10104         var inputblock = input;
10105         
10106         if(this.hasFeedback && !this.allowBlank){
10107             
10108             var feedback = {
10109                 tag: 'span',
10110                 cls: 'glyphicon form-control-feedback'
10111             };
10112             
10113             if(this.removable && !this.editable && !this.tickable){
10114                 inputblock = {
10115                     cls : 'has-feedback',
10116                     cn :  [
10117                         inputblock,
10118                         {
10119                             tag: 'button',
10120                             html : 'x',
10121                             cls : 'roo-combo-removable-btn close'
10122                         },
10123                         feedback
10124                     ] 
10125                 };
10126             } else {
10127                 inputblock = {
10128                     cls : 'has-feedback',
10129                     cn :  [
10130                         inputblock,
10131                         feedback
10132                     ] 
10133                 };
10134             }
10135
10136         } else {
10137             if(this.removable && !this.editable && !this.tickable){
10138                 inputblock = {
10139                     cls : 'roo-removable',
10140                     cn :  [
10141                         inputblock,
10142                         {
10143                             tag: 'button',
10144                             html : 'x',
10145                             cls : 'roo-combo-removable-btn close'
10146                         }
10147                     ] 
10148                 };
10149             }
10150         }
10151         
10152         if (this.before || this.after) {
10153             
10154             inputblock = {
10155                 cls : 'input-group',
10156                 cn :  [] 
10157             };
10158             if (this.before) {
10159                 inputblock.cn.push({
10160                     tag :'span',
10161                     cls : 'input-group-addon',
10162                     html : this.before
10163                 });
10164             }
10165             
10166             inputblock.cn.push(input);
10167             
10168             if(this.hasFeedback && !this.allowBlank){
10169                 inputblock.cls += ' has-feedback';
10170                 inputblock.cn.push(feedback);
10171             }
10172             
10173             if (this.after) {
10174                 inputblock.cn.push({
10175                     tag :'span',
10176                     cls : 'input-group-addon',
10177                     html : this.after
10178                 });
10179             }
10180             
10181         };
10182         
10183         var box = {
10184             tag: 'div',
10185             cn: [
10186                 {
10187                     tag: 'input',
10188                     type : 'hidden',
10189                     cls: 'form-hidden-field'
10190                 },
10191                 inputblock
10192             ]
10193             
10194         };
10195         
10196         if(this.multiple){
10197             box = {
10198                 tag: 'div',
10199                 cn: [
10200                     {
10201                         tag: 'input',
10202                         type : 'hidden',
10203                         cls: 'form-hidden-field'
10204                     },
10205                     {
10206                         tag: 'ul',
10207                         cls: 'roo-select2-choices',
10208                         cn:[
10209                             {
10210                                 tag: 'li',
10211                                 cls: 'roo-select2-search-field',
10212                                 cn: [
10213
10214                                     inputblock
10215                                 ]
10216                             }
10217                         ]
10218                     }
10219                 ]
10220             }
10221         };
10222         
10223         var combobox = {
10224             cls: 'roo-select2-container input-group',
10225             cn: [
10226                 box
10227 //                {
10228 //                    tag: 'ul',
10229 //                    cls: 'typeahead typeahead-long dropdown-menu',
10230 //                    style: 'display:none'
10231 //                }
10232             ]
10233         };
10234         
10235         if(!this.multiple && this.showToggleBtn){
10236             
10237             var caret = {
10238                         tag: 'span',
10239                         cls: 'caret'
10240              };
10241             if (this.caret != false) {
10242                 caret = {
10243                      tag: 'i',
10244                      cls: 'fa fa-' + this.caret
10245                 };
10246                 
10247             }
10248             
10249             combobox.cn.push({
10250                 tag :'span',
10251                 cls : 'input-group-addon btn dropdown-toggle',
10252                 cn : [
10253                     caret,
10254                     {
10255                         tag: 'span',
10256                         cls: 'combobox-clear',
10257                         cn  : [
10258                             {
10259                                 tag : 'i',
10260                                 cls: 'icon-remove'
10261                             }
10262                         ]
10263                     }
10264                 ]
10265
10266             })
10267         }
10268         
10269         if(this.multiple){
10270             combobox.cls += ' roo-select2-container-multi';
10271         }
10272         
10273         if (align ==='left' && this.fieldLabel.length) {
10274             
10275             cfg.cls += ' roo-form-group-label-left';
10276
10277             cfg.cn = [
10278                 {
10279                     tag : 'i',
10280                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10281                     tooltip : 'This field is required'
10282                 },
10283                 {
10284                     tag: 'label',
10285                     'for' :  id,
10286                     cls : 'control-label',
10287                     html : this.fieldLabel
10288
10289                 },
10290                 {
10291                     cls : "", 
10292                     cn: [
10293                         combobox
10294                     ]
10295                 }
10296
10297             ];
10298             
10299             var labelCfg = cfg.cn[1];
10300             var contentCfg = cfg.cn[2];
10301             
10302             if(this.indicatorpos == 'right'){
10303                 cfg.cn = [
10304                     {
10305                         tag: 'label',
10306                         'for' :  id,
10307                         cls : 'control-label',
10308                         cn : [
10309                             {
10310                                 tag : 'span',
10311                                 html : this.fieldLabel
10312                             },
10313                             {
10314                                 tag : 'i',
10315                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10316                                 tooltip : 'This field is required'
10317                             }
10318                         ]
10319                     },
10320                     {
10321                         cls : "", 
10322                         cn: [
10323                             combobox
10324                         ]
10325                     }
10326
10327                 ];
10328                 
10329                 labelCfg = cfg.cn[0];
10330                 contentCfg = cfg.cn[1];
10331             }
10332             
10333             if(this.labelWidth > 12){
10334                 labelCfg.style = "width: " + this.labelWidth + 'px';
10335             }
10336             
10337             if(this.labelWidth < 13 && this.labelmd == 0){
10338                 this.labelmd = this.labelWidth;
10339             }
10340             
10341             if(this.labellg > 0){
10342                 labelCfg.cls += ' col-lg-' + this.labellg;
10343                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10344             }
10345             
10346             if(this.labelmd > 0){
10347                 labelCfg.cls += ' col-md-' + this.labelmd;
10348                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10349             }
10350             
10351             if(this.labelsm > 0){
10352                 labelCfg.cls += ' col-sm-' + this.labelsm;
10353                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10354             }
10355             
10356             if(this.labelxs > 0){
10357                 labelCfg.cls += ' col-xs-' + this.labelxs;
10358                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10359             }
10360             
10361         } else if ( this.fieldLabel.length) {
10362 //                Roo.log(" label");
10363             cfg.cn = [
10364                 {
10365                    tag : 'i',
10366                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10367                    tooltip : 'This field is required'
10368                },
10369                {
10370                    tag: 'label',
10371                    //cls : 'input-group-addon',
10372                    html : this.fieldLabel
10373
10374                },
10375
10376                combobox
10377
10378             ];
10379             
10380             if(this.indicatorpos == 'right'){
10381                 
10382                 cfg.cn = [
10383                     {
10384                        tag: 'label',
10385                        cn : [
10386                            {
10387                                tag : 'span',
10388                                html : this.fieldLabel
10389                            },
10390                            {
10391                               tag : 'i',
10392                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10393                               tooltip : 'This field is required'
10394                            }
10395                        ]
10396
10397                     },
10398                     combobox
10399
10400                 ];
10401
10402             }
10403
10404         } else {
10405             
10406 //                Roo.log(" no label && no align");
10407                 cfg = combobox
10408                      
10409                 
10410         }
10411         
10412         var settings=this;
10413         ['xs','sm','md','lg'].map(function(size){
10414             if (settings[size]) {
10415                 cfg.cls += ' col-' + size + '-' + settings[size];
10416             }
10417         });
10418         
10419         return cfg;
10420         
10421     },
10422     
10423     
10424     
10425     // private
10426     onResize : function(w, h){
10427 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10428 //        if(typeof w == 'number'){
10429 //            var x = w - this.trigger.getWidth();
10430 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10431 //            this.trigger.setStyle('left', x+'px');
10432 //        }
10433     },
10434
10435     // private
10436     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10437
10438     // private
10439     getResizeEl : function(){
10440         return this.inputEl();
10441     },
10442
10443     // private
10444     getPositionEl : function(){
10445         return this.inputEl();
10446     },
10447
10448     // private
10449     alignErrorIcon : function(){
10450         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10451     },
10452
10453     // private
10454     initEvents : function(){
10455         
10456         this.createList();
10457         
10458         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10459         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10460         if(!this.multiple && this.showToggleBtn){
10461             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10462             if(this.hideTrigger){
10463                 this.trigger.setDisplayed(false);
10464             }
10465             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10466         }
10467         
10468         if(this.multiple){
10469             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10470         }
10471         
10472         if(this.removable && !this.editable && !this.tickable){
10473             var close = this.closeTriggerEl();
10474             
10475             if(close){
10476                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10477                 close.on('click', this.removeBtnClick, this, close);
10478             }
10479         }
10480         
10481         //this.trigger.addClassOnOver('x-form-trigger-over');
10482         //this.trigger.addClassOnClick('x-form-trigger-click');
10483         
10484         //if(!this.width){
10485         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10486         //}
10487     },
10488     
10489     closeTriggerEl : function()
10490     {
10491         var close = this.el.select('.roo-combo-removable-btn', true).first();
10492         return close ? close : false;
10493     },
10494     
10495     removeBtnClick : function(e, h, el)
10496     {
10497         e.preventDefault();
10498         
10499         if(this.fireEvent("remove", this) !== false){
10500             this.reset();
10501             this.fireEvent("afterremove", this)
10502         }
10503     },
10504     
10505     createList : function()
10506     {
10507         this.list = Roo.get(document.body).createChild({
10508             tag: 'ul',
10509             cls: 'typeahead typeahead-long dropdown-menu',
10510             style: 'display:none'
10511         });
10512         
10513         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10514         
10515     },
10516
10517     // private
10518     initTrigger : function(){
10519        
10520     },
10521
10522     // private
10523     onDestroy : function(){
10524         if(this.trigger){
10525             this.trigger.removeAllListeners();
10526           //  this.trigger.remove();
10527         }
10528         //if(this.wrap){
10529         //    this.wrap.remove();
10530         //}
10531         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10532     },
10533
10534     // private
10535     onFocus : function(){
10536         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10537         /*
10538         if(!this.mimicing){
10539             this.wrap.addClass('x-trigger-wrap-focus');
10540             this.mimicing = true;
10541             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10542             if(this.monitorTab){
10543                 this.el.on("keydown", this.checkTab, this);
10544             }
10545         }
10546         */
10547     },
10548
10549     // private
10550     checkTab : function(e){
10551         if(e.getKey() == e.TAB){
10552             this.triggerBlur();
10553         }
10554     },
10555
10556     // private
10557     onBlur : function(){
10558         // do nothing
10559     },
10560
10561     // private
10562     mimicBlur : function(e, t){
10563         /*
10564         if(!this.wrap.contains(t) && this.validateBlur()){
10565             this.triggerBlur();
10566         }
10567         */
10568     },
10569
10570     // private
10571     triggerBlur : function(){
10572         this.mimicing = false;
10573         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10574         if(this.monitorTab){
10575             this.el.un("keydown", this.checkTab, this);
10576         }
10577         //this.wrap.removeClass('x-trigger-wrap-focus');
10578         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10579     },
10580
10581     // private
10582     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10583     validateBlur : function(e, t){
10584         return true;
10585     },
10586
10587     // private
10588     onDisable : function(){
10589         this.inputEl().dom.disabled = true;
10590         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10591         //if(this.wrap){
10592         //    this.wrap.addClass('x-item-disabled');
10593         //}
10594     },
10595
10596     // private
10597     onEnable : function(){
10598         this.inputEl().dom.disabled = false;
10599         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10600         //if(this.wrap){
10601         //    this.el.removeClass('x-item-disabled');
10602         //}
10603     },
10604
10605     // private
10606     onShow : function(){
10607         var ae = this.getActionEl();
10608         
10609         if(ae){
10610             ae.dom.style.display = '';
10611             ae.dom.style.visibility = 'visible';
10612         }
10613     },
10614
10615     // private
10616     
10617     onHide : function(){
10618         var ae = this.getActionEl();
10619         ae.dom.style.display = 'none';
10620     },
10621
10622     /**
10623      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10624      * by an implementing function.
10625      * @method
10626      * @param {EventObject} e
10627      */
10628     onTriggerClick : Roo.emptyFn
10629 });
10630  /*
10631  * Based on:
10632  * Ext JS Library 1.1.1
10633  * Copyright(c) 2006-2007, Ext JS, LLC.
10634  *
10635  * Originally Released Under LGPL - original licence link has changed is not relivant.
10636  *
10637  * Fork - LGPL
10638  * <script type="text/javascript">
10639  */
10640
10641
10642 /**
10643  * @class Roo.data.SortTypes
10644  * @singleton
10645  * Defines the default sorting (casting?) comparison functions used when sorting data.
10646  */
10647 Roo.data.SortTypes = {
10648     /**
10649      * Default sort that does nothing
10650      * @param {Mixed} s The value being converted
10651      * @return {Mixed} The comparison value
10652      */
10653     none : function(s){
10654         return s;
10655     },
10656     
10657     /**
10658      * The regular expression used to strip tags
10659      * @type {RegExp}
10660      * @property
10661      */
10662     stripTagsRE : /<\/?[^>]+>/gi,
10663     
10664     /**
10665      * Strips all HTML tags to sort on text only
10666      * @param {Mixed} s The value being converted
10667      * @return {String} The comparison value
10668      */
10669     asText : function(s){
10670         return String(s).replace(this.stripTagsRE, "");
10671     },
10672     
10673     /**
10674      * Strips all HTML tags to sort on text only - Case insensitive
10675      * @param {Mixed} s The value being converted
10676      * @return {String} The comparison value
10677      */
10678     asUCText : function(s){
10679         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10680     },
10681     
10682     /**
10683      * Case insensitive string
10684      * @param {Mixed} s The value being converted
10685      * @return {String} The comparison value
10686      */
10687     asUCString : function(s) {
10688         return String(s).toUpperCase();
10689     },
10690     
10691     /**
10692      * Date sorting
10693      * @param {Mixed} s The value being converted
10694      * @return {Number} The comparison value
10695      */
10696     asDate : function(s) {
10697         if(!s){
10698             return 0;
10699         }
10700         if(s instanceof Date){
10701             return s.getTime();
10702         }
10703         return Date.parse(String(s));
10704     },
10705     
10706     /**
10707      * Float sorting
10708      * @param {Mixed} s The value being converted
10709      * @return {Float} The comparison value
10710      */
10711     asFloat : function(s) {
10712         var val = parseFloat(String(s).replace(/,/g, ""));
10713         if(isNaN(val)) {
10714             val = 0;
10715         }
10716         return val;
10717     },
10718     
10719     /**
10720      * Integer sorting
10721      * @param {Mixed} s The value being converted
10722      * @return {Number} The comparison value
10723      */
10724     asInt : function(s) {
10725         var val = parseInt(String(s).replace(/,/g, ""));
10726         if(isNaN(val)) {
10727             val = 0;
10728         }
10729         return val;
10730     }
10731 };/*
10732  * Based on:
10733  * Ext JS Library 1.1.1
10734  * Copyright(c) 2006-2007, Ext JS, LLC.
10735  *
10736  * Originally Released Under LGPL - original licence link has changed is not relivant.
10737  *
10738  * Fork - LGPL
10739  * <script type="text/javascript">
10740  */
10741
10742 /**
10743 * @class Roo.data.Record
10744  * Instances of this class encapsulate both record <em>definition</em> information, and record
10745  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10746  * to access Records cached in an {@link Roo.data.Store} object.<br>
10747  * <p>
10748  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10749  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10750  * objects.<br>
10751  * <p>
10752  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10753  * @constructor
10754  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10755  * {@link #create}. The parameters are the same.
10756  * @param {Array} data An associative Array of data values keyed by the field name.
10757  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10758  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10759  * not specified an integer id is generated.
10760  */
10761 Roo.data.Record = function(data, id){
10762     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10763     this.data = data;
10764 };
10765
10766 /**
10767  * Generate a constructor for a specific record layout.
10768  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10769  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10770  * Each field definition object may contain the following properties: <ul>
10771  * <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,
10772  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10773  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10774  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10775  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10776  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10777  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10778  * this may be omitted.</p></li>
10779  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10780  * <ul><li>auto (Default, implies no conversion)</li>
10781  * <li>string</li>
10782  * <li>int</li>
10783  * <li>float</li>
10784  * <li>boolean</li>
10785  * <li>date</li></ul></p></li>
10786  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10787  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10788  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10789  * by the Reader into an object that will be stored in the Record. It is passed the
10790  * following parameters:<ul>
10791  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10792  * </ul></p></li>
10793  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10794  * </ul>
10795  * <br>usage:<br><pre><code>
10796 var TopicRecord = Roo.data.Record.create(
10797     {name: 'title', mapping: 'topic_title'},
10798     {name: 'author', mapping: 'username'},
10799     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10800     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10801     {name: 'lastPoster', mapping: 'user2'},
10802     {name: 'excerpt', mapping: 'post_text'}
10803 );
10804
10805 var myNewRecord = new TopicRecord({
10806     title: 'Do my job please',
10807     author: 'noobie',
10808     totalPosts: 1,
10809     lastPost: new Date(),
10810     lastPoster: 'Animal',
10811     excerpt: 'No way dude!'
10812 });
10813 myStore.add(myNewRecord);
10814 </code></pre>
10815  * @method create
10816  * @static
10817  */
10818 Roo.data.Record.create = function(o){
10819     var f = function(){
10820         f.superclass.constructor.apply(this, arguments);
10821     };
10822     Roo.extend(f, Roo.data.Record);
10823     var p = f.prototype;
10824     p.fields = new Roo.util.MixedCollection(false, function(field){
10825         return field.name;
10826     });
10827     for(var i = 0, len = o.length; i < len; i++){
10828         p.fields.add(new Roo.data.Field(o[i]));
10829     }
10830     f.getField = function(name){
10831         return p.fields.get(name);  
10832     };
10833     return f;
10834 };
10835
10836 Roo.data.Record.AUTO_ID = 1000;
10837 Roo.data.Record.EDIT = 'edit';
10838 Roo.data.Record.REJECT = 'reject';
10839 Roo.data.Record.COMMIT = 'commit';
10840
10841 Roo.data.Record.prototype = {
10842     /**
10843      * Readonly flag - true if this record has been modified.
10844      * @type Boolean
10845      */
10846     dirty : false,
10847     editing : false,
10848     error: null,
10849     modified: null,
10850
10851     // private
10852     join : function(store){
10853         this.store = store;
10854     },
10855
10856     /**
10857      * Set the named field to the specified value.
10858      * @param {String} name The name of the field to set.
10859      * @param {Object} value The value to set the field to.
10860      */
10861     set : function(name, value){
10862         if(this.data[name] == value){
10863             return;
10864         }
10865         this.dirty = true;
10866         if(!this.modified){
10867             this.modified = {};
10868         }
10869         if(typeof this.modified[name] == 'undefined'){
10870             this.modified[name] = this.data[name];
10871         }
10872         this.data[name] = value;
10873         if(!this.editing && this.store){
10874             this.store.afterEdit(this);
10875         }       
10876     },
10877
10878     /**
10879      * Get the value of the named field.
10880      * @param {String} name The name of the field to get the value of.
10881      * @return {Object} The value of the field.
10882      */
10883     get : function(name){
10884         return this.data[name]; 
10885     },
10886
10887     // private
10888     beginEdit : function(){
10889         this.editing = true;
10890         this.modified = {}; 
10891     },
10892
10893     // private
10894     cancelEdit : function(){
10895         this.editing = false;
10896         delete this.modified;
10897     },
10898
10899     // private
10900     endEdit : function(){
10901         this.editing = false;
10902         if(this.dirty && this.store){
10903             this.store.afterEdit(this);
10904         }
10905     },
10906
10907     /**
10908      * Usually called by the {@link Roo.data.Store} which owns the Record.
10909      * Rejects all changes made to the Record since either creation, or the last commit operation.
10910      * Modified fields are reverted to their original values.
10911      * <p>
10912      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10913      * of reject operations.
10914      */
10915     reject : function(){
10916         var m = this.modified;
10917         for(var n in m){
10918             if(typeof m[n] != "function"){
10919                 this.data[n] = m[n];
10920             }
10921         }
10922         this.dirty = false;
10923         delete this.modified;
10924         this.editing = false;
10925         if(this.store){
10926             this.store.afterReject(this);
10927         }
10928     },
10929
10930     /**
10931      * Usually called by the {@link Roo.data.Store} which owns the Record.
10932      * Commits all changes made to the Record since either creation, or the last commit operation.
10933      * <p>
10934      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10935      * of commit operations.
10936      */
10937     commit : function(){
10938         this.dirty = false;
10939         delete this.modified;
10940         this.editing = false;
10941         if(this.store){
10942             this.store.afterCommit(this);
10943         }
10944     },
10945
10946     // private
10947     hasError : function(){
10948         return this.error != null;
10949     },
10950
10951     // private
10952     clearError : function(){
10953         this.error = null;
10954     },
10955
10956     /**
10957      * Creates a copy of this record.
10958      * @param {String} id (optional) A new record id if you don't want to use this record's id
10959      * @return {Record}
10960      */
10961     copy : function(newId) {
10962         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10963     }
10964 };/*
10965  * Based on:
10966  * Ext JS Library 1.1.1
10967  * Copyright(c) 2006-2007, Ext JS, LLC.
10968  *
10969  * Originally Released Under LGPL - original licence link has changed is not relivant.
10970  *
10971  * Fork - LGPL
10972  * <script type="text/javascript">
10973  */
10974
10975
10976
10977 /**
10978  * @class Roo.data.Store
10979  * @extends Roo.util.Observable
10980  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10981  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10982  * <p>
10983  * 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
10984  * has no knowledge of the format of the data returned by the Proxy.<br>
10985  * <p>
10986  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10987  * instances from the data object. These records are cached and made available through accessor functions.
10988  * @constructor
10989  * Creates a new Store.
10990  * @param {Object} config A config object containing the objects needed for the Store to access data,
10991  * and read the data into Records.
10992  */
10993 Roo.data.Store = function(config){
10994     this.data = new Roo.util.MixedCollection(false);
10995     this.data.getKey = function(o){
10996         return o.id;
10997     };
10998     this.baseParams = {};
10999     // private
11000     this.paramNames = {
11001         "start" : "start",
11002         "limit" : "limit",
11003         "sort" : "sort",
11004         "dir" : "dir",
11005         "multisort" : "_multisort"
11006     };
11007
11008     if(config && config.data){
11009         this.inlineData = config.data;
11010         delete config.data;
11011     }
11012
11013     Roo.apply(this, config);
11014     
11015     if(this.reader){ // reader passed
11016         this.reader = Roo.factory(this.reader, Roo.data);
11017         this.reader.xmodule = this.xmodule || false;
11018         if(!this.recordType){
11019             this.recordType = this.reader.recordType;
11020         }
11021         if(this.reader.onMetaChange){
11022             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11023         }
11024     }
11025
11026     if(this.recordType){
11027         this.fields = this.recordType.prototype.fields;
11028     }
11029     this.modified = [];
11030
11031     this.addEvents({
11032         /**
11033          * @event datachanged
11034          * Fires when the data cache has changed, and a widget which is using this Store
11035          * as a Record cache should refresh its view.
11036          * @param {Store} this
11037          */
11038         datachanged : true,
11039         /**
11040          * @event metachange
11041          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11042          * @param {Store} this
11043          * @param {Object} meta The JSON metadata
11044          */
11045         metachange : true,
11046         /**
11047          * @event add
11048          * Fires when Records have been added to the Store
11049          * @param {Store} this
11050          * @param {Roo.data.Record[]} records The array of Records added
11051          * @param {Number} index The index at which the record(s) were added
11052          */
11053         add : true,
11054         /**
11055          * @event remove
11056          * Fires when a Record has been removed from the Store
11057          * @param {Store} this
11058          * @param {Roo.data.Record} record The Record that was removed
11059          * @param {Number} index The index at which the record was removed
11060          */
11061         remove : true,
11062         /**
11063          * @event update
11064          * Fires when a Record has been updated
11065          * @param {Store} this
11066          * @param {Roo.data.Record} record The Record that was updated
11067          * @param {String} operation The update operation being performed.  Value may be one of:
11068          * <pre><code>
11069  Roo.data.Record.EDIT
11070  Roo.data.Record.REJECT
11071  Roo.data.Record.COMMIT
11072          * </code></pre>
11073          */
11074         update : true,
11075         /**
11076          * @event clear
11077          * Fires when the data cache has been cleared.
11078          * @param {Store} this
11079          */
11080         clear : true,
11081         /**
11082          * @event beforeload
11083          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11084          * the load action will be canceled.
11085          * @param {Store} this
11086          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11087          */
11088         beforeload : true,
11089         /**
11090          * @event beforeloadadd
11091          * Fires after a new set of Records has been loaded.
11092          * @param {Store} this
11093          * @param {Roo.data.Record[]} records The Records that were loaded
11094          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11095          */
11096         beforeloadadd : true,
11097         /**
11098          * @event load
11099          * Fires after a new set of Records has been loaded, before they are added to the store.
11100          * @param {Store} this
11101          * @param {Roo.data.Record[]} records The Records that were loaded
11102          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11103          * @params {Object} return from reader
11104          */
11105         load : true,
11106         /**
11107          * @event loadexception
11108          * Fires if an exception occurs in the Proxy during loading.
11109          * Called with the signature of the Proxy's "loadexception" event.
11110          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11111          * 
11112          * @param {Proxy} 
11113          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11114          * @param {Object} load options 
11115          * @param {Object} jsonData from your request (normally this contains the Exception)
11116          */
11117         loadexception : true
11118     });
11119     
11120     if(this.proxy){
11121         this.proxy = Roo.factory(this.proxy, Roo.data);
11122         this.proxy.xmodule = this.xmodule || false;
11123         this.relayEvents(this.proxy,  ["loadexception"]);
11124     }
11125     this.sortToggle = {};
11126     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11127
11128     Roo.data.Store.superclass.constructor.call(this);
11129
11130     if(this.inlineData){
11131         this.loadData(this.inlineData);
11132         delete this.inlineData;
11133     }
11134 };
11135
11136 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11137      /**
11138     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11139     * without a remote query - used by combo/forms at present.
11140     */
11141     
11142     /**
11143     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11144     */
11145     /**
11146     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11147     */
11148     /**
11149     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11150     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11151     */
11152     /**
11153     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11154     * on any HTTP request
11155     */
11156     /**
11157     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11158     */
11159     /**
11160     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11161     */
11162     multiSort: false,
11163     /**
11164     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11165     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11166     */
11167     remoteSort : false,
11168
11169     /**
11170     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11171      * loaded or when a record is removed. (defaults to false).
11172     */
11173     pruneModifiedRecords : false,
11174
11175     // private
11176     lastOptions : null,
11177
11178     /**
11179      * Add Records to the Store and fires the add event.
11180      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11181      */
11182     add : function(records){
11183         records = [].concat(records);
11184         for(var i = 0, len = records.length; i < len; i++){
11185             records[i].join(this);
11186         }
11187         var index = this.data.length;
11188         this.data.addAll(records);
11189         this.fireEvent("add", this, records, index);
11190     },
11191
11192     /**
11193      * Remove a Record from the Store and fires the remove event.
11194      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11195      */
11196     remove : function(record){
11197         var index = this.data.indexOf(record);
11198         this.data.removeAt(index);
11199  
11200         if(this.pruneModifiedRecords){
11201             this.modified.remove(record);
11202         }
11203         this.fireEvent("remove", this, record, index);
11204     },
11205
11206     /**
11207      * Remove all Records from the Store and fires the clear event.
11208      */
11209     removeAll : function(){
11210         this.data.clear();
11211         if(this.pruneModifiedRecords){
11212             this.modified = [];
11213         }
11214         this.fireEvent("clear", this);
11215     },
11216
11217     /**
11218      * Inserts Records to the Store at the given index and fires the add event.
11219      * @param {Number} index The start index at which to insert the passed Records.
11220      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11221      */
11222     insert : function(index, records){
11223         records = [].concat(records);
11224         for(var i = 0, len = records.length; i < len; i++){
11225             this.data.insert(index, records[i]);
11226             records[i].join(this);
11227         }
11228         this.fireEvent("add", this, records, index);
11229     },
11230
11231     /**
11232      * Get the index within the cache of the passed Record.
11233      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11234      * @return {Number} The index of the passed Record. Returns -1 if not found.
11235      */
11236     indexOf : function(record){
11237         return this.data.indexOf(record);
11238     },
11239
11240     /**
11241      * Get the index within the cache of the Record with the passed id.
11242      * @param {String} id The id of the Record to find.
11243      * @return {Number} The index of the Record. Returns -1 if not found.
11244      */
11245     indexOfId : function(id){
11246         return this.data.indexOfKey(id);
11247     },
11248
11249     /**
11250      * Get the Record with the specified id.
11251      * @param {String} id The id of the Record to find.
11252      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11253      */
11254     getById : function(id){
11255         return this.data.key(id);
11256     },
11257
11258     /**
11259      * Get the Record at the specified index.
11260      * @param {Number} index The index of the Record to find.
11261      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11262      */
11263     getAt : function(index){
11264         return this.data.itemAt(index);
11265     },
11266
11267     /**
11268      * Returns a range of Records between specified indices.
11269      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11270      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11271      * @return {Roo.data.Record[]} An array of Records
11272      */
11273     getRange : function(start, end){
11274         return this.data.getRange(start, end);
11275     },
11276
11277     // private
11278     storeOptions : function(o){
11279         o = Roo.apply({}, o);
11280         delete o.callback;
11281         delete o.scope;
11282         this.lastOptions = o;
11283     },
11284
11285     /**
11286      * Loads the Record cache from the configured Proxy using the configured Reader.
11287      * <p>
11288      * If using remote paging, then the first load call must specify the <em>start</em>
11289      * and <em>limit</em> properties in the options.params property to establish the initial
11290      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11291      * <p>
11292      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11293      * and this call will return before the new data has been loaded. Perform any post-processing
11294      * in a callback function, or in a "load" event handler.</strong>
11295      * <p>
11296      * @param {Object} options An object containing properties which control loading options:<ul>
11297      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11298      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11299      * passed the following arguments:<ul>
11300      * <li>r : Roo.data.Record[]</li>
11301      * <li>options: Options object from the load call</li>
11302      * <li>success: Boolean success indicator</li></ul></li>
11303      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11304      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11305      * </ul>
11306      */
11307     load : function(options){
11308         options = options || {};
11309         if(this.fireEvent("beforeload", this, options) !== false){
11310             this.storeOptions(options);
11311             var p = Roo.apply(options.params || {}, this.baseParams);
11312             // if meta was not loaded from remote source.. try requesting it.
11313             if (!this.reader.metaFromRemote) {
11314                 p._requestMeta = 1;
11315             }
11316             if(this.sortInfo && this.remoteSort){
11317                 var pn = this.paramNames;
11318                 p[pn["sort"]] = this.sortInfo.field;
11319                 p[pn["dir"]] = this.sortInfo.direction;
11320             }
11321             if (this.multiSort) {
11322                 var pn = this.paramNames;
11323                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11324             }
11325             
11326             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11327         }
11328     },
11329
11330     /**
11331      * Reloads the Record cache from the configured Proxy using the configured Reader and
11332      * the options from the last load operation performed.
11333      * @param {Object} options (optional) An object containing properties which may override the options
11334      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11335      * the most recently used options are reused).
11336      */
11337     reload : function(options){
11338         this.load(Roo.applyIf(options||{}, this.lastOptions));
11339     },
11340
11341     // private
11342     // Called as a callback by the Reader during a load operation.
11343     loadRecords : function(o, options, success){
11344         if(!o || success === false){
11345             if(success !== false){
11346                 this.fireEvent("load", this, [], options, o);
11347             }
11348             if(options.callback){
11349                 options.callback.call(options.scope || this, [], options, false);
11350             }
11351             return;
11352         }
11353         // if data returned failure - throw an exception.
11354         if (o.success === false) {
11355             // show a message if no listener is registered.
11356             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11357                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11358             }
11359             // loadmask wil be hooked into this..
11360             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11361             return;
11362         }
11363         var r = o.records, t = o.totalRecords || r.length;
11364         
11365         this.fireEvent("beforeloadadd", this, r, options, o);
11366         
11367         if(!options || options.add !== true){
11368             if(this.pruneModifiedRecords){
11369                 this.modified = [];
11370             }
11371             for(var i = 0, len = r.length; i < len; i++){
11372                 r[i].join(this);
11373             }
11374             if(this.snapshot){
11375                 this.data = this.snapshot;
11376                 delete this.snapshot;
11377             }
11378             this.data.clear();
11379             this.data.addAll(r);
11380             this.totalLength = t;
11381             this.applySort();
11382             this.fireEvent("datachanged", this);
11383         }else{
11384             this.totalLength = Math.max(t, this.data.length+r.length);
11385             this.add(r);
11386         }
11387         
11388         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11389                 
11390             var e = new Roo.data.Record({});
11391
11392             e.set(this.parent.displayField, this.parent.emptyTitle);
11393             e.set(this.parent.valueField, '');
11394
11395             this.insert(0, e);
11396         }
11397             
11398         this.fireEvent("load", this, r, options, o);
11399         if(options.callback){
11400             options.callback.call(options.scope || this, r, options, true);
11401         }
11402     },
11403
11404
11405     /**
11406      * Loads data from a passed data block. A Reader which understands the format of the data
11407      * must have been configured in the constructor.
11408      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11409      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11410      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11411      */
11412     loadData : function(o, append){
11413         var r = this.reader.readRecords(o);
11414         this.loadRecords(r, {add: append}, true);
11415     },
11416
11417     /**
11418      * Gets the number of cached records.
11419      * <p>
11420      * <em>If using paging, this may not be the total size of the dataset. If the data object
11421      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11422      * the data set size</em>
11423      */
11424     getCount : function(){
11425         return this.data.length || 0;
11426     },
11427
11428     /**
11429      * Gets the total number of records in the dataset as returned by the server.
11430      * <p>
11431      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11432      * the dataset size</em>
11433      */
11434     getTotalCount : function(){
11435         return this.totalLength || 0;
11436     },
11437
11438     /**
11439      * Returns the sort state of the Store as an object with two properties:
11440      * <pre><code>
11441  field {String} The name of the field by which the Records are sorted
11442  direction {String} The sort order, "ASC" or "DESC"
11443      * </code></pre>
11444      */
11445     getSortState : function(){
11446         return this.sortInfo;
11447     },
11448
11449     // private
11450     applySort : function(){
11451         if(this.sortInfo && !this.remoteSort){
11452             var s = this.sortInfo, f = s.field;
11453             var st = this.fields.get(f).sortType;
11454             var fn = function(r1, r2){
11455                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11456                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11457             };
11458             this.data.sort(s.direction, fn);
11459             if(this.snapshot && this.snapshot != this.data){
11460                 this.snapshot.sort(s.direction, fn);
11461             }
11462         }
11463     },
11464
11465     /**
11466      * Sets the default sort column and order to be used by the next load operation.
11467      * @param {String} fieldName The name of the field to sort by.
11468      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11469      */
11470     setDefaultSort : function(field, dir){
11471         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11472     },
11473
11474     /**
11475      * Sort the Records.
11476      * If remote sorting is used, the sort is performed on the server, and the cache is
11477      * reloaded. If local sorting is used, the cache is sorted internally.
11478      * @param {String} fieldName The name of the field to sort by.
11479      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11480      */
11481     sort : function(fieldName, dir){
11482         var f = this.fields.get(fieldName);
11483         if(!dir){
11484             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11485             
11486             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11487                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11488             }else{
11489                 dir = f.sortDir;
11490             }
11491         }
11492         this.sortToggle[f.name] = dir;
11493         this.sortInfo = {field: f.name, direction: dir};
11494         if(!this.remoteSort){
11495             this.applySort();
11496             this.fireEvent("datachanged", this);
11497         }else{
11498             this.load(this.lastOptions);
11499         }
11500     },
11501
11502     /**
11503      * Calls the specified function for each of the Records in the cache.
11504      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11505      * Returning <em>false</em> aborts and exits the iteration.
11506      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11507      */
11508     each : function(fn, scope){
11509         this.data.each(fn, scope);
11510     },
11511
11512     /**
11513      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11514      * (e.g., during paging).
11515      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11516      */
11517     getModifiedRecords : function(){
11518         return this.modified;
11519     },
11520
11521     // private
11522     createFilterFn : function(property, value, anyMatch){
11523         if(!value.exec){ // not a regex
11524             value = String(value);
11525             if(value.length == 0){
11526                 return false;
11527             }
11528             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11529         }
11530         return function(r){
11531             return value.test(r.data[property]);
11532         };
11533     },
11534
11535     /**
11536      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11537      * @param {String} property A field on your records
11538      * @param {Number} start The record index to start at (defaults to 0)
11539      * @param {Number} end The last record index to include (defaults to length - 1)
11540      * @return {Number} The sum
11541      */
11542     sum : function(property, start, end){
11543         var rs = this.data.items, v = 0;
11544         start = start || 0;
11545         end = (end || end === 0) ? end : rs.length-1;
11546
11547         for(var i = start; i <= end; i++){
11548             v += (rs[i].data[property] || 0);
11549         }
11550         return v;
11551     },
11552
11553     /**
11554      * Filter the records by a specified property.
11555      * @param {String} field A field on your records
11556      * @param {String/RegExp} value Either a string that the field
11557      * should start with or a RegExp to test against the field
11558      * @param {Boolean} anyMatch True to match any part not just the beginning
11559      */
11560     filter : function(property, value, anyMatch){
11561         var fn = this.createFilterFn(property, value, anyMatch);
11562         return fn ? this.filterBy(fn) : this.clearFilter();
11563     },
11564
11565     /**
11566      * Filter by a function. The specified function will be called with each
11567      * record in this data source. If the function returns true the record is included,
11568      * otherwise it is filtered.
11569      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11570      * @param {Object} scope (optional) The scope of the function (defaults to this)
11571      */
11572     filterBy : function(fn, scope){
11573         this.snapshot = this.snapshot || this.data;
11574         this.data = this.queryBy(fn, scope||this);
11575         this.fireEvent("datachanged", this);
11576     },
11577
11578     /**
11579      * Query the records by a specified property.
11580      * @param {String} field A field on your records
11581      * @param {String/RegExp} value Either a string that the field
11582      * should start with or a RegExp to test against the field
11583      * @param {Boolean} anyMatch True to match any part not just the beginning
11584      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11585      */
11586     query : function(property, value, anyMatch){
11587         var fn = this.createFilterFn(property, value, anyMatch);
11588         return fn ? this.queryBy(fn) : this.data.clone();
11589     },
11590
11591     /**
11592      * Query by a function. The specified function will be called with each
11593      * record in this data source. If the function returns true the record is included
11594      * in the results.
11595      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11596      * @param {Object} scope (optional) The scope of the function (defaults to this)
11597       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11598      **/
11599     queryBy : function(fn, scope){
11600         var data = this.snapshot || this.data;
11601         return data.filterBy(fn, scope||this);
11602     },
11603
11604     /**
11605      * Collects unique values for a particular dataIndex from this store.
11606      * @param {String} dataIndex The property to collect
11607      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11608      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11609      * @return {Array} An array of the unique values
11610      **/
11611     collect : function(dataIndex, allowNull, bypassFilter){
11612         var d = (bypassFilter === true && this.snapshot) ?
11613                 this.snapshot.items : this.data.items;
11614         var v, sv, r = [], l = {};
11615         for(var i = 0, len = d.length; i < len; i++){
11616             v = d[i].data[dataIndex];
11617             sv = String(v);
11618             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11619                 l[sv] = true;
11620                 r[r.length] = v;
11621             }
11622         }
11623         return r;
11624     },
11625
11626     /**
11627      * Revert to a view of the Record cache with no filtering applied.
11628      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11629      */
11630     clearFilter : function(suppressEvent){
11631         if(this.snapshot && this.snapshot != this.data){
11632             this.data = this.snapshot;
11633             delete this.snapshot;
11634             if(suppressEvent !== true){
11635                 this.fireEvent("datachanged", this);
11636             }
11637         }
11638     },
11639
11640     // private
11641     afterEdit : function(record){
11642         if(this.modified.indexOf(record) == -1){
11643             this.modified.push(record);
11644         }
11645         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11646     },
11647     
11648     // private
11649     afterReject : function(record){
11650         this.modified.remove(record);
11651         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11652     },
11653
11654     // private
11655     afterCommit : function(record){
11656         this.modified.remove(record);
11657         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11658     },
11659
11660     /**
11661      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11662      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11663      */
11664     commitChanges : function(){
11665         var m = this.modified.slice(0);
11666         this.modified = [];
11667         for(var i = 0, len = m.length; i < len; i++){
11668             m[i].commit();
11669         }
11670     },
11671
11672     /**
11673      * Cancel outstanding changes on all changed records.
11674      */
11675     rejectChanges : function(){
11676         var m = this.modified.slice(0);
11677         this.modified = [];
11678         for(var i = 0, len = m.length; i < len; i++){
11679             m[i].reject();
11680         }
11681     },
11682
11683     onMetaChange : function(meta, rtype, o){
11684         this.recordType = rtype;
11685         this.fields = rtype.prototype.fields;
11686         delete this.snapshot;
11687         this.sortInfo = meta.sortInfo || this.sortInfo;
11688         this.modified = [];
11689         this.fireEvent('metachange', this, this.reader.meta);
11690     },
11691     
11692     moveIndex : function(data, type)
11693     {
11694         var index = this.indexOf(data);
11695         
11696         var newIndex = index + type;
11697         
11698         this.remove(data);
11699         
11700         this.insert(newIndex, data);
11701         
11702     }
11703 });/*
11704  * Based on:
11705  * Ext JS Library 1.1.1
11706  * Copyright(c) 2006-2007, Ext JS, LLC.
11707  *
11708  * Originally Released Under LGPL - original licence link has changed is not relivant.
11709  *
11710  * Fork - LGPL
11711  * <script type="text/javascript">
11712  */
11713
11714 /**
11715  * @class Roo.data.SimpleStore
11716  * @extends Roo.data.Store
11717  * Small helper class to make creating Stores from Array data easier.
11718  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11719  * @cfg {Array} fields An array of field definition objects, or field name strings.
11720  * @cfg {Array} data The multi-dimensional array of data
11721  * @constructor
11722  * @param {Object} config
11723  */
11724 Roo.data.SimpleStore = function(config){
11725     Roo.data.SimpleStore.superclass.constructor.call(this, {
11726         isLocal : true,
11727         reader: new Roo.data.ArrayReader({
11728                 id: config.id
11729             },
11730             Roo.data.Record.create(config.fields)
11731         ),
11732         proxy : new Roo.data.MemoryProxy(config.data)
11733     });
11734     this.load();
11735 };
11736 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11737  * Based on:
11738  * Ext JS Library 1.1.1
11739  * Copyright(c) 2006-2007, Ext JS, LLC.
11740  *
11741  * Originally Released Under LGPL - original licence link has changed is not relivant.
11742  *
11743  * Fork - LGPL
11744  * <script type="text/javascript">
11745  */
11746
11747 /**
11748 /**
11749  * @extends Roo.data.Store
11750  * @class Roo.data.JsonStore
11751  * Small helper class to make creating Stores for JSON data easier. <br/>
11752 <pre><code>
11753 var store = new Roo.data.JsonStore({
11754     url: 'get-images.php',
11755     root: 'images',
11756     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11757 });
11758 </code></pre>
11759  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11760  * JsonReader and HttpProxy (unless inline data is provided).</b>
11761  * @cfg {Array} fields An array of field definition objects, or field name strings.
11762  * @constructor
11763  * @param {Object} config
11764  */
11765 Roo.data.JsonStore = function(c){
11766     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11767         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11768         reader: new Roo.data.JsonReader(c, c.fields)
11769     }));
11770 };
11771 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11772  * Based on:
11773  * Ext JS Library 1.1.1
11774  * Copyright(c) 2006-2007, Ext JS, LLC.
11775  *
11776  * Originally Released Under LGPL - original licence link has changed is not relivant.
11777  *
11778  * Fork - LGPL
11779  * <script type="text/javascript">
11780  */
11781
11782  
11783 Roo.data.Field = function(config){
11784     if(typeof config == "string"){
11785         config = {name: config};
11786     }
11787     Roo.apply(this, config);
11788     
11789     if(!this.type){
11790         this.type = "auto";
11791     }
11792     
11793     var st = Roo.data.SortTypes;
11794     // named sortTypes are supported, here we look them up
11795     if(typeof this.sortType == "string"){
11796         this.sortType = st[this.sortType];
11797     }
11798     
11799     // set default sortType for strings and dates
11800     if(!this.sortType){
11801         switch(this.type){
11802             case "string":
11803                 this.sortType = st.asUCString;
11804                 break;
11805             case "date":
11806                 this.sortType = st.asDate;
11807                 break;
11808             default:
11809                 this.sortType = st.none;
11810         }
11811     }
11812
11813     // define once
11814     var stripRe = /[\$,%]/g;
11815
11816     // prebuilt conversion function for this field, instead of
11817     // switching every time we're reading a value
11818     if(!this.convert){
11819         var cv, dateFormat = this.dateFormat;
11820         switch(this.type){
11821             case "":
11822             case "auto":
11823             case undefined:
11824                 cv = function(v){ return v; };
11825                 break;
11826             case "string":
11827                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11828                 break;
11829             case "int":
11830                 cv = function(v){
11831                     return v !== undefined && v !== null && v !== '' ?
11832                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11833                     };
11834                 break;
11835             case "float":
11836                 cv = function(v){
11837                     return v !== undefined && v !== null && v !== '' ?
11838                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11839                     };
11840                 break;
11841             case "bool":
11842             case "boolean":
11843                 cv = function(v){ return v === true || v === "true" || v == 1; };
11844                 break;
11845             case "date":
11846                 cv = function(v){
11847                     if(!v){
11848                         return '';
11849                     }
11850                     if(v instanceof Date){
11851                         return v;
11852                     }
11853                     if(dateFormat){
11854                         if(dateFormat == "timestamp"){
11855                             return new Date(v*1000);
11856                         }
11857                         return Date.parseDate(v, dateFormat);
11858                     }
11859                     var parsed = Date.parse(v);
11860                     return parsed ? new Date(parsed) : null;
11861                 };
11862              break;
11863             
11864         }
11865         this.convert = cv;
11866     }
11867 };
11868
11869 Roo.data.Field.prototype = {
11870     dateFormat: null,
11871     defaultValue: "",
11872     mapping: null,
11873     sortType : null,
11874     sortDir : "ASC"
11875 };/*
11876  * Based on:
11877  * Ext JS Library 1.1.1
11878  * Copyright(c) 2006-2007, Ext JS, LLC.
11879  *
11880  * Originally Released Under LGPL - original licence link has changed is not relivant.
11881  *
11882  * Fork - LGPL
11883  * <script type="text/javascript">
11884  */
11885  
11886 // Base class for reading structured data from a data source.  This class is intended to be
11887 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11888
11889 /**
11890  * @class Roo.data.DataReader
11891  * Base class for reading structured data from a data source.  This class is intended to be
11892  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11893  */
11894
11895 Roo.data.DataReader = function(meta, recordType){
11896     
11897     this.meta = meta;
11898     
11899     this.recordType = recordType instanceof Array ? 
11900         Roo.data.Record.create(recordType) : recordType;
11901 };
11902
11903 Roo.data.DataReader.prototype = {
11904      /**
11905      * Create an empty record
11906      * @param {Object} data (optional) - overlay some values
11907      * @return {Roo.data.Record} record created.
11908      */
11909     newRow :  function(d) {
11910         var da =  {};
11911         this.recordType.prototype.fields.each(function(c) {
11912             switch( c.type) {
11913                 case 'int' : da[c.name] = 0; break;
11914                 case 'date' : da[c.name] = new Date(); break;
11915                 case 'float' : da[c.name] = 0.0; break;
11916                 case 'boolean' : da[c.name] = false; break;
11917                 default : da[c.name] = ""; break;
11918             }
11919             
11920         });
11921         return new this.recordType(Roo.apply(da, d));
11922     }
11923     
11924 };/*
11925  * Based on:
11926  * Ext JS Library 1.1.1
11927  * Copyright(c) 2006-2007, Ext JS, LLC.
11928  *
11929  * Originally Released Under LGPL - original licence link has changed is not relivant.
11930  *
11931  * Fork - LGPL
11932  * <script type="text/javascript">
11933  */
11934
11935 /**
11936  * @class Roo.data.DataProxy
11937  * @extends Roo.data.Observable
11938  * This class is an abstract base class for implementations which provide retrieval of
11939  * unformatted data objects.<br>
11940  * <p>
11941  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11942  * (of the appropriate type which knows how to parse the data object) to provide a block of
11943  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11944  * <p>
11945  * Custom implementations must implement the load method as described in
11946  * {@link Roo.data.HttpProxy#load}.
11947  */
11948 Roo.data.DataProxy = function(){
11949     this.addEvents({
11950         /**
11951          * @event beforeload
11952          * Fires before a network request is made to retrieve a data object.
11953          * @param {Object} This DataProxy object.
11954          * @param {Object} params The params parameter to the load function.
11955          */
11956         beforeload : true,
11957         /**
11958          * @event load
11959          * Fires before the load method's callback is called.
11960          * @param {Object} This DataProxy object.
11961          * @param {Object} o The data object.
11962          * @param {Object} arg The callback argument object passed to the load function.
11963          */
11964         load : true,
11965         /**
11966          * @event loadexception
11967          * Fires if an Exception occurs during data retrieval.
11968          * @param {Object} This DataProxy object.
11969          * @param {Object} o The data object.
11970          * @param {Object} arg The callback argument object passed to the load function.
11971          * @param {Object} e The Exception.
11972          */
11973         loadexception : true
11974     });
11975     Roo.data.DataProxy.superclass.constructor.call(this);
11976 };
11977
11978 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11979
11980     /**
11981      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11982      */
11983 /*
11984  * Based on:
11985  * Ext JS Library 1.1.1
11986  * Copyright(c) 2006-2007, Ext JS, LLC.
11987  *
11988  * Originally Released Under LGPL - original licence link has changed is not relivant.
11989  *
11990  * Fork - LGPL
11991  * <script type="text/javascript">
11992  */
11993 /**
11994  * @class Roo.data.MemoryProxy
11995  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11996  * to the Reader when its load method is called.
11997  * @constructor
11998  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11999  */
12000 Roo.data.MemoryProxy = function(data){
12001     if (data.data) {
12002         data = data.data;
12003     }
12004     Roo.data.MemoryProxy.superclass.constructor.call(this);
12005     this.data = data;
12006 };
12007
12008 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12009     
12010     /**
12011      * Load data from the requested source (in this case an in-memory
12012      * data object passed to the constructor), read the data object into
12013      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12014      * process that block using the passed callback.
12015      * @param {Object} params This parameter is not used by the MemoryProxy class.
12016      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12017      * object into a block of Roo.data.Records.
12018      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12019      * The function must be passed <ul>
12020      * <li>The Record block object</li>
12021      * <li>The "arg" argument from the load function</li>
12022      * <li>A boolean success indicator</li>
12023      * </ul>
12024      * @param {Object} scope The scope in which to call the callback
12025      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12026      */
12027     load : function(params, reader, callback, scope, arg){
12028         params = params || {};
12029         var result;
12030         try {
12031             result = reader.readRecords(this.data);
12032         }catch(e){
12033             this.fireEvent("loadexception", this, arg, null, e);
12034             callback.call(scope, null, arg, false);
12035             return;
12036         }
12037         callback.call(scope, result, arg, true);
12038     },
12039     
12040     // private
12041     update : function(params, records){
12042         
12043     }
12044 });/*
12045  * Based on:
12046  * Ext JS Library 1.1.1
12047  * Copyright(c) 2006-2007, Ext JS, LLC.
12048  *
12049  * Originally Released Under LGPL - original licence link has changed is not relivant.
12050  *
12051  * Fork - LGPL
12052  * <script type="text/javascript">
12053  */
12054 /**
12055  * @class Roo.data.HttpProxy
12056  * @extends Roo.data.DataProxy
12057  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12058  * configured to reference a certain URL.<br><br>
12059  * <p>
12060  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12061  * from which the running page was served.<br><br>
12062  * <p>
12063  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12064  * <p>
12065  * Be aware that to enable the browser to parse an XML document, the server must set
12066  * the Content-Type header in the HTTP response to "text/xml".
12067  * @constructor
12068  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12069  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12070  * will be used to make the request.
12071  */
12072 Roo.data.HttpProxy = function(conn){
12073     Roo.data.HttpProxy.superclass.constructor.call(this);
12074     // is conn a conn config or a real conn?
12075     this.conn = conn;
12076     this.useAjax = !conn || !conn.events;
12077   
12078 };
12079
12080 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12081     // thse are take from connection...
12082     
12083     /**
12084      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12085      */
12086     /**
12087      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12088      * extra parameters to each request made by this object. (defaults to undefined)
12089      */
12090     /**
12091      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12092      *  to each request made by this object. (defaults to undefined)
12093      */
12094     /**
12095      * @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)
12096      */
12097     /**
12098      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12099      */
12100      /**
12101      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12102      * @type Boolean
12103      */
12104   
12105
12106     /**
12107      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12108      * @type Boolean
12109      */
12110     /**
12111      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12112      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12113      * a finer-grained basis than the DataProxy events.
12114      */
12115     getConnection : function(){
12116         return this.useAjax ? Roo.Ajax : this.conn;
12117     },
12118
12119     /**
12120      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12121      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12122      * process that block using the passed callback.
12123      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12124      * for the request to the remote server.
12125      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12126      * object into a block of Roo.data.Records.
12127      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12128      * The function must be passed <ul>
12129      * <li>The Record block object</li>
12130      * <li>The "arg" argument from the load function</li>
12131      * <li>A boolean success indicator</li>
12132      * </ul>
12133      * @param {Object} scope The scope in which to call the callback
12134      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12135      */
12136     load : function(params, reader, callback, scope, arg){
12137         if(this.fireEvent("beforeload", this, params) !== false){
12138             var  o = {
12139                 params : params || {},
12140                 request: {
12141                     callback : callback,
12142                     scope : scope,
12143                     arg : arg
12144                 },
12145                 reader: reader,
12146                 callback : this.loadResponse,
12147                 scope: this
12148             };
12149             if(this.useAjax){
12150                 Roo.applyIf(o, this.conn);
12151                 if(this.activeRequest){
12152                     Roo.Ajax.abort(this.activeRequest);
12153                 }
12154                 this.activeRequest = Roo.Ajax.request(o);
12155             }else{
12156                 this.conn.request(o);
12157             }
12158         }else{
12159             callback.call(scope||this, null, arg, false);
12160         }
12161     },
12162
12163     // private
12164     loadResponse : function(o, success, response){
12165         delete this.activeRequest;
12166         if(!success){
12167             this.fireEvent("loadexception", this, o, response);
12168             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12169             return;
12170         }
12171         var result;
12172         try {
12173             result = o.reader.read(response);
12174         }catch(e){
12175             this.fireEvent("loadexception", this, o, response, e);
12176             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12177             return;
12178         }
12179         
12180         this.fireEvent("load", this, o, o.request.arg);
12181         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12182     },
12183
12184     // private
12185     update : function(dataSet){
12186
12187     },
12188
12189     // private
12190     updateResponse : function(dataSet){
12191
12192     }
12193 });/*
12194  * Based on:
12195  * Ext JS Library 1.1.1
12196  * Copyright(c) 2006-2007, Ext JS, LLC.
12197  *
12198  * Originally Released Under LGPL - original licence link has changed is not relivant.
12199  *
12200  * Fork - LGPL
12201  * <script type="text/javascript">
12202  */
12203
12204 /**
12205  * @class Roo.data.ScriptTagProxy
12206  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12207  * other than the originating domain of the running page.<br><br>
12208  * <p>
12209  * <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
12210  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12211  * <p>
12212  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12213  * source code that is used as the source inside a &lt;script> tag.<br><br>
12214  * <p>
12215  * In order for the browser to process the returned data, the server must wrap the data object
12216  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12217  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12218  * depending on whether the callback name was passed:
12219  * <p>
12220  * <pre><code>
12221 boolean scriptTag = false;
12222 String cb = request.getParameter("callback");
12223 if (cb != null) {
12224     scriptTag = true;
12225     response.setContentType("text/javascript");
12226 } else {
12227     response.setContentType("application/x-json");
12228 }
12229 Writer out = response.getWriter();
12230 if (scriptTag) {
12231     out.write(cb + "(");
12232 }
12233 out.print(dataBlock.toJsonString());
12234 if (scriptTag) {
12235     out.write(");");
12236 }
12237 </pre></code>
12238  *
12239  * @constructor
12240  * @param {Object} config A configuration object.
12241  */
12242 Roo.data.ScriptTagProxy = function(config){
12243     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12244     Roo.apply(this, config);
12245     this.head = document.getElementsByTagName("head")[0];
12246 };
12247
12248 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12249
12250 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12251     /**
12252      * @cfg {String} url The URL from which to request the data object.
12253      */
12254     /**
12255      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12256      */
12257     timeout : 30000,
12258     /**
12259      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12260      * the server the name of the callback function set up by the load call to process the returned data object.
12261      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12262      * javascript output which calls this named function passing the data object as its only parameter.
12263      */
12264     callbackParam : "callback",
12265     /**
12266      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12267      * name to the request.
12268      */
12269     nocache : true,
12270
12271     /**
12272      * Load data from the configured URL, read the data object into
12273      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12274      * process that block using the passed callback.
12275      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12276      * for the request to the remote server.
12277      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12278      * object into a block of Roo.data.Records.
12279      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12280      * The function must be passed <ul>
12281      * <li>The Record block object</li>
12282      * <li>The "arg" argument from the load function</li>
12283      * <li>A boolean success indicator</li>
12284      * </ul>
12285      * @param {Object} scope The scope in which to call the callback
12286      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12287      */
12288     load : function(params, reader, callback, scope, arg){
12289         if(this.fireEvent("beforeload", this, params) !== false){
12290
12291             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12292
12293             var url = this.url;
12294             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12295             if(this.nocache){
12296                 url += "&_dc=" + (new Date().getTime());
12297             }
12298             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12299             var trans = {
12300                 id : transId,
12301                 cb : "stcCallback"+transId,
12302                 scriptId : "stcScript"+transId,
12303                 params : params,
12304                 arg : arg,
12305                 url : url,
12306                 callback : callback,
12307                 scope : scope,
12308                 reader : reader
12309             };
12310             var conn = this;
12311
12312             window[trans.cb] = function(o){
12313                 conn.handleResponse(o, trans);
12314             };
12315
12316             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12317
12318             if(this.autoAbort !== false){
12319                 this.abort();
12320             }
12321
12322             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12323
12324             var script = document.createElement("script");
12325             script.setAttribute("src", url);
12326             script.setAttribute("type", "text/javascript");
12327             script.setAttribute("id", trans.scriptId);
12328             this.head.appendChild(script);
12329
12330             this.trans = trans;
12331         }else{
12332             callback.call(scope||this, null, arg, false);
12333         }
12334     },
12335
12336     // private
12337     isLoading : function(){
12338         return this.trans ? true : false;
12339     },
12340
12341     /**
12342      * Abort the current server request.
12343      */
12344     abort : function(){
12345         if(this.isLoading()){
12346             this.destroyTrans(this.trans);
12347         }
12348     },
12349
12350     // private
12351     destroyTrans : function(trans, isLoaded){
12352         this.head.removeChild(document.getElementById(trans.scriptId));
12353         clearTimeout(trans.timeoutId);
12354         if(isLoaded){
12355             window[trans.cb] = undefined;
12356             try{
12357                 delete window[trans.cb];
12358             }catch(e){}
12359         }else{
12360             // if hasn't been loaded, wait for load to remove it to prevent script error
12361             window[trans.cb] = function(){
12362                 window[trans.cb] = undefined;
12363                 try{
12364                     delete window[trans.cb];
12365                 }catch(e){}
12366             };
12367         }
12368     },
12369
12370     // private
12371     handleResponse : function(o, trans){
12372         this.trans = false;
12373         this.destroyTrans(trans, true);
12374         var result;
12375         try {
12376             result = trans.reader.readRecords(o);
12377         }catch(e){
12378             this.fireEvent("loadexception", this, o, trans.arg, e);
12379             trans.callback.call(trans.scope||window, null, trans.arg, false);
12380             return;
12381         }
12382         this.fireEvent("load", this, o, trans.arg);
12383         trans.callback.call(trans.scope||window, result, trans.arg, true);
12384     },
12385
12386     // private
12387     handleFailure : function(trans){
12388         this.trans = false;
12389         this.destroyTrans(trans, false);
12390         this.fireEvent("loadexception", this, null, trans.arg);
12391         trans.callback.call(trans.scope||window, null, trans.arg, false);
12392     }
12393 });/*
12394  * Based on:
12395  * Ext JS Library 1.1.1
12396  * Copyright(c) 2006-2007, Ext JS, LLC.
12397  *
12398  * Originally Released Under LGPL - original licence link has changed is not relivant.
12399  *
12400  * Fork - LGPL
12401  * <script type="text/javascript">
12402  */
12403
12404 /**
12405  * @class Roo.data.JsonReader
12406  * @extends Roo.data.DataReader
12407  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12408  * based on mappings in a provided Roo.data.Record constructor.
12409  * 
12410  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12411  * in the reply previously. 
12412  * 
12413  * <p>
12414  * Example code:
12415  * <pre><code>
12416 var RecordDef = Roo.data.Record.create([
12417     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12418     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12419 ]);
12420 var myReader = new Roo.data.JsonReader({
12421     totalProperty: "results",    // The property which contains the total dataset size (optional)
12422     root: "rows",                // The property which contains an Array of row objects
12423     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12424 }, RecordDef);
12425 </code></pre>
12426  * <p>
12427  * This would consume a JSON file like this:
12428  * <pre><code>
12429 { 'results': 2, 'rows': [
12430     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12431     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12432 }
12433 </code></pre>
12434  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12435  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12436  * paged from the remote server.
12437  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12438  * @cfg {String} root name of the property which contains the Array of row objects.
12439  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12440  * @cfg {Array} fields Array of field definition objects
12441  * @constructor
12442  * Create a new JsonReader
12443  * @param {Object} meta Metadata configuration options
12444  * @param {Object} recordType Either an Array of field definition objects,
12445  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12446  */
12447 Roo.data.JsonReader = function(meta, recordType){
12448     
12449     meta = meta || {};
12450     // set some defaults:
12451     Roo.applyIf(meta, {
12452         totalProperty: 'total',
12453         successProperty : 'success',
12454         root : 'data',
12455         id : 'id'
12456     });
12457     
12458     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12459 };
12460 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12461     
12462     /**
12463      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12464      * Used by Store query builder to append _requestMeta to params.
12465      * 
12466      */
12467     metaFromRemote : false,
12468     /**
12469      * This method is only used by a DataProxy which has retrieved data from a remote server.
12470      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12471      * @return {Object} data A data block which is used by an Roo.data.Store object as
12472      * a cache of Roo.data.Records.
12473      */
12474     read : function(response){
12475         var json = response.responseText;
12476        
12477         var o = /* eval:var:o */ eval("("+json+")");
12478         if(!o) {
12479             throw {message: "JsonReader.read: Json object not found"};
12480         }
12481         
12482         if(o.metaData){
12483             
12484             delete this.ef;
12485             this.metaFromRemote = true;
12486             this.meta = o.metaData;
12487             this.recordType = Roo.data.Record.create(o.metaData.fields);
12488             this.onMetaChange(this.meta, this.recordType, o);
12489         }
12490         return this.readRecords(o);
12491     },
12492
12493     // private function a store will implement
12494     onMetaChange : function(meta, recordType, o){
12495
12496     },
12497
12498     /**
12499          * @ignore
12500          */
12501     simpleAccess: function(obj, subsc) {
12502         return obj[subsc];
12503     },
12504
12505         /**
12506          * @ignore
12507          */
12508     getJsonAccessor: function(){
12509         var re = /[\[\.]/;
12510         return function(expr) {
12511             try {
12512                 return(re.test(expr))
12513                     ? new Function("obj", "return obj." + expr)
12514                     : function(obj){
12515                         return obj[expr];
12516                     };
12517             } catch(e){}
12518             return Roo.emptyFn;
12519         };
12520     }(),
12521
12522     /**
12523      * Create a data block containing Roo.data.Records from an XML document.
12524      * @param {Object} o An object which contains an Array of row objects in the property specified
12525      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12526      * which contains the total size of the dataset.
12527      * @return {Object} data A data block which is used by an Roo.data.Store object as
12528      * a cache of Roo.data.Records.
12529      */
12530     readRecords : function(o){
12531         /**
12532          * After any data loads, the raw JSON data is available for further custom processing.
12533          * @type Object
12534          */
12535         this.o = o;
12536         var s = this.meta, Record = this.recordType,
12537             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12538
12539 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12540         if (!this.ef) {
12541             if(s.totalProperty) {
12542                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12543                 }
12544                 if(s.successProperty) {
12545                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12546                 }
12547                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12548                 if (s.id) {
12549                         var g = this.getJsonAccessor(s.id);
12550                         this.getId = function(rec) {
12551                                 var r = g(rec);  
12552                                 return (r === undefined || r === "") ? null : r;
12553                         };
12554                 } else {
12555                         this.getId = function(){return null;};
12556                 }
12557             this.ef = [];
12558             for(var jj = 0; jj < fl; jj++){
12559                 f = fi[jj];
12560                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12561                 this.ef[jj] = this.getJsonAccessor(map);
12562             }
12563         }
12564
12565         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12566         if(s.totalProperty){
12567             var vt = parseInt(this.getTotal(o), 10);
12568             if(!isNaN(vt)){
12569                 totalRecords = vt;
12570             }
12571         }
12572         if(s.successProperty){
12573             var vs = this.getSuccess(o);
12574             if(vs === false || vs === 'false'){
12575                 success = false;
12576             }
12577         }
12578         var records = [];
12579         for(var i = 0; i < c; i++){
12580                 var n = root[i];
12581             var values = {};
12582             var id = this.getId(n);
12583             for(var j = 0; j < fl; j++){
12584                 f = fi[j];
12585             var v = this.ef[j](n);
12586             if (!f.convert) {
12587                 Roo.log('missing convert for ' + f.name);
12588                 Roo.log(f);
12589                 continue;
12590             }
12591             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12592             }
12593             var record = new Record(values, id);
12594             record.json = n;
12595             records[i] = record;
12596         }
12597         return {
12598             raw : o,
12599             success : success,
12600             records : records,
12601             totalRecords : totalRecords
12602         };
12603     }
12604 });/*
12605  * Based on:
12606  * Ext JS Library 1.1.1
12607  * Copyright(c) 2006-2007, Ext JS, LLC.
12608  *
12609  * Originally Released Under LGPL - original licence link has changed is not relivant.
12610  *
12611  * Fork - LGPL
12612  * <script type="text/javascript">
12613  */
12614
12615 /**
12616  * @class Roo.data.ArrayReader
12617  * @extends Roo.data.DataReader
12618  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12619  * Each element of that Array represents a row of data fields. The
12620  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12621  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12622  * <p>
12623  * Example code:.
12624  * <pre><code>
12625 var RecordDef = Roo.data.Record.create([
12626     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12627     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12628 ]);
12629 var myReader = new Roo.data.ArrayReader({
12630     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12631 }, RecordDef);
12632 </code></pre>
12633  * <p>
12634  * This would consume an Array like this:
12635  * <pre><code>
12636 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12637   </code></pre>
12638  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12639  * @constructor
12640  * Create a new JsonReader
12641  * @param {Object} meta Metadata configuration options.
12642  * @param {Object} recordType Either an Array of field definition objects
12643  * as specified to {@link Roo.data.Record#create},
12644  * or an {@link Roo.data.Record} object
12645  * created using {@link Roo.data.Record#create}.
12646  */
12647 Roo.data.ArrayReader = function(meta, recordType){
12648     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12649 };
12650
12651 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12652     /**
12653      * Create a data block containing Roo.data.Records from an XML document.
12654      * @param {Object} o An Array of row objects which represents the dataset.
12655      * @return {Object} data A data block which is used by an Roo.data.Store object as
12656      * a cache of Roo.data.Records.
12657      */
12658     readRecords : function(o){
12659         var sid = this.meta ? this.meta.id : null;
12660         var recordType = this.recordType, fields = recordType.prototype.fields;
12661         var records = [];
12662         var root = o;
12663             for(var i = 0; i < root.length; i++){
12664                     var n = root[i];
12665                 var values = {};
12666                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12667                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12668                 var f = fields.items[j];
12669                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12670                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12671                 v = f.convert(v);
12672                 values[f.name] = v;
12673             }
12674                 var record = new recordType(values, id);
12675                 record.json = n;
12676                 records[records.length] = record;
12677             }
12678             return {
12679                 records : records,
12680                 totalRecords : records.length
12681             };
12682     }
12683 });/*
12684  * - LGPL
12685  * * 
12686  */
12687
12688 /**
12689  * @class Roo.bootstrap.ComboBox
12690  * @extends Roo.bootstrap.TriggerField
12691  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12692  * @cfg {Boolean} append (true|false) default false
12693  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12694  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12695  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12696  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12697  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12698  * @cfg {Boolean} animate default true
12699  * @cfg {Boolean} emptyResultText only for touch device
12700  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12701  * @cfg {String} emptyTitle default ''
12702  * @constructor
12703  * Create a new ComboBox.
12704  * @param {Object} config Configuration options
12705  */
12706 Roo.bootstrap.ComboBox = function(config){
12707     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12708     this.addEvents({
12709         /**
12710          * @event expand
12711          * Fires when the dropdown list is expanded
12712         * @param {Roo.bootstrap.ComboBox} combo This combo box
12713         */
12714         'expand' : true,
12715         /**
12716          * @event collapse
12717          * Fires when the dropdown list is collapsed
12718         * @param {Roo.bootstrap.ComboBox} combo This combo box
12719         */
12720         'collapse' : true,
12721         /**
12722          * @event beforeselect
12723          * Fires before a list item is selected. Return false to cancel the selection.
12724         * @param {Roo.bootstrap.ComboBox} combo This combo box
12725         * @param {Roo.data.Record} record The data record returned from the underlying store
12726         * @param {Number} index The index of the selected item in the dropdown list
12727         */
12728         'beforeselect' : true,
12729         /**
12730          * @event select
12731          * Fires when a list item is selected
12732         * @param {Roo.bootstrap.ComboBox} combo This combo box
12733         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12734         * @param {Number} index The index of the selected item in the dropdown list
12735         */
12736         'select' : true,
12737         /**
12738          * @event beforequery
12739          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12740          * The event object passed has these properties:
12741         * @param {Roo.bootstrap.ComboBox} combo This combo box
12742         * @param {String} query The query
12743         * @param {Boolean} forceAll true to force "all" query
12744         * @param {Boolean} cancel true to cancel the query
12745         * @param {Object} e The query event object
12746         */
12747         'beforequery': true,
12748          /**
12749          * @event add
12750          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12751         * @param {Roo.bootstrap.ComboBox} combo This combo box
12752         */
12753         'add' : true,
12754         /**
12755          * @event edit
12756          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12757         * @param {Roo.bootstrap.ComboBox} combo This combo box
12758         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12759         */
12760         'edit' : true,
12761         /**
12762          * @event remove
12763          * Fires when the remove value from the combobox array
12764         * @param {Roo.bootstrap.ComboBox} combo This combo box
12765         */
12766         'remove' : true,
12767         /**
12768          * @event afterremove
12769          * Fires when the remove value from the combobox array
12770         * @param {Roo.bootstrap.ComboBox} combo This combo box
12771         */
12772         'afterremove' : true,
12773         /**
12774          * @event specialfilter
12775          * Fires when specialfilter
12776             * @param {Roo.bootstrap.ComboBox} combo This combo box
12777             */
12778         'specialfilter' : true,
12779         /**
12780          * @event tick
12781          * Fires when tick the element
12782             * @param {Roo.bootstrap.ComboBox} combo This combo box
12783             */
12784         'tick' : true,
12785         /**
12786          * @event touchviewdisplay
12787          * Fires when touch view require special display (default is using displayField)
12788             * @param {Roo.bootstrap.ComboBox} combo This combo box
12789             * @param {Object} cfg set html .
12790             */
12791         'touchviewdisplay' : true
12792         
12793     });
12794     
12795     this.item = [];
12796     this.tickItems = [];
12797     
12798     this.selectedIndex = -1;
12799     if(this.mode == 'local'){
12800         if(config.queryDelay === undefined){
12801             this.queryDelay = 10;
12802         }
12803         if(config.minChars === undefined){
12804             this.minChars = 0;
12805         }
12806     }
12807 };
12808
12809 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12810      
12811     /**
12812      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12813      * rendering into an Roo.Editor, defaults to false)
12814      */
12815     /**
12816      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12817      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12818      */
12819     /**
12820      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12821      */
12822     /**
12823      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12824      * the dropdown list (defaults to undefined, with no header element)
12825      */
12826
12827      /**
12828      * @cfg {String/Roo.Template} tpl The template to use to render the output
12829      */
12830      
12831      /**
12832      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12833      */
12834     listWidth: undefined,
12835     /**
12836      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12837      * mode = 'remote' or 'text' if mode = 'local')
12838      */
12839     displayField: undefined,
12840     
12841     /**
12842      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12843      * mode = 'remote' or 'value' if mode = 'local'). 
12844      * Note: use of a valueField requires the user make a selection
12845      * in order for a value to be mapped.
12846      */
12847     valueField: undefined,
12848     /**
12849      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12850      */
12851     modalTitle : '',
12852     
12853     /**
12854      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12855      * field's data value (defaults to the underlying DOM element's name)
12856      */
12857     hiddenName: undefined,
12858     /**
12859      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12860      */
12861     listClass: '',
12862     /**
12863      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12864      */
12865     selectedClass: 'active',
12866     
12867     /**
12868      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12869      */
12870     shadow:'sides',
12871     /**
12872      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12873      * anchor positions (defaults to 'tl-bl')
12874      */
12875     listAlign: 'tl-bl?',
12876     /**
12877      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12878      */
12879     maxHeight: 300,
12880     /**
12881      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12882      * query specified by the allQuery config option (defaults to 'query')
12883      */
12884     triggerAction: 'query',
12885     /**
12886      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12887      * (defaults to 4, does not apply if editable = false)
12888      */
12889     minChars : 4,
12890     /**
12891      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12892      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12893      */
12894     typeAhead: false,
12895     /**
12896      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12897      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12898      */
12899     queryDelay: 500,
12900     /**
12901      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12902      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12903      */
12904     pageSize: 0,
12905     /**
12906      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12907      * when editable = true (defaults to false)
12908      */
12909     selectOnFocus:false,
12910     /**
12911      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12912      */
12913     queryParam: 'query',
12914     /**
12915      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12916      * when mode = 'remote' (defaults to 'Loading...')
12917      */
12918     loadingText: 'Loading...',
12919     /**
12920      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12921      */
12922     resizable: false,
12923     /**
12924      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12925      */
12926     handleHeight : 8,
12927     /**
12928      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12929      * traditional select (defaults to true)
12930      */
12931     editable: true,
12932     /**
12933      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12934      */
12935     allQuery: '',
12936     /**
12937      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12938      */
12939     mode: 'remote',
12940     /**
12941      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12942      * listWidth has a higher value)
12943      */
12944     minListWidth : 70,
12945     /**
12946      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12947      * allow the user to set arbitrary text into the field (defaults to false)
12948      */
12949     forceSelection:false,
12950     /**
12951      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12952      * if typeAhead = true (defaults to 250)
12953      */
12954     typeAheadDelay : 250,
12955     /**
12956      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12957      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12958      */
12959     valueNotFoundText : undefined,
12960     /**
12961      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12962      */
12963     blockFocus : false,
12964     
12965     /**
12966      * @cfg {Boolean} disableClear Disable showing of clear button.
12967      */
12968     disableClear : false,
12969     /**
12970      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12971      */
12972     alwaysQuery : false,
12973     
12974     /**
12975      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12976      */
12977     multiple : false,
12978     
12979     /**
12980      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12981      */
12982     invalidClass : "has-warning",
12983     
12984     /**
12985      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12986      */
12987     validClass : "has-success",
12988     
12989     /**
12990      * @cfg {Boolean} specialFilter (true|false) special filter default false
12991      */
12992     specialFilter : false,
12993     
12994     /**
12995      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12996      */
12997     mobileTouchView : true,
12998     
12999     /**
13000      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13001      */
13002     useNativeIOS : false,
13003     
13004     ios_options : false,
13005     
13006     //private
13007     addicon : false,
13008     editicon: false,
13009     
13010     page: 0,
13011     hasQuery: false,
13012     append: false,
13013     loadNext: false,
13014     autoFocus : true,
13015     tickable : false,
13016     btnPosition : 'right',
13017     triggerList : true,
13018     showToggleBtn : true,
13019     animate : true,
13020     emptyResultText: 'Empty',
13021     triggerText : 'Select',
13022     emptyTitle : '',
13023     
13024     // element that contains real text value.. (when hidden is used..)
13025     
13026     getAutoCreate : function()
13027     {   
13028         var cfg = false;
13029         //render
13030         /*
13031          * Render classic select for iso
13032          */
13033         
13034         if(Roo.isIOS && this.useNativeIOS){
13035             cfg = this.getAutoCreateNativeIOS();
13036             return cfg;
13037         }
13038         
13039         /*
13040          * Touch Devices
13041          */
13042         
13043         if(Roo.isTouch && this.mobileTouchView){
13044             cfg = this.getAutoCreateTouchView();
13045             return cfg;;
13046         }
13047         
13048         /*
13049          *  Normal ComboBox
13050          */
13051         if(!this.tickable){
13052             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13053             return cfg;
13054         }
13055         
13056         /*
13057          *  ComboBox with tickable selections
13058          */
13059              
13060         var align = this.labelAlign || this.parentLabelAlign();
13061         
13062         cfg = {
13063             cls : 'form-group roo-combobox-tickable' //input-group
13064         };
13065         
13066         var btn_text_select = '';
13067         var btn_text_done = '';
13068         var btn_text_cancel = '';
13069         
13070         if (this.btn_text_show) {
13071             btn_text_select = 'Select';
13072             btn_text_done = 'Done';
13073             btn_text_cancel = 'Cancel'; 
13074         }
13075         
13076         var buttons = {
13077             tag : 'div',
13078             cls : 'tickable-buttons',
13079             cn : [
13080                 {
13081                     tag : 'button',
13082                     type : 'button',
13083                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13084                     //html : this.triggerText
13085                     html: btn_text_select
13086                 },
13087                 {
13088                     tag : 'button',
13089                     type : 'button',
13090                     name : 'ok',
13091                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13092                     //html : 'Done'
13093                     html: btn_text_done
13094                 },
13095                 {
13096                     tag : 'button',
13097                     type : 'button',
13098                     name : 'cancel',
13099                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13100                     //html : 'Cancel'
13101                     html: btn_text_cancel
13102                 }
13103             ]
13104         };
13105         
13106         if(this.editable){
13107             buttons.cn.unshift({
13108                 tag: 'input',
13109                 cls: 'roo-select2-search-field-input'
13110             });
13111         }
13112         
13113         var _this = this;
13114         
13115         Roo.each(buttons.cn, function(c){
13116             if (_this.size) {
13117                 c.cls += ' btn-' + _this.size;
13118             }
13119
13120             if (_this.disabled) {
13121                 c.disabled = true;
13122             }
13123         });
13124         
13125         var box = {
13126             tag: 'div',
13127             cn: [
13128                 {
13129                     tag: 'input',
13130                     type : 'hidden',
13131                     cls: 'form-hidden-field'
13132                 },
13133                 {
13134                     tag: 'ul',
13135                     cls: 'roo-select2-choices',
13136                     cn:[
13137                         {
13138                             tag: 'li',
13139                             cls: 'roo-select2-search-field',
13140                             cn: [
13141                                 buttons
13142                             ]
13143                         }
13144                     ]
13145                 }
13146             ]
13147         };
13148         
13149         var combobox = {
13150             cls: 'roo-select2-container input-group roo-select2-container-multi',
13151             cn: [
13152                 box
13153 //                {
13154 //                    tag: 'ul',
13155 //                    cls: 'typeahead typeahead-long dropdown-menu',
13156 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13157 //                }
13158             ]
13159         };
13160         
13161         if(this.hasFeedback && !this.allowBlank){
13162             
13163             var feedback = {
13164                 tag: 'span',
13165                 cls: 'glyphicon form-control-feedback'
13166             };
13167
13168             combobox.cn.push(feedback);
13169         }
13170         
13171         
13172         if (align ==='left' && this.fieldLabel.length) {
13173             
13174             cfg.cls += ' roo-form-group-label-left';
13175             
13176             cfg.cn = [
13177                 {
13178                     tag : 'i',
13179                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13180                     tooltip : 'This field is required'
13181                 },
13182                 {
13183                     tag: 'label',
13184                     'for' :  id,
13185                     cls : 'control-label',
13186                     html : this.fieldLabel
13187
13188                 },
13189                 {
13190                     cls : "", 
13191                     cn: [
13192                         combobox
13193                     ]
13194                 }
13195
13196             ];
13197             
13198             var labelCfg = cfg.cn[1];
13199             var contentCfg = cfg.cn[2];
13200             
13201
13202             if(this.indicatorpos == 'right'){
13203                 
13204                 cfg.cn = [
13205                     {
13206                         tag: 'label',
13207                         'for' :  id,
13208                         cls : 'control-label',
13209                         cn : [
13210                             {
13211                                 tag : 'span',
13212                                 html : this.fieldLabel
13213                             },
13214                             {
13215                                 tag : 'i',
13216                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13217                                 tooltip : 'This field is required'
13218                             }
13219                         ]
13220                     },
13221                     {
13222                         cls : "",
13223                         cn: [
13224                             combobox
13225                         ]
13226                     }
13227
13228                 ];
13229                 
13230                 
13231                 
13232                 labelCfg = cfg.cn[0];
13233                 contentCfg = cfg.cn[1];
13234             
13235             }
13236             
13237             if(this.labelWidth > 12){
13238                 labelCfg.style = "width: " + this.labelWidth + 'px';
13239             }
13240             
13241             if(this.labelWidth < 13 && this.labelmd == 0){
13242                 this.labelmd = this.labelWidth;
13243             }
13244             
13245             if(this.labellg > 0){
13246                 labelCfg.cls += ' col-lg-' + this.labellg;
13247                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13248             }
13249             
13250             if(this.labelmd > 0){
13251                 labelCfg.cls += ' col-md-' + this.labelmd;
13252                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13253             }
13254             
13255             if(this.labelsm > 0){
13256                 labelCfg.cls += ' col-sm-' + this.labelsm;
13257                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13258             }
13259             
13260             if(this.labelxs > 0){
13261                 labelCfg.cls += ' col-xs-' + this.labelxs;
13262                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13263             }
13264                 
13265                 
13266         } else if ( this.fieldLabel.length) {
13267 //                Roo.log(" label");
13268                  cfg.cn = [
13269                     {
13270                         tag : 'i',
13271                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13272                         tooltip : 'This field is required'
13273                     },
13274                     {
13275                         tag: 'label',
13276                         //cls : 'input-group-addon',
13277                         html : this.fieldLabel
13278                     },
13279                     combobox
13280                 ];
13281                 
13282                 if(this.indicatorpos == 'right'){
13283                     cfg.cn = [
13284                         {
13285                             tag: 'label',
13286                             //cls : 'input-group-addon',
13287                             html : this.fieldLabel
13288                         },
13289                         {
13290                             tag : 'i',
13291                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13292                             tooltip : 'This field is required'
13293                         },
13294                         combobox
13295                     ];
13296                     
13297                 }
13298
13299         } else {
13300             
13301 //                Roo.log(" no label && no align");
13302                 cfg = combobox
13303                      
13304                 
13305         }
13306          
13307         var settings=this;
13308         ['xs','sm','md','lg'].map(function(size){
13309             if (settings[size]) {
13310                 cfg.cls += ' col-' + size + '-' + settings[size];
13311             }
13312         });
13313         
13314         return cfg;
13315         
13316     },
13317     
13318     _initEventsCalled : false,
13319     
13320     // private
13321     initEvents: function()
13322     {   
13323         if (this._initEventsCalled) { // as we call render... prevent looping...
13324             return;
13325         }
13326         this._initEventsCalled = true;
13327         
13328         if (!this.store) {
13329             throw "can not find store for combo";
13330         }
13331         
13332         this.indicator = this.indicatorEl();
13333         
13334         this.store = Roo.factory(this.store, Roo.data);
13335         this.store.parent = this;
13336         
13337         // if we are building from html. then this element is so complex, that we can not really
13338         // use the rendered HTML.
13339         // so we have to trash and replace the previous code.
13340         if (Roo.XComponent.build_from_html) {
13341             // remove this element....
13342             var e = this.el.dom, k=0;
13343             while (e ) { e = e.previousSibling;  ++k;}
13344
13345             this.el.remove();
13346             
13347             this.el=false;
13348             this.rendered = false;
13349             
13350             this.render(this.parent().getChildContainer(true), k);
13351         }
13352         
13353         if(Roo.isIOS && this.useNativeIOS){
13354             this.initIOSView();
13355             return;
13356         }
13357         
13358         /*
13359          * Touch Devices
13360          */
13361         
13362         if(Roo.isTouch && this.mobileTouchView){
13363             this.initTouchView();
13364             return;
13365         }
13366         
13367         if(this.tickable){
13368             this.initTickableEvents();
13369             return;
13370         }
13371         
13372         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13373         
13374         if(this.hiddenName){
13375             
13376             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13377             
13378             this.hiddenField.dom.value =
13379                 this.hiddenValue !== undefined ? this.hiddenValue :
13380                 this.value !== undefined ? this.value : '';
13381
13382             // prevent input submission
13383             this.el.dom.removeAttribute('name');
13384             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13385              
13386              
13387         }
13388         //if(Roo.isGecko){
13389         //    this.el.dom.setAttribute('autocomplete', 'off');
13390         //}
13391         
13392         var cls = 'x-combo-list';
13393         
13394         //this.list = new Roo.Layer({
13395         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13396         //});
13397         
13398         var _this = this;
13399         
13400         (function(){
13401             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13402             _this.list.setWidth(lw);
13403         }).defer(100);
13404         
13405         this.list.on('mouseover', this.onViewOver, this);
13406         this.list.on('mousemove', this.onViewMove, this);
13407         this.list.on('scroll', this.onViewScroll, this);
13408         
13409         /*
13410         this.list.swallowEvent('mousewheel');
13411         this.assetHeight = 0;
13412
13413         if(this.title){
13414             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13415             this.assetHeight += this.header.getHeight();
13416         }
13417
13418         this.innerList = this.list.createChild({cls:cls+'-inner'});
13419         this.innerList.on('mouseover', this.onViewOver, this);
13420         this.innerList.on('mousemove', this.onViewMove, this);
13421         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13422         
13423         if(this.allowBlank && !this.pageSize && !this.disableClear){
13424             this.footer = this.list.createChild({cls:cls+'-ft'});
13425             this.pageTb = new Roo.Toolbar(this.footer);
13426            
13427         }
13428         if(this.pageSize){
13429             this.footer = this.list.createChild({cls:cls+'-ft'});
13430             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13431                     {pageSize: this.pageSize});
13432             
13433         }
13434         
13435         if (this.pageTb && this.allowBlank && !this.disableClear) {
13436             var _this = this;
13437             this.pageTb.add(new Roo.Toolbar.Fill(), {
13438                 cls: 'x-btn-icon x-btn-clear',
13439                 text: '&#160;',
13440                 handler: function()
13441                 {
13442                     _this.collapse();
13443                     _this.clearValue();
13444                     _this.onSelect(false, -1);
13445                 }
13446             });
13447         }
13448         if (this.footer) {
13449             this.assetHeight += this.footer.getHeight();
13450         }
13451         */
13452             
13453         if(!this.tpl){
13454             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13455         }
13456
13457         this.view = new Roo.View(this.list, this.tpl, {
13458             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13459         });
13460         //this.view.wrapEl.setDisplayed(false);
13461         this.view.on('click', this.onViewClick, this);
13462         
13463         
13464         this.store.on('beforeload', this.onBeforeLoad, this);
13465         this.store.on('load', this.onLoad, this);
13466         this.store.on('loadexception', this.onLoadException, this);
13467         /*
13468         if(this.resizable){
13469             this.resizer = new Roo.Resizable(this.list,  {
13470                pinned:true, handles:'se'
13471             });
13472             this.resizer.on('resize', function(r, w, h){
13473                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13474                 this.listWidth = w;
13475                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13476                 this.restrictHeight();
13477             }, this);
13478             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13479         }
13480         */
13481         if(!this.editable){
13482             this.editable = true;
13483             this.setEditable(false);
13484         }
13485         
13486         /*
13487         
13488         if (typeof(this.events.add.listeners) != 'undefined') {
13489             
13490             this.addicon = this.wrap.createChild(
13491                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13492        
13493             this.addicon.on('click', function(e) {
13494                 this.fireEvent('add', this);
13495             }, this);
13496         }
13497         if (typeof(this.events.edit.listeners) != 'undefined') {
13498             
13499             this.editicon = this.wrap.createChild(
13500                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13501             if (this.addicon) {
13502                 this.editicon.setStyle('margin-left', '40px');
13503             }
13504             this.editicon.on('click', function(e) {
13505                 
13506                 // we fire even  if inothing is selected..
13507                 this.fireEvent('edit', this, this.lastData );
13508                 
13509             }, this);
13510         }
13511         */
13512         
13513         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13514             "up" : function(e){
13515                 this.inKeyMode = true;
13516                 this.selectPrev();
13517             },
13518
13519             "down" : function(e){
13520                 if(!this.isExpanded()){
13521                     this.onTriggerClick();
13522                 }else{
13523                     this.inKeyMode = true;
13524                     this.selectNext();
13525                 }
13526             },
13527
13528             "enter" : function(e){
13529 //                this.onViewClick();
13530                 //return true;
13531                 this.collapse();
13532                 
13533                 if(this.fireEvent("specialkey", this, e)){
13534                     this.onViewClick(false);
13535                 }
13536                 
13537                 return true;
13538             },
13539
13540             "esc" : function(e){
13541                 this.collapse();
13542             },
13543
13544             "tab" : function(e){
13545                 this.collapse();
13546                 
13547                 if(this.fireEvent("specialkey", this, e)){
13548                     this.onViewClick(false);
13549                 }
13550                 
13551                 return true;
13552             },
13553
13554             scope : this,
13555
13556             doRelay : function(foo, bar, hname){
13557                 if(hname == 'down' || this.scope.isExpanded()){
13558                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13559                 }
13560                 return true;
13561             },
13562
13563             forceKeyDown: true
13564         });
13565         
13566         
13567         this.queryDelay = Math.max(this.queryDelay || 10,
13568                 this.mode == 'local' ? 10 : 250);
13569         
13570         
13571         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13572         
13573         if(this.typeAhead){
13574             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13575         }
13576         if(this.editable !== false){
13577             this.inputEl().on("keyup", this.onKeyUp, this);
13578         }
13579         if(this.forceSelection){
13580             this.inputEl().on('blur', this.doForce, this);
13581         }
13582         
13583         if(this.multiple){
13584             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13585             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13586         }
13587     },
13588     
13589     initTickableEvents: function()
13590     {   
13591         this.createList();
13592         
13593         if(this.hiddenName){
13594             
13595             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13596             
13597             this.hiddenField.dom.value =
13598                 this.hiddenValue !== undefined ? this.hiddenValue :
13599                 this.value !== undefined ? this.value : '';
13600
13601             // prevent input submission
13602             this.el.dom.removeAttribute('name');
13603             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13604              
13605              
13606         }
13607         
13608 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13609         
13610         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13611         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13612         if(this.triggerList){
13613             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13614         }
13615          
13616         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13617         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13618         
13619         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13620         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13621         
13622         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13623         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13624         
13625         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13626         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13627         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13628         
13629         this.okBtn.hide();
13630         this.cancelBtn.hide();
13631         
13632         var _this = this;
13633         
13634         (function(){
13635             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13636             _this.list.setWidth(lw);
13637         }).defer(100);
13638         
13639         this.list.on('mouseover', this.onViewOver, this);
13640         this.list.on('mousemove', this.onViewMove, this);
13641         
13642         this.list.on('scroll', this.onViewScroll, this);
13643         
13644         if(!this.tpl){
13645             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13646                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13647         }
13648
13649         this.view = new Roo.View(this.list, this.tpl, {
13650             singleSelect:true,
13651             tickable:true,
13652             parent:this,
13653             store: this.store,
13654             selectedClass: this.selectedClass
13655         });
13656         
13657         //this.view.wrapEl.setDisplayed(false);
13658         this.view.on('click', this.onViewClick, this);
13659         
13660         
13661         
13662         this.store.on('beforeload', this.onBeforeLoad, this);
13663         this.store.on('load', this.onLoad, this);
13664         this.store.on('loadexception', this.onLoadException, this);
13665         
13666         if(this.editable){
13667             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13668                 "up" : function(e){
13669                     this.inKeyMode = true;
13670                     this.selectPrev();
13671                 },
13672
13673                 "down" : function(e){
13674                     this.inKeyMode = true;
13675                     this.selectNext();
13676                 },
13677
13678                 "enter" : function(e){
13679                     if(this.fireEvent("specialkey", this, e)){
13680                         this.onViewClick(false);
13681                     }
13682                     
13683                     return true;
13684                 },
13685
13686                 "esc" : function(e){
13687                     this.onTickableFooterButtonClick(e, false, false);
13688                 },
13689
13690                 "tab" : function(e){
13691                     this.fireEvent("specialkey", this, e);
13692                     
13693                     this.onTickableFooterButtonClick(e, false, false);
13694                     
13695                     return true;
13696                 },
13697
13698                 scope : this,
13699
13700                 doRelay : function(e, fn, key){
13701                     if(this.scope.isExpanded()){
13702                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13703                     }
13704                     return true;
13705                 },
13706
13707                 forceKeyDown: true
13708             });
13709         }
13710         
13711         this.queryDelay = Math.max(this.queryDelay || 10,
13712                 this.mode == 'local' ? 10 : 250);
13713         
13714         
13715         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13716         
13717         if(this.typeAhead){
13718             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13719         }
13720         
13721         if(this.editable !== false){
13722             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13723         }
13724         
13725         this.indicator = this.indicatorEl();
13726         
13727         if(this.indicator){
13728             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13729             this.indicator.hide();
13730         }
13731         
13732     },
13733
13734     onDestroy : function(){
13735         if(this.view){
13736             this.view.setStore(null);
13737             this.view.el.removeAllListeners();
13738             this.view.el.remove();
13739             this.view.purgeListeners();
13740         }
13741         if(this.list){
13742             this.list.dom.innerHTML  = '';
13743         }
13744         
13745         if(this.store){
13746             this.store.un('beforeload', this.onBeforeLoad, this);
13747             this.store.un('load', this.onLoad, this);
13748             this.store.un('loadexception', this.onLoadException, this);
13749         }
13750         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13751     },
13752
13753     // private
13754     fireKey : function(e){
13755         if(e.isNavKeyPress() && !this.list.isVisible()){
13756             this.fireEvent("specialkey", this, e);
13757         }
13758     },
13759
13760     // private
13761     onResize: function(w, h){
13762 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13763 //        
13764 //        if(typeof w != 'number'){
13765 //            // we do not handle it!?!?
13766 //            return;
13767 //        }
13768 //        var tw = this.trigger.getWidth();
13769 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13770 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13771 //        var x = w - tw;
13772 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13773 //            
13774 //        //this.trigger.setStyle('left', x+'px');
13775 //        
13776 //        if(this.list && this.listWidth === undefined){
13777 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13778 //            this.list.setWidth(lw);
13779 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13780 //        }
13781         
13782     
13783         
13784     },
13785
13786     /**
13787      * Allow or prevent the user from directly editing the field text.  If false is passed,
13788      * the user will only be able to select from the items defined in the dropdown list.  This method
13789      * is the runtime equivalent of setting the 'editable' config option at config time.
13790      * @param {Boolean} value True to allow the user to directly edit the field text
13791      */
13792     setEditable : function(value){
13793         if(value == this.editable){
13794             return;
13795         }
13796         this.editable = value;
13797         if(!value){
13798             this.inputEl().dom.setAttribute('readOnly', true);
13799             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13800             this.inputEl().addClass('x-combo-noedit');
13801         }else{
13802             this.inputEl().dom.setAttribute('readOnly', false);
13803             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13804             this.inputEl().removeClass('x-combo-noedit');
13805         }
13806     },
13807
13808     // private
13809     
13810     onBeforeLoad : function(combo,opts){
13811         if(!this.hasFocus){
13812             return;
13813         }
13814          if (!opts.add) {
13815             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13816          }
13817         this.restrictHeight();
13818         this.selectedIndex = -1;
13819     },
13820
13821     // private
13822     onLoad : function(){
13823         
13824         this.hasQuery = false;
13825         
13826         if(!this.hasFocus){
13827             return;
13828         }
13829         
13830         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13831             this.loading.hide();
13832         }
13833         
13834         if(this.store.getCount() > 0){
13835             
13836             this.expand();
13837             this.restrictHeight();
13838             if(this.lastQuery == this.allQuery){
13839                 if(this.editable && !this.tickable){
13840                     this.inputEl().dom.select();
13841                 }
13842                 
13843                 if(
13844                     !this.selectByValue(this.value, true) &&
13845                     this.autoFocus && 
13846                     (
13847                         !this.store.lastOptions ||
13848                         typeof(this.store.lastOptions.add) == 'undefined' || 
13849                         this.store.lastOptions.add != true
13850                     )
13851                 ){
13852                     this.select(0, true);
13853                 }
13854             }else{
13855                 if(this.autoFocus){
13856                     this.selectNext();
13857                 }
13858                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13859                     this.taTask.delay(this.typeAheadDelay);
13860                 }
13861             }
13862         }else{
13863             this.onEmptyResults();
13864         }
13865         
13866         //this.el.focus();
13867     },
13868     // private
13869     onLoadException : function()
13870     {
13871         this.hasQuery = false;
13872         
13873         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13874             this.loading.hide();
13875         }
13876         
13877         if(this.tickable && this.editable){
13878             return;
13879         }
13880         
13881         this.collapse();
13882         // only causes errors at present
13883         //Roo.log(this.store.reader.jsonData);
13884         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13885             // fixme
13886             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13887         //}
13888         
13889         
13890     },
13891     // private
13892     onTypeAhead : function(){
13893         if(this.store.getCount() > 0){
13894             var r = this.store.getAt(0);
13895             var newValue = r.data[this.displayField];
13896             var len = newValue.length;
13897             var selStart = this.getRawValue().length;
13898             
13899             if(selStart != len){
13900                 this.setRawValue(newValue);
13901                 this.selectText(selStart, newValue.length);
13902             }
13903         }
13904     },
13905
13906     // private
13907     onSelect : function(record, index){
13908         
13909         if(this.fireEvent('beforeselect', this, record, index) !== false){
13910         
13911             this.setFromData(index > -1 ? record.data : false);
13912             
13913             this.collapse();
13914             this.fireEvent('select', this, record, index);
13915         }
13916     },
13917
13918     /**
13919      * Returns the currently selected field value or empty string if no value is set.
13920      * @return {String} value The selected value
13921      */
13922     getValue : function()
13923     {
13924         if(Roo.isIOS && this.useNativeIOS){
13925             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13926         }
13927         
13928         if(this.multiple){
13929             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13930         }
13931         
13932         if(this.valueField){
13933             return typeof this.value != 'undefined' ? this.value : '';
13934         }else{
13935             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13936         }
13937     },
13938     
13939     getRawValue : function()
13940     {
13941         if(Roo.isIOS && this.useNativeIOS){
13942             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13943         }
13944         
13945         var v = this.inputEl().getValue();
13946         
13947         return v;
13948     },
13949
13950     /**
13951      * Clears any text/value currently set in the field
13952      */
13953     clearValue : function(){
13954         
13955         if(this.hiddenField){
13956             this.hiddenField.dom.value = '';
13957         }
13958         this.value = '';
13959         this.setRawValue('');
13960         this.lastSelectionText = '';
13961         this.lastData = false;
13962         
13963         var close = this.closeTriggerEl();
13964         
13965         if(close){
13966             close.hide();
13967         }
13968         
13969         this.validate();
13970         
13971     },
13972
13973     /**
13974      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13975      * will be displayed in the field.  If the value does not match the data value of an existing item,
13976      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13977      * Otherwise the field will be blank (although the value will still be set).
13978      * @param {String} value The value to match
13979      */
13980     setValue : function(v)
13981     {
13982         if(Roo.isIOS && this.useNativeIOS){
13983             this.setIOSValue(v);
13984             return;
13985         }
13986         
13987         if(this.multiple){
13988             this.syncValue();
13989             return;
13990         }
13991         
13992         var text = v;
13993         if(this.valueField){
13994             var r = this.findRecord(this.valueField, v);
13995             if(r){
13996                 text = r.data[this.displayField];
13997             }else if(this.valueNotFoundText !== undefined){
13998                 text = this.valueNotFoundText;
13999             }
14000         }
14001         this.lastSelectionText = text;
14002         if(this.hiddenField){
14003             this.hiddenField.dom.value = v;
14004         }
14005         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14006         this.value = v;
14007         
14008         var close = this.closeTriggerEl();
14009         
14010         if(close){
14011             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14012         }
14013         
14014         this.validate();
14015     },
14016     /**
14017      * @property {Object} the last set data for the element
14018      */
14019     
14020     lastData : false,
14021     /**
14022      * Sets the value of the field based on a object which is related to the record format for the store.
14023      * @param {Object} value the value to set as. or false on reset?
14024      */
14025     setFromData : function(o){
14026         
14027         if(this.multiple){
14028             this.addItem(o);
14029             return;
14030         }
14031             
14032         var dv = ''; // display value
14033         var vv = ''; // value value..
14034         this.lastData = o;
14035         if (this.displayField) {
14036             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14037         } else {
14038             // this is an error condition!!!
14039             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14040         }
14041         
14042         if(this.valueField){
14043             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14044         }
14045         
14046         var close = this.closeTriggerEl();
14047         
14048         if(close){
14049             if(dv.length || vv * 1 > 0){
14050                 close.show() ;
14051                 this.blockFocus=true;
14052             } else {
14053                 close.hide();
14054             }             
14055         }
14056         
14057         if(this.hiddenField){
14058             this.hiddenField.dom.value = vv;
14059             
14060             this.lastSelectionText = dv;
14061             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14062             this.value = vv;
14063             return;
14064         }
14065         // no hidden field.. - we store the value in 'value', but still display
14066         // display field!!!!
14067         this.lastSelectionText = dv;
14068         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14069         this.value = vv;
14070         
14071         
14072         
14073     },
14074     // private
14075     reset : function(){
14076         // overridden so that last data is reset..
14077         
14078         if(this.multiple){
14079             this.clearItem();
14080             return;
14081         }
14082         
14083         this.setValue(this.originalValue);
14084         //this.clearInvalid();
14085         this.lastData = false;
14086         if (this.view) {
14087             this.view.clearSelections();
14088         }
14089         
14090         this.validate();
14091     },
14092     // private
14093     findRecord : function(prop, value){
14094         var record;
14095         if(this.store.getCount() > 0){
14096             this.store.each(function(r){
14097                 if(r.data[prop] == value){
14098                     record = r;
14099                     return false;
14100                 }
14101                 return true;
14102             });
14103         }
14104         return record;
14105     },
14106     
14107     getName: function()
14108     {
14109         // returns hidden if it's set..
14110         if (!this.rendered) {return ''};
14111         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14112         
14113     },
14114     // private
14115     onViewMove : function(e, t){
14116         this.inKeyMode = false;
14117     },
14118
14119     // private
14120     onViewOver : function(e, t){
14121         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14122             return;
14123         }
14124         var item = this.view.findItemFromChild(t);
14125         
14126         if(item){
14127             var index = this.view.indexOf(item);
14128             this.select(index, false);
14129         }
14130     },
14131
14132     // private
14133     onViewClick : function(view, doFocus, el, e)
14134     {
14135         var index = this.view.getSelectedIndexes()[0];
14136         
14137         var r = this.store.getAt(index);
14138         
14139         if(this.tickable){
14140             
14141             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14142                 return;
14143             }
14144             
14145             var rm = false;
14146             var _this = this;
14147             
14148             Roo.each(this.tickItems, function(v,k){
14149                 
14150                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14151                     Roo.log(v);
14152                     _this.tickItems.splice(k, 1);
14153                     
14154                     if(typeof(e) == 'undefined' && view == false){
14155                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14156                     }
14157                     
14158                     rm = true;
14159                     return;
14160                 }
14161             });
14162             
14163             if(rm){
14164                 return;
14165             }
14166             
14167             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14168                 this.tickItems.push(r.data);
14169             }
14170             
14171             if(typeof(e) == 'undefined' && view == false){
14172                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14173             }
14174                     
14175             return;
14176         }
14177         
14178         if(r){
14179             this.onSelect(r, index);
14180         }
14181         if(doFocus !== false && !this.blockFocus){
14182             this.inputEl().focus();
14183         }
14184     },
14185
14186     // private
14187     restrictHeight : function(){
14188         //this.innerList.dom.style.height = '';
14189         //var inner = this.innerList.dom;
14190         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14191         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14192         //this.list.beginUpdate();
14193         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14194         this.list.alignTo(this.inputEl(), this.listAlign);
14195         this.list.alignTo(this.inputEl(), this.listAlign);
14196         //this.list.endUpdate();
14197     },
14198
14199     // private
14200     onEmptyResults : function(){
14201         
14202         if(this.tickable && this.editable){
14203             this.hasFocus = false;
14204             this.restrictHeight();
14205             return;
14206         }
14207         
14208         this.collapse();
14209     },
14210
14211     /**
14212      * Returns true if the dropdown list is expanded, else false.
14213      */
14214     isExpanded : function(){
14215         return this.list.isVisible();
14216     },
14217
14218     /**
14219      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14220      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14221      * @param {String} value The data value of the item to select
14222      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14223      * selected item if it is not currently in view (defaults to true)
14224      * @return {Boolean} True if the value matched an item in the list, else false
14225      */
14226     selectByValue : function(v, scrollIntoView){
14227         if(v !== undefined && v !== null){
14228             var r = this.findRecord(this.valueField || this.displayField, v);
14229             if(r){
14230                 this.select(this.store.indexOf(r), scrollIntoView);
14231                 return true;
14232             }
14233         }
14234         return false;
14235     },
14236
14237     /**
14238      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14239      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14240      * @param {Number} index The zero-based index of the list item to select
14241      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14242      * selected item if it is not currently in view (defaults to true)
14243      */
14244     select : function(index, scrollIntoView){
14245         this.selectedIndex = index;
14246         this.view.select(index);
14247         if(scrollIntoView !== false){
14248             var el = this.view.getNode(index);
14249             /*
14250              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14251              */
14252             if(el){
14253                 this.list.scrollChildIntoView(el, false);
14254             }
14255         }
14256     },
14257
14258     // private
14259     selectNext : function(){
14260         var ct = this.store.getCount();
14261         if(ct > 0){
14262             if(this.selectedIndex == -1){
14263                 this.select(0);
14264             }else if(this.selectedIndex < ct-1){
14265                 this.select(this.selectedIndex+1);
14266             }
14267         }
14268     },
14269
14270     // private
14271     selectPrev : function(){
14272         var ct = this.store.getCount();
14273         if(ct > 0){
14274             if(this.selectedIndex == -1){
14275                 this.select(0);
14276             }else if(this.selectedIndex != 0){
14277                 this.select(this.selectedIndex-1);
14278             }
14279         }
14280     },
14281
14282     // private
14283     onKeyUp : function(e){
14284         if(this.editable !== false && !e.isSpecialKey()){
14285             this.lastKey = e.getKey();
14286             this.dqTask.delay(this.queryDelay);
14287         }
14288     },
14289
14290     // private
14291     validateBlur : function(){
14292         return !this.list || !this.list.isVisible();   
14293     },
14294
14295     // private
14296     initQuery : function(){
14297         
14298         var v = this.getRawValue();
14299         
14300         if(this.tickable && this.editable){
14301             v = this.tickableInputEl().getValue();
14302         }
14303         
14304         this.doQuery(v);
14305     },
14306
14307     // private
14308     doForce : function(){
14309         if(this.inputEl().dom.value.length > 0){
14310             this.inputEl().dom.value =
14311                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14312              
14313         }
14314     },
14315
14316     /**
14317      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14318      * query allowing the query action to be canceled if needed.
14319      * @param {String} query The SQL query to execute
14320      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14321      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14322      * saved in the current store (defaults to false)
14323      */
14324     doQuery : function(q, forceAll){
14325         
14326         if(q === undefined || q === null){
14327             q = '';
14328         }
14329         var qe = {
14330             query: q,
14331             forceAll: forceAll,
14332             combo: this,
14333             cancel:false
14334         };
14335         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14336             return false;
14337         }
14338         q = qe.query;
14339         
14340         forceAll = qe.forceAll;
14341         if(forceAll === true || (q.length >= this.minChars)){
14342             
14343             this.hasQuery = true;
14344             
14345             if(this.lastQuery != q || this.alwaysQuery){
14346                 this.lastQuery = q;
14347                 if(this.mode == 'local'){
14348                     this.selectedIndex = -1;
14349                     if(forceAll){
14350                         this.store.clearFilter();
14351                     }else{
14352                         
14353                         if(this.specialFilter){
14354                             this.fireEvent('specialfilter', this);
14355                             this.onLoad();
14356                             return;
14357                         }
14358                         
14359                         this.store.filter(this.displayField, q);
14360                     }
14361                     
14362                     this.store.fireEvent("datachanged", this.store);
14363                     
14364                     this.onLoad();
14365                     
14366                     
14367                 }else{
14368                     
14369                     this.store.baseParams[this.queryParam] = q;
14370                     
14371                     var options = {params : this.getParams(q)};
14372                     
14373                     if(this.loadNext){
14374                         options.add = true;
14375                         options.params.start = this.page * this.pageSize;
14376                     }
14377                     
14378                     this.store.load(options);
14379                     
14380                     /*
14381                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14382                      *  we should expand the list on onLoad
14383                      *  so command out it
14384                      */
14385 //                    this.expand();
14386                 }
14387             }else{
14388                 this.selectedIndex = -1;
14389                 this.onLoad();   
14390             }
14391         }
14392         
14393         this.loadNext = false;
14394     },
14395     
14396     // private
14397     getParams : function(q){
14398         var p = {};
14399         //p[this.queryParam] = q;
14400         
14401         if(this.pageSize){
14402             p.start = 0;
14403             p.limit = this.pageSize;
14404         }
14405         return p;
14406     },
14407
14408     /**
14409      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14410      */
14411     collapse : function(){
14412         if(!this.isExpanded()){
14413             return;
14414         }
14415         
14416         this.list.hide();
14417         
14418         this.hasFocus = false;
14419         
14420         if(this.tickable){
14421             this.okBtn.hide();
14422             this.cancelBtn.hide();
14423             this.trigger.show();
14424             
14425             if(this.editable){
14426                 this.tickableInputEl().dom.value = '';
14427                 this.tickableInputEl().blur();
14428             }
14429             
14430         }
14431         
14432         Roo.get(document).un('mousedown', this.collapseIf, this);
14433         Roo.get(document).un('mousewheel', this.collapseIf, this);
14434         if (!this.editable) {
14435             Roo.get(document).un('keydown', this.listKeyPress, this);
14436         }
14437         this.fireEvent('collapse', this);
14438         
14439         this.validate();
14440     },
14441
14442     // private
14443     collapseIf : function(e){
14444         var in_combo  = e.within(this.el);
14445         var in_list =  e.within(this.list);
14446         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14447         
14448         if (in_combo || in_list || is_list) {
14449             //e.stopPropagation();
14450             return;
14451         }
14452         
14453         if(this.tickable){
14454             this.onTickableFooterButtonClick(e, false, false);
14455         }
14456
14457         this.collapse();
14458         
14459     },
14460
14461     /**
14462      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14463      */
14464     expand : function(){
14465        
14466         if(this.isExpanded() || !this.hasFocus){
14467             return;
14468         }
14469         
14470         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14471         this.list.setWidth(lw);
14472         
14473         Roo.log('expand');
14474         
14475         this.list.show();
14476         
14477         this.restrictHeight();
14478         
14479         if(this.tickable){
14480             
14481             this.tickItems = Roo.apply([], this.item);
14482             
14483             this.okBtn.show();
14484             this.cancelBtn.show();
14485             this.trigger.hide();
14486             
14487             if(this.editable){
14488                 this.tickableInputEl().focus();
14489             }
14490             
14491         }
14492         
14493         Roo.get(document).on('mousedown', this.collapseIf, this);
14494         Roo.get(document).on('mousewheel', this.collapseIf, this);
14495         if (!this.editable) {
14496             Roo.get(document).on('keydown', this.listKeyPress, this);
14497         }
14498         
14499         this.fireEvent('expand', this);
14500     },
14501
14502     // private
14503     // Implements the default empty TriggerField.onTriggerClick function
14504     onTriggerClick : function(e)
14505     {
14506         Roo.log('trigger click');
14507         
14508         if(this.disabled || !this.triggerList){
14509             return;
14510         }
14511         
14512         this.page = 0;
14513         this.loadNext = false;
14514         
14515         if(this.isExpanded()){
14516             this.collapse();
14517             if (!this.blockFocus) {
14518                 this.inputEl().focus();
14519             }
14520             
14521         }else {
14522             this.hasFocus = true;
14523             if(this.triggerAction == 'all') {
14524                 this.doQuery(this.allQuery, true);
14525             } else {
14526                 this.doQuery(this.getRawValue());
14527             }
14528             if (!this.blockFocus) {
14529                 this.inputEl().focus();
14530             }
14531         }
14532     },
14533     
14534     onTickableTriggerClick : function(e)
14535     {
14536         if(this.disabled){
14537             return;
14538         }
14539         
14540         this.page = 0;
14541         this.loadNext = false;
14542         this.hasFocus = true;
14543         
14544         if(this.triggerAction == 'all') {
14545             this.doQuery(this.allQuery, true);
14546         } else {
14547             this.doQuery(this.getRawValue());
14548         }
14549     },
14550     
14551     onSearchFieldClick : function(e)
14552     {
14553         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14554             this.onTickableFooterButtonClick(e, false, false);
14555             return;
14556         }
14557         
14558         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14559             return;
14560         }
14561         
14562         this.page = 0;
14563         this.loadNext = false;
14564         this.hasFocus = true;
14565         
14566         if(this.triggerAction == 'all') {
14567             this.doQuery(this.allQuery, true);
14568         } else {
14569             this.doQuery(this.getRawValue());
14570         }
14571     },
14572     
14573     listKeyPress : function(e)
14574     {
14575         //Roo.log('listkeypress');
14576         // scroll to first matching element based on key pres..
14577         if (e.isSpecialKey()) {
14578             return false;
14579         }
14580         var k = String.fromCharCode(e.getKey()).toUpperCase();
14581         //Roo.log(k);
14582         var match  = false;
14583         var csel = this.view.getSelectedNodes();
14584         var cselitem = false;
14585         if (csel.length) {
14586             var ix = this.view.indexOf(csel[0]);
14587             cselitem  = this.store.getAt(ix);
14588             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14589                 cselitem = false;
14590             }
14591             
14592         }
14593         
14594         this.store.each(function(v) { 
14595             if (cselitem) {
14596                 // start at existing selection.
14597                 if (cselitem.id == v.id) {
14598                     cselitem = false;
14599                 }
14600                 return true;
14601             }
14602                 
14603             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14604                 match = this.store.indexOf(v);
14605                 return false;
14606             }
14607             return true;
14608         }, this);
14609         
14610         if (match === false) {
14611             return true; // no more action?
14612         }
14613         // scroll to?
14614         this.view.select(match);
14615         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14616         sn.scrollIntoView(sn.dom.parentNode, false);
14617     },
14618     
14619     onViewScroll : function(e, t){
14620         
14621         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){
14622             return;
14623         }
14624         
14625         this.hasQuery = true;
14626         
14627         this.loading = this.list.select('.loading', true).first();
14628         
14629         if(this.loading === null){
14630             this.list.createChild({
14631                 tag: 'div',
14632                 cls: 'loading roo-select2-more-results roo-select2-active',
14633                 html: 'Loading more results...'
14634             });
14635             
14636             this.loading = this.list.select('.loading', true).first();
14637             
14638             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14639             
14640             this.loading.hide();
14641         }
14642         
14643         this.loading.show();
14644         
14645         var _combo = this;
14646         
14647         this.page++;
14648         this.loadNext = true;
14649         
14650         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14651         
14652         return;
14653     },
14654     
14655     addItem : function(o)
14656     {   
14657         var dv = ''; // display value
14658         
14659         if (this.displayField) {
14660             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14661         } else {
14662             // this is an error condition!!!
14663             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14664         }
14665         
14666         if(!dv.length){
14667             return;
14668         }
14669         
14670         var choice = this.choices.createChild({
14671             tag: 'li',
14672             cls: 'roo-select2-search-choice',
14673             cn: [
14674                 {
14675                     tag: 'div',
14676                     html: dv
14677                 },
14678                 {
14679                     tag: 'a',
14680                     href: '#',
14681                     cls: 'roo-select2-search-choice-close fa fa-times',
14682                     tabindex: '-1'
14683                 }
14684             ]
14685             
14686         }, this.searchField);
14687         
14688         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14689         
14690         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14691         
14692         this.item.push(o);
14693         
14694         this.lastData = o;
14695         
14696         this.syncValue();
14697         
14698         this.inputEl().dom.value = '';
14699         
14700         this.validate();
14701     },
14702     
14703     onRemoveItem : function(e, _self, o)
14704     {
14705         e.preventDefault();
14706         
14707         this.lastItem = Roo.apply([], this.item);
14708         
14709         var index = this.item.indexOf(o.data) * 1;
14710         
14711         if( index < 0){
14712             Roo.log('not this item?!');
14713             return;
14714         }
14715         
14716         this.item.splice(index, 1);
14717         o.item.remove();
14718         
14719         this.syncValue();
14720         
14721         this.fireEvent('remove', this, e);
14722         
14723         this.validate();
14724         
14725     },
14726     
14727     syncValue : function()
14728     {
14729         if(!this.item.length){
14730             this.clearValue();
14731             return;
14732         }
14733             
14734         var value = [];
14735         var _this = this;
14736         Roo.each(this.item, function(i){
14737             if(_this.valueField){
14738                 value.push(i[_this.valueField]);
14739                 return;
14740             }
14741
14742             value.push(i);
14743         });
14744
14745         this.value = value.join(',');
14746
14747         if(this.hiddenField){
14748             this.hiddenField.dom.value = this.value;
14749         }
14750         
14751         this.store.fireEvent("datachanged", this.store);
14752         
14753         this.validate();
14754     },
14755     
14756     clearItem : function()
14757     {
14758         if(!this.multiple){
14759             return;
14760         }
14761         
14762         this.item = [];
14763         
14764         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14765            c.remove();
14766         });
14767         
14768         this.syncValue();
14769         
14770         this.validate();
14771         
14772         if(this.tickable && !Roo.isTouch){
14773             this.view.refresh();
14774         }
14775     },
14776     
14777     inputEl: function ()
14778     {
14779         if(Roo.isIOS && this.useNativeIOS){
14780             return this.el.select('select.roo-ios-select', true).first();
14781         }
14782         
14783         if(Roo.isTouch && this.mobileTouchView){
14784             return this.el.select('input.form-control',true).first();
14785         }
14786         
14787         if(this.tickable){
14788             return this.searchField;
14789         }
14790         
14791         return this.el.select('input.form-control',true).first();
14792     },
14793     
14794     onTickableFooterButtonClick : function(e, btn, el)
14795     {
14796         e.preventDefault();
14797         
14798         this.lastItem = Roo.apply([], this.item);
14799         
14800         if(btn && btn.name == 'cancel'){
14801             this.tickItems = Roo.apply([], this.item);
14802             this.collapse();
14803             return;
14804         }
14805         
14806         this.clearItem();
14807         
14808         var _this = this;
14809         
14810         Roo.each(this.tickItems, function(o){
14811             _this.addItem(o);
14812         });
14813         
14814         this.collapse();
14815         
14816     },
14817     
14818     validate : function()
14819     {
14820         if(this.getVisibilityEl().hasClass('hidden')){
14821             return true;
14822         }
14823         
14824         var v = this.getRawValue();
14825         
14826         if(this.multiple){
14827             v = this.getValue();
14828         }
14829         
14830         if(this.disabled || this.allowBlank || v.length){
14831             this.markValid();
14832             return true;
14833         }
14834         
14835         this.markInvalid();
14836         return false;
14837     },
14838     
14839     tickableInputEl : function()
14840     {
14841         if(!this.tickable || !this.editable){
14842             return this.inputEl();
14843         }
14844         
14845         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14846     },
14847     
14848     
14849     getAutoCreateTouchView : function()
14850     {
14851         var id = Roo.id();
14852         
14853         var cfg = {
14854             cls: 'form-group' //input-group
14855         };
14856         
14857         var input =  {
14858             tag: 'input',
14859             id : id,
14860             type : this.inputType,
14861             cls : 'form-control x-combo-noedit',
14862             autocomplete: 'new-password',
14863             placeholder : this.placeholder || '',
14864             readonly : true
14865         };
14866         
14867         if (this.name) {
14868             input.name = this.name;
14869         }
14870         
14871         if (this.size) {
14872             input.cls += ' input-' + this.size;
14873         }
14874         
14875         if (this.disabled) {
14876             input.disabled = true;
14877         }
14878         
14879         var inputblock = {
14880             cls : '',
14881             cn : [
14882                 input
14883             ]
14884         };
14885         
14886         if(this.before){
14887             inputblock.cls += ' input-group';
14888             
14889             inputblock.cn.unshift({
14890                 tag :'span',
14891                 cls : 'input-group-addon',
14892                 html : this.before
14893             });
14894         }
14895         
14896         if(this.removable && !this.multiple){
14897             inputblock.cls += ' roo-removable';
14898             
14899             inputblock.cn.push({
14900                 tag: 'button',
14901                 html : 'x',
14902                 cls : 'roo-combo-removable-btn close'
14903             });
14904         }
14905
14906         if(this.hasFeedback && !this.allowBlank){
14907             
14908             inputblock.cls += ' has-feedback';
14909             
14910             inputblock.cn.push({
14911                 tag: 'span',
14912                 cls: 'glyphicon form-control-feedback'
14913             });
14914             
14915         }
14916         
14917         if (this.after) {
14918             
14919             inputblock.cls += (this.before) ? '' : ' input-group';
14920             
14921             inputblock.cn.push({
14922                 tag :'span',
14923                 cls : 'input-group-addon',
14924                 html : this.after
14925             });
14926         }
14927
14928         var box = {
14929             tag: 'div',
14930             cn: [
14931                 {
14932                     tag: 'input',
14933                     type : 'hidden',
14934                     cls: 'form-hidden-field'
14935                 },
14936                 inputblock
14937             ]
14938             
14939         };
14940         
14941         if(this.multiple){
14942             box = {
14943                 tag: 'div',
14944                 cn: [
14945                     {
14946                         tag: 'input',
14947                         type : 'hidden',
14948                         cls: 'form-hidden-field'
14949                     },
14950                     {
14951                         tag: 'ul',
14952                         cls: 'roo-select2-choices',
14953                         cn:[
14954                             {
14955                                 tag: 'li',
14956                                 cls: 'roo-select2-search-field',
14957                                 cn: [
14958
14959                                     inputblock
14960                                 ]
14961                             }
14962                         ]
14963                     }
14964                 ]
14965             }
14966         };
14967         
14968         var combobox = {
14969             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14970             cn: [
14971                 box
14972             ]
14973         };
14974         
14975         if(!this.multiple && this.showToggleBtn){
14976             
14977             var caret = {
14978                         tag: 'span',
14979                         cls: 'caret'
14980             };
14981             
14982             if (this.caret != false) {
14983                 caret = {
14984                      tag: 'i',
14985                      cls: 'fa fa-' + this.caret
14986                 };
14987                 
14988             }
14989             
14990             combobox.cn.push({
14991                 tag :'span',
14992                 cls : 'input-group-addon btn dropdown-toggle',
14993                 cn : [
14994                     caret,
14995                     {
14996                         tag: 'span',
14997                         cls: 'combobox-clear',
14998                         cn  : [
14999                             {
15000                                 tag : 'i',
15001                                 cls: 'icon-remove'
15002                             }
15003                         ]
15004                     }
15005                 ]
15006
15007             })
15008         }
15009         
15010         if(this.multiple){
15011             combobox.cls += ' roo-select2-container-multi';
15012         }
15013         
15014         var align = this.labelAlign || this.parentLabelAlign();
15015         
15016         if (align ==='left' && this.fieldLabel.length) {
15017
15018             cfg.cn = [
15019                 {
15020                    tag : 'i',
15021                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15022                    tooltip : 'This field is required'
15023                 },
15024                 {
15025                     tag: 'label',
15026                     cls : 'control-label',
15027                     html : this.fieldLabel
15028
15029                 },
15030                 {
15031                     cls : '', 
15032                     cn: [
15033                         combobox
15034                     ]
15035                 }
15036             ];
15037             
15038             var labelCfg = cfg.cn[1];
15039             var contentCfg = cfg.cn[2];
15040             
15041
15042             if(this.indicatorpos == 'right'){
15043                 cfg.cn = [
15044                     {
15045                         tag: 'label',
15046                         'for' :  id,
15047                         cls : 'control-label',
15048                         cn : [
15049                             {
15050                                 tag : 'span',
15051                                 html : this.fieldLabel
15052                             },
15053                             {
15054                                 tag : 'i',
15055                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15056                                 tooltip : 'This field is required'
15057                             }
15058                         ]
15059                     },
15060                     {
15061                         cls : "",
15062                         cn: [
15063                             combobox
15064                         ]
15065                     }
15066
15067                 ];
15068                 
15069                 labelCfg = cfg.cn[0];
15070                 contentCfg = cfg.cn[1];
15071             }
15072             
15073            
15074             
15075             if(this.labelWidth > 12){
15076                 labelCfg.style = "width: " + this.labelWidth + 'px';
15077             }
15078             
15079             if(this.labelWidth < 13 && this.labelmd == 0){
15080                 this.labelmd = this.labelWidth;
15081             }
15082             
15083             if(this.labellg > 0){
15084                 labelCfg.cls += ' col-lg-' + this.labellg;
15085                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15086             }
15087             
15088             if(this.labelmd > 0){
15089                 labelCfg.cls += ' col-md-' + this.labelmd;
15090                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15091             }
15092             
15093             if(this.labelsm > 0){
15094                 labelCfg.cls += ' col-sm-' + this.labelsm;
15095                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15096             }
15097             
15098             if(this.labelxs > 0){
15099                 labelCfg.cls += ' col-xs-' + this.labelxs;
15100                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15101             }
15102                 
15103                 
15104         } else if ( this.fieldLabel.length) {
15105             cfg.cn = [
15106                 {
15107                    tag : 'i',
15108                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15109                    tooltip : 'This field is required'
15110                 },
15111                 {
15112                     tag: 'label',
15113                     cls : 'control-label',
15114                     html : this.fieldLabel
15115
15116                 },
15117                 {
15118                     cls : '', 
15119                     cn: [
15120                         combobox
15121                     ]
15122                 }
15123             ];
15124             
15125             if(this.indicatorpos == 'right'){
15126                 cfg.cn = [
15127                     {
15128                         tag: 'label',
15129                         cls : 'control-label',
15130                         html : this.fieldLabel,
15131                         cn : [
15132                             {
15133                                tag : 'i',
15134                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15135                                tooltip : 'This field is required'
15136                             }
15137                         ]
15138                     },
15139                     {
15140                         cls : '', 
15141                         cn: [
15142                             combobox
15143                         ]
15144                     }
15145                 ];
15146             }
15147         } else {
15148             cfg.cn = combobox;    
15149         }
15150         
15151         
15152         var settings = this;
15153         
15154         ['xs','sm','md','lg'].map(function(size){
15155             if (settings[size]) {
15156                 cfg.cls += ' col-' + size + '-' + settings[size];
15157             }
15158         });
15159         
15160         return cfg;
15161     },
15162     
15163     initTouchView : function()
15164     {
15165         this.renderTouchView();
15166         
15167         this.touchViewEl.on('scroll', function(){
15168             this.el.dom.scrollTop = 0;
15169         }, this);
15170         
15171         this.originalValue = this.getValue();
15172         
15173         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15174         
15175         this.inputEl().on("click", this.showTouchView, this);
15176         if (this.triggerEl) {
15177             this.triggerEl.on("click", this.showTouchView, this);
15178         }
15179         
15180         
15181         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15182         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15183         
15184         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15185         
15186         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15187         this.store.on('load', this.onTouchViewLoad, this);
15188         this.store.on('loadexception', this.onTouchViewLoadException, this);
15189         
15190         if(this.hiddenName){
15191             
15192             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15193             
15194             this.hiddenField.dom.value =
15195                 this.hiddenValue !== undefined ? this.hiddenValue :
15196                 this.value !== undefined ? this.value : '';
15197         
15198             this.el.dom.removeAttribute('name');
15199             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15200         }
15201         
15202         if(this.multiple){
15203             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15204             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15205         }
15206         
15207         if(this.removable && !this.multiple){
15208             var close = this.closeTriggerEl();
15209             if(close){
15210                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15211                 close.on('click', this.removeBtnClick, this, close);
15212             }
15213         }
15214         /*
15215          * fix the bug in Safari iOS8
15216          */
15217         this.inputEl().on("focus", function(e){
15218             document.activeElement.blur();
15219         }, this);
15220         
15221         return;
15222         
15223         
15224     },
15225     
15226     renderTouchView : function()
15227     {
15228         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15229         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15230         
15231         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15232         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15233         
15234         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15235         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15236         this.touchViewBodyEl.setStyle('overflow', 'auto');
15237         
15238         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15239         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15240         
15241         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15242         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15243         
15244     },
15245     
15246     showTouchView : function()
15247     {
15248         if(this.disabled){
15249             return;
15250         }
15251         
15252         this.touchViewHeaderEl.hide();
15253
15254         if(this.modalTitle.length){
15255             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15256             this.touchViewHeaderEl.show();
15257         }
15258
15259         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15260         this.touchViewEl.show();
15261
15262         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15263         
15264         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15265         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15266
15267         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15268
15269         if(this.modalTitle.length){
15270             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15271         }
15272         
15273         this.touchViewBodyEl.setHeight(bodyHeight);
15274
15275         if(this.animate){
15276             var _this = this;
15277             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15278         }else{
15279             this.touchViewEl.addClass('in');
15280         }
15281
15282         this.doTouchViewQuery();
15283         
15284     },
15285     
15286     hideTouchView : function()
15287     {
15288         this.touchViewEl.removeClass('in');
15289
15290         if(this.animate){
15291             var _this = this;
15292             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15293         }else{
15294             this.touchViewEl.setStyle('display', 'none');
15295         }
15296         
15297     },
15298     
15299     setTouchViewValue : function()
15300     {
15301         if(this.multiple){
15302             this.clearItem();
15303         
15304             var _this = this;
15305
15306             Roo.each(this.tickItems, function(o){
15307                 this.addItem(o);
15308             }, this);
15309         }
15310         
15311         this.hideTouchView();
15312     },
15313     
15314     doTouchViewQuery : function()
15315     {
15316         var qe = {
15317             query: '',
15318             forceAll: true,
15319             combo: this,
15320             cancel:false
15321         };
15322         
15323         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15324             return false;
15325         }
15326         
15327         if(!this.alwaysQuery || this.mode == 'local'){
15328             this.onTouchViewLoad();
15329             return;
15330         }
15331         
15332         this.store.load();
15333     },
15334     
15335     onTouchViewBeforeLoad : function(combo,opts)
15336     {
15337         return;
15338     },
15339
15340     // private
15341     onTouchViewLoad : function()
15342     {
15343         if(this.store.getCount() < 1){
15344             this.onTouchViewEmptyResults();
15345             return;
15346         }
15347         
15348         this.clearTouchView();
15349         
15350         var rawValue = this.getRawValue();
15351         
15352         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15353         
15354         this.tickItems = [];
15355         
15356         this.store.data.each(function(d, rowIndex){
15357             var row = this.touchViewListGroup.createChild(template);
15358             
15359             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15360                 row.addClass(d.data.cls);
15361             }
15362             
15363             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15364                 var cfg = {
15365                     data : d.data,
15366                     html : d.data[this.displayField]
15367                 };
15368                 
15369                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15370                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15371                 }
15372             }
15373             row.removeClass('selected');
15374             if(!this.multiple && this.valueField &&
15375                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15376             {
15377                 // radio buttons..
15378                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15379                 row.addClass('selected');
15380             }
15381             
15382             if(this.multiple && this.valueField &&
15383                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15384             {
15385                 
15386                 // checkboxes...
15387                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15388                 this.tickItems.push(d.data);
15389             }
15390             
15391             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15392             
15393         }, this);
15394         
15395         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15396         
15397         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15398
15399         if(this.modalTitle.length){
15400             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15401         }
15402
15403         var listHeight = this.touchViewListGroup.getHeight();
15404         
15405         var _this = this;
15406         
15407         if(firstChecked && listHeight > bodyHeight){
15408             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15409         }
15410         
15411     },
15412     
15413     onTouchViewLoadException : function()
15414     {
15415         this.hideTouchView();
15416     },
15417     
15418     onTouchViewEmptyResults : function()
15419     {
15420         this.clearTouchView();
15421         
15422         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15423         
15424         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15425         
15426     },
15427     
15428     clearTouchView : function()
15429     {
15430         this.touchViewListGroup.dom.innerHTML = '';
15431     },
15432     
15433     onTouchViewClick : function(e, el, o)
15434     {
15435         e.preventDefault();
15436         
15437         var row = o.row;
15438         var rowIndex = o.rowIndex;
15439         
15440         var r = this.store.getAt(rowIndex);
15441         
15442         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15443             
15444             if(!this.multiple){
15445                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15446                     c.dom.removeAttribute('checked');
15447                 }, this);
15448
15449                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15450
15451                 this.setFromData(r.data);
15452
15453                 var close = this.closeTriggerEl();
15454
15455                 if(close){
15456                     close.show();
15457                 }
15458
15459                 this.hideTouchView();
15460
15461                 this.fireEvent('select', this, r, rowIndex);
15462
15463                 return;
15464             }
15465
15466             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15467                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15468                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15469                 return;
15470             }
15471
15472             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15473             this.addItem(r.data);
15474             this.tickItems.push(r.data);
15475         }
15476     },
15477     
15478     getAutoCreateNativeIOS : function()
15479     {
15480         var cfg = {
15481             cls: 'form-group' //input-group,
15482         };
15483         
15484         var combobox =  {
15485             tag: 'select',
15486             cls : 'roo-ios-select'
15487         };
15488         
15489         if (this.name) {
15490             combobox.name = this.name;
15491         }
15492         
15493         if (this.disabled) {
15494             combobox.disabled = true;
15495         }
15496         
15497         var settings = this;
15498         
15499         ['xs','sm','md','lg'].map(function(size){
15500             if (settings[size]) {
15501                 cfg.cls += ' col-' + size + '-' + settings[size];
15502             }
15503         });
15504         
15505         cfg.cn = combobox;
15506         
15507         return cfg;
15508         
15509     },
15510     
15511     initIOSView : function()
15512     {
15513         this.store.on('load', this.onIOSViewLoad, this);
15514         
15515         return;
15516     },
15517     
15518     onIOSViewLoad : function()
15519     {
15520         if(this.store.getCount() < 1){
15521             return;
15522         }
15523         
15524         this.clearIOSView();
15525         
15526         if(this.allowBlank) {
15527             
15528             var default_text = '-- SELECT --';
15529             
15530             if(this.placeholder.length){
15531                 default_text = this.placeholder;
15532             }
15533             
15534             if(this.emptyTitle.length){
15535                 default_text += ' - ' + this.emptyTitle + ' -';
15536             }
15537             
15538             var opt = this.inputEl().createChild({
15539                 tag: 'option',
15540                 value : 0,
15541                 html : default_text
15542             });
15543             
15544             var o = {};
15545             o[this.valueField] = 0;
15546             o[this.displayField] = default_text;
15547             
15548             this.ios_options.push({
15549                 data : o,
15550                 el : opt
15551             });
15552             
15553         }
15554         
15555         this.store.data.each(function(d, rowIndex){
15556             
15557             var html = '';
15558             
15559             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15560                 html = d.data[this.displayField];
15561             }
15562             
15563             var value = '';
15564             
15565             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15566                 value = d.data[this.valueField];
15567             }
15568             
15569             var option = {
15570                 tag: 'option',
15571                 value : value,
15572                 html : html
15573             };
15574             
15575             if(this.value == d.data[this.valueField]){
15576                 option['selected'] = true;
15577             }
15578             
15579             var opt = this.inputEl().createChild(option);
15580             
15581             this.ios_options.push({
15582                 data : d.data,
15583                 el : opt
15584             });
15585             
15586         }, this);
15587         
15588         this.inputEl().on('change', function(){
15589            this.fireEvent('select', this);
15590         }, this);
15591         
15592     },
15593     
15594     clearIOSView: function()
15595     {
15596         this.inputEl().dom.innerHTML = '';
15597         
15598         this.ios_options = [];
15599     },
15600     
15601     setIOSValue: function(v)
15602     {
15603         this.value = v;
15604         
15605         if(!this.ios_options){
15606             return;
15607         }
15608         
15609         Roo.each(this.ios_options, function(opts){
15610            
15611            opts.el.dom.removeAttribute('selected');
15612            
15613            if(opts.data[this.valueField] != v){
15614                return;
15615            }
15616            
15617            opts.el.dom.setAttribute('selected', true);
15618            
15619         }, this);
15620     }
15621
15622     /** 
15623     * @cfg {Boolean} grow 
15624     * @hide 
15625     */
15626     /** 
15627     * @cfg {Number} growMin 
15628     * @hide 
15629     */
15630     /** 
15631     * @cfg {Number} growMax 
15632     * @hide 
15633     */
15634     /**
15635      * @hide
15636      * @method autoSize
15637      */
15638 });
15639
15640 Roo.apply(Roo.bootstrap.ComboBox,  {
15641     
15642     header : {
15643         tag: 'div',
15644         cls: 'modal-header',
15645         cn: [
15646             {
15647                 tag: 'h4',
15648                 cls: 'modal-title'
15649             }
15650         ]
15651     },
15652     
15653     body : {
15654         tag: 'div',
15655         cls: 'modal-body',
15656         cn: [
15657             {
15658                 tag: 'ul',
15659                 cls: 'list-group'
15660             }
15661         ]
15662     },
15663     
15664     listItemRadio : {
15665         tag: 'li',
15666         cls: 'list-group-item',
15667         cn: [
15668             {
15669                 tag: 'span',
15670                 cls: 'roo-combobox-list-group-item-value'
15671             },
15672             {
15673                 tag: 'div',
15674                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15675                 cn: [
15676                     {
15677                         tag: 'input',
15678                         type: 'radio'
15679                     },
15680                     {
15681                         tag: 'label'
15682                     }
15683                 ]
15684             }
15685         ]
15686     },
15687     
15688     listItemCheckbox : {
15689         tag: 'li',
15690         cls: 'list-group-item',
15691         cn: [
15692             {
15693                 tag: 'span',
15694                 cls: 'roo-combobox-list-group-item-value'
15695             },
15696             {
15697                 tag: 'div',
15698                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15699                 cn: [
15700                     {
15701                         tag: 'input',
15702                         type: 'checkbox'
15703                     },
15704                     {
15705                         tag: 'label'
15706                     }
15707                 ]
15708             }
15709         ]
15710     },
15711     
15712     emptyResult : {
15713         tag: 'div',
15714         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15715     },
15716     
15717     footer : {
15718         tag: 'div',
15719         cls: 'modal-footer',
15720         cn: [
15721             {
15722                 tag: 'div',
15723                 cls: 'row',
15724                 cn: [
15725                     {
15726                         tag: 'div',
15727                         cls: 'col-xs-6 text-left',
15728                         cn: {
15729                             tag: 'button',
15730                             cls: 'btn btn-danger roo-touch-view-cancel',
15731                             html: 'Cancel'
15732                         }
15733                     },
15734                     {
15735                         tag: 'div',
15736                         cls: 'col-xs-6 text-right',
15737                         cn: {
15738                             tag: 'button',
15739                             cls: 'btn btn-success roo-touch-view-ok',
15740                             html: 'OK'
15741                         }
15742                     }
15743                 ]
15744             }
15745         ]
15746         
15747     }
15748 });
15749
15750 Roo.apply(Roo.bootstrap.ComboBox,  {
15751     
15752     touchViewTemplate : {
15753         tag: 'div',
15754         cls: 'modal fade roo-combobox-touch-view',
15755         cn: [
15756             {
15757                 tag: 'div',
15758                 cls: 'modal-dialog',
15759                 style : 'position:fixed', // we have to fix position....
15760                 cn: [
15761                     {
15762                         tag: 'div',
15763                         cls: 'modal-content',
15764                         cn: [
15765                             Roo.bootstrap.ComboBox.header,
15766                             Roo.bootstrap.ComboBox.body,
15767                             Roo.bootstrap.ComboBox.footer
15768                         ]
15769                     }
15770                 ]
15771             }
15772         ]
15773     }
15774 });/*
15775  * Based on:
15776  * Ext JS Library 1.1.1
15777  * Copyright(c) 2006-2007, Ext JS, LLC.
15778  *
15779  * Originally Released Under LGPL - original licence link has changed is not relivant.
15780  *
15781  * Fork - LGPL
15782  * <script type="text/javascript">
15783  */
15784
15785 /**
15786  * @class Roo.View
15787  * @extends Roo.util.Observable
15788  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15789  * This class also supports single and multi selection modes. <br>
15790  * Create a data model bound view:
15791  <pre><code>
15792  var store = new Roo.data.Store(...);
15793
15794  var view = new Roo.View({
15795     el : "my-element",
15796     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15797  
15798     singleSelect: true,
15799     selectedClass: "ydataview-selected",
15800     store: store
15801  });
15802
15803  // listen for node click?
15804  view.on("click", function(vw, index, node, e){
15805  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15806  });
15807
15808  // load XML data
15809  dataModel.load("foobar.xml");
15810  </code></pre>
15811  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15812  * <br><br>
15813  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15814  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15815  * 
15816  * Note: old style constructor is still suported (container, template, config)
15817  * 
15818  * @constructor
15819  * Create a new View
15820  * @param {Object} config The config object
15821  * 
15822  */
15823 Roo.View = function(config, depreciated_tpl, depreciated_config){
15824     
15825     this.parent = false;
15826     
15827     if (typeof(depreciated_tpl) == 'undefined') {
15828         // new way.. - universal constructor.
15829         Roo.apply(this, config);
15830         this.el  = Roo.get(this.el);
15831     } else {
15832         // old format..
15833         this.el  = Roo.get(config);
15834         this.tpl = depreciated_tpl;
15835         Roo.apply(this, depreciated_config);
15836     }
15837     this.wrapEl  = this.el.wrap().wrap();
15838     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15839     
15840     
15841     if(typeof(this.tpl) == "string"){
15842         this.tpl = new Roo.Template(this.tpl);
15843     } else {
15844         // support xtype ctors..
15845         this.tpl = new Roo.factory(this.tpl, Roo);
15846     }
15847     
15848     
15849     this.tpl.compile();
15850     
15851     /** @private */
15852     this.addEvents({
15853         /**
15854          * @event beforeclick
15855          * Fires before a click is processed. Returns false to cancel the default action.
15856          * @param {Roo.View} this
15857          * @param {Number} index The index of the target node
15858          * @param {HTMLElement} node The target node
15859          * @param {Roo.EventObject} e The raw event object
15860          */
15861             "beforeclick" : true,
15862         /**
15863          * @event click
15864          * Fires when a template node is clicked.
15865          * @param {Roo.View} this
15866          * @param {Number} index The index of the target node
15867          * @param {HTMLElement} node The target node
15868          * @param {Roo.EventObject} e The raw event object
15869          */
15870             "click" : true,
15871         /**
15872          * @event dblclick
15873          * Fires when a template node is double clicked.
15874          * @param {Roo.View} this
15875          * @param {Number} index The index of the target node
15876          * @param {HTMLElement} node The target node
15877          * @param {Roo.EventObject} e The raw event object
15878          */
15879             "dblclick" : true,
15880         /**
15881          * @event contextmenu
15882          * Fires when a template node is right clicked.
15883          * @param {Roo.View} this
15884          * @param {Number} index The index of the target node
15885          * @param {HTMLElement} node The target node
15886          * @param {Roo.EventObject} e The raw event object
15887          */
15888             "contextmenu" : true,
15889         /**
15890          * @event selectionchange
15891          * Fires when the selected nodes change.
15892          * @param {Roo.View} this
15893          * @param {Array} selections Array of the selected nodes
15894          */
15895             "selectionchange" : true,
15896     
15897         /**
15898          * @event beforeselect
15899          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15900          * @param {Roo.View} this
15901          * @param {HTMLElement} node The node to be selected
15902          * @param {Array} selections Array of currently selected nodes
15903          */
15904             "beforeselect" : true,
15905         /**
15906          * @event preparedata
15907          * Fires on every row to render, to allow you to change the data.
15908          * @param {Roo.View} this
15909          * @param {Object} data to be rendered (change this)
15910          */
15911           "preparedata" : true
15912           
15913           
15914         });
15915
15916
15917
15918     this.el.on({
15919         "click": this.onClick,
15920         "dblclick": this.onDblClick,
15921         "contextmenu": this.onContextMenu,
15922         scope:this
15923     });
15924
15925     this.selections = [];
15926     this.nodes = [];
15927     this.cmp = new Roo.CompositeElementLite([]);
15928     if(this.store){
15929         this.store = Roo.factory(this.store, Roo.data);
15930         this.setStore(this.store, true);
15931     }
15932     
15933     if ( this.footer && this.footer.xtype) {
15934            
15935          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15936         
15937         this.footer.dataSource = this.store;
15938         this.footer.container = fctr;
15939         this.footer = Roo.factory(this.footer, Roo);
15940         fctr.insertFirst(this.el);
15941         
15942         // this is a bit insane - as the paging toolbar seems to detach the el..
15943 //        dom.parentNode.parentNode.parentNode
15944          // they get detached?
15945     }
15946     
15947     
15948     Roo.View.superclass.constructor.call(this);
15949     
15950     
15951 };
15952
15953 Roo.extend(Roo.View, Roo.util.Observable, {
15954     
15955      /**
15956      * @cfg {Roo.data.Store} store Data store to load data from.
15957      */
15958     store : false,
15959     
15960     /**
15961      * @cfg {String|Roo.Element} el The container element.
15962      */
15963     el : '',
15964     
15965     /**
15966      * @cfg {String|Roo.Template} tpl The template used by this View 
15967      */
15968     tpl : false,
15969     /**
15970      * @cfg {String} dataName the named area of the template to use as the data area
15971      *                          Works with domtemplates roo-name="name"
15972      */
15973     dataName: false,
15974     /**
15975      * @cfg {String} selectedClass The css class to add to selected nodes
15976      */
15977     selectedClass : "x-view-selected",
15978      /**
15979      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15980      */
15981     emptyText : "",
15982     
15983     /**
15984      * @cfg {String} text to display on mask (default Loading)
15985      */
15986     mask : false,
15987     /**
15988      * @cfg {Boolean} multiSelect Allow multiple selection
15989      */
15990     multiSelect : false,
15991     /**
15992      * @cfg {Boolean} singleSelect Allow single selection
15993      */
15994     singleSelect:  false,
15995     
15996     /**
15997      * @cfg {Boolean} toggleSelect - selecting 
15998      */
15999     toggleSelect : false,
16000     
16001     /**
16002      * @cfg {Boolean} tickable - selecting 
16003      */
16004     tickable : false,
16005     
16006     /**
16007      * Returns the element this view is bound to.
16008      * @return {Roo.Element}
16009      */
16010     getEl : function(){
16011         return this.wrapEl;
16012     },
16013     
16014     
16015
16016     /**
16017      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16018      */
16019     refresh : function(){
16020         //Roo.log('refresh');
16021         var t = this.tpl;
16022         
16023         // if we are using something like 'domtemplate', then
16024         // the what gets used is:
16025         // t.applySubtemplate(NAME, data, wrapping data..)
16026         // the outer template then get' applied with
16027         //     the store 'extra data'
16028         // and the body get's added to the
16029         //      roo-name="data" node?
16030         //      <span class='roo-tpl-{name}'></span> ?????
16031         
16032         
16033         
16034         this.clearSelections();
16035         this.el.update("");
16036         var html = [];
16037         var records = this.store.getRange();
16038         if(records.length < 1) {
16039             
16040             // is this valid??  = should it render a template??
16041             
16042             this.el.update(this.emptyText);
16043             return;
16044         }
16045         var el = this.el;
16046         if (this.dataName) {
16047             this.el.update(t.apply(this.store.meta)); //????
16048             el = this.el.child('.roo-tpl-' + this.dataName);
16049         }
16050         
16051         for(var i = 0, len = records.length; i < len; i++){
16052             var data = this.prepareData(records[i].data, i, records[i]);
16053             this.fireEvent("preparedata", this, data, i, records[i]);
16054             
16055             var d = Roo.apply({}, data);
16056             
16057             if(this.tickable){
16058                 Roo.apply(d, {'roo-id' : Roo.id()});
16059                 
16060                 var _this = this;
16061             
16062                 Roo.each(this.parent.item, function(item){
16063                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16064                         return;
16065                     }
16066                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16067                 });
16068             }
16069             
16070             html[html.length] = Roo.util.Format.trim(
16071                 this.dataName ?
16072                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16073                     t.apply(d)
16074             );
16075         }
16076         
16077         
16078         
16079         el.update(html.join(""));
16080         this.nodes = el.dom.childNodes;
16081         this.updateIndexes(0);
16082     },
16083     
16084
16085     /**
16086      * Function to override to reformat the data that is sent to
16087      * the template for each node.
16088      * DEPRICATED - use the preparedata event handler.
16089      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16090      * a JSON object for an UpdateManager bound view).
16091      */
16092     prepareData : function(data, index, record)
16093     {
16094         this.fireEvent("preparedata", this, data, index, record);
16095         return data;
16096     },
16097
16098     onUpdate : function(ds, record){
16099         // Roo.log('on update');   
16100         this.clearSelections();
16101         var index = this.store.indexOf(record);
16102         var n = this.nodes[index];
16103         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16104         n.parentNode.removeChild(n);
16105         this.updateIndexes(index, index);
16106     },
16107
16108     
16109     
16110 // --------- FIXME     
16111     onAdd : function(ds, records, index)
16112     {
16113         //Roo.log(['on Add', ds, records, index] );        
16114         this.clearSelections();
16115         if(this.nodes.length == 0){
16116             this.refresh();
16117             return;
16118         }
16119         var n = this.nodes[index];
16120         for(var i = 0, len = records.length; i < len; i++){
16121             var d = this.prepareData(records[i].data, i, records[i]);
16122             if(n){
16123                 this.tpl.insertBefore(n, d);
16124             }else{
16125                 
16126                 this.tpl.append(this.el, d);
16127             }
16128         }
16129         this.updateIndexes(index);
16130     },
16131
16132     onRemove : function(ds, record, index){
16133        // Roo.log('onRemove');
16134         this.clearSelections();
16135         var el = this.dataName  ?
16136             this.el.child('.roo-tpl-' + this.dataName) :
16137             this.el; 
16138         
16139         el.dom.removeChild(this.nodes[index]);
16140         this.updateIndexes(index);
16141     },
16142
16143     /**
16144      * Refresh an individual node.
16145      * @param {Number} index
16146      */
16147     refreshNode : function(index){
16148         this.onUpdate(this.store, this.store.getAt(index));
16149     },
16150
16151     updateIndexes : function(startIndex, endIndex){
16152         var ns = this.nodes;
16153         startIndex = startIndex || 0;
16154         endIndex = endIndex || ns.length - 1;
16155         for(var i = startIndex; i <= endIndex; i++){
16156             ns[i].nodeIndex = i;
16157         }
16158     },
16159
16160     /**
16161      * Changes the data store this view uses and refresh the view.
16162      * @param {Store} store
16163      */
16164     setStore : function(store, initial){
16165         if(!initial && this.store){
16166             this.store.un("datachanged", this.refresh);
16167             this.store.un("add", this.onAdd);
16168             this.store.un("remove", this.onRemove);
16169             this.store.un("update", this.onUpdate);
16170             this.store.un("clear", this.refresh);
16171             this.store.un("beforeload", this.onBeforeLoad);
16172             this.store.un("load", this.onLoad);
16173             this.store.un("loadexception", this.onLoad);
16174         }
16175         if(store){
16176           
16177             store.on("datachanged", this.refresh, this);
16178             store.on("add", this.onAdd, this);
16179             store.on("remove", this.onRemove, this);
16180             store.on("update", this.onUpdate, this);
16181             store.on("clear", this.refresh, this);
16182             store.on("beforeload", this.onBeforeLoad, this);
16183             store.on("load", this.onLoad, this);
16184             store.on("loadexception", this.onLoad, this);
16185         }
16186         
16187         if(store){
16188             this.refresh();
16189         }
16190     },
16191     /**
16192      * onbeforeLoad - masks the loading area.
16193      *
16194      */
16195     onBeforeLoad : function(store,opts)
16196     {
16197          //Roo.log('onBeforeLoad');   
16198         if (!opts.add) {
16199             this.el.update("");
16200         }
16201         this.el.mask(this.mask ? this.mask : "Loading" ); 
16202     },
16203     onLoad : function ()
16204     {
16205         this.el.unmask();
16206     },
16207     
16208
16209     /**
16210      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16211      * @param {HTMLElement} node
16212      * @return {HTMLElement} The template node
16213      */
16214     findItemFromChild : function(node){
16215         var el = this.dataName  ?
16216             this.el.child('.roo-tpl-' + this.dataName,true) :
16217             this.el.dom; 
16218         
16219         if(!node || node.parentNode == el){
16220                     return node;
16221             }
16222             var p = node.parentNode;
16223             while(p && p != el){
16224             if(p.parentNode == el){
16225                 return p;
16226             }
16227             p = p.parentNode;
16228         }
16229             return null;
16230     },
16231
16232     /** @ignore */
16233     onClick : function(e){
16234         var item = this.findItemFromChild(e.getTarget());
16235         if(item){
16236             var index = this.indexOf(item);
16237             if(this.onItemClick(item, index, e) !== false){
16238                 this.fireEvent("click", this, index, item, e);
16239             }
16240         }else{
16241             this.clearSelections();
16242         }
16243     },
16244
16245     /** @ignore */
16246     onContextMenu : function(e){
16247         var item = this.findItemFromChild(e.getTarget());
16248         if(item){
16249             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16250         }
16251     },
16252
16253     /** @ignore */
16254     onDblClick : function(e){
16255         var item = this.findItemFromChild(e.getTarget());
16256         if(item){
16257             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16258         }
16259     },
16260
16261     onItemClick : function(item, index, e)
16262     {
16263         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16264             return false;
16265         }
16266         if (this.toggleSelect) {
16267             var m = this.isSelected(item) ? 'unselect' : 'select';
16268             //Roo.log(m);
16269             var _t = this;
16270             _t[m](item, true, false);
16271             return true;
16272         }
16273         if(this.multiSelect || this.singleSelect){
16274             if(this.multiSelect && e.shiftKey && this.lastSelection){
16275                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16276             }else{
16277                 this.select(item, this.multiSelect && e.ctrlKey);
16278                 this.lastSelection = item;
16279             }
16280             
16281             if(!this.tickable){
16282                 e.preventDefault();
16283             }
16284             
16285         }
16286         return true;
16287     },
16288
16289     /**
16290      * Get the number of selected nodes.
16291      * @return {Number}
16292      */
16293     getSelectionCount : function(){
16294         return this.selections.length;
16295     },
16296
16297     /**
16298      * Get the currently selected nodes.
16299      * @return {Array} An array of HTMLElements
16300      */
16301     getSelectedNodes : function(){
16302         return this.selections;
16303     },
16304
16305     /**
16306      * Get the indexes of the selected nodes.
16307      * @return {Array}
16308      */
16309     getSelectedIndexes : function(){
16310         var indexes = [], s = this.selections;
16311         for(var i = 0, len = s.length; i < len; i++){
16312             indexes.push(s[i].nodeIndex);
16313         }
16314         return indexes;
16315     },
16316
16317     /**
16318      * Clear all selections
16319      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16320      */
16321     clearSelections : function(suppressEvent){
16322         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16323             this.cmp.elements = this.selections;
16324             this.cmp.removeClass(this.selectedClass);
16325             this.selections = [];
16326             if(!suppressEvent){
16327                 this.fireEvent("selectionchange", this, this.selections);
16328             }
16329         }
16330     },
16331
16332     /**
16333      * Returns true if the passed node is selected
16334      * @param {HTMLElement/Number} node The node or node index
16335      * @return {Boolean}
16336      */
16337     isSelected : function(node){
16338         var s = this.selections;
16339         if(s.length < 1){
16340             return false;
16341         }
16342         node = this.getNode(node);
16343         return s.indexOf(node) !== -1;
16344     },
16345
16346     /**
16347      * Selects nodes.
16348      * @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
16349      * @param {Boolean} keepExisting (optional) true to keep existing selections
16350      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16351      */
16352     select : function(nodeInfo, keepExisting, suppressEvent){
16353         if(nodeInfo instanceof Array){
16354             if(!keepExisting){
16355                 this.clearSelections(true);
16356             }
16357             for(var i = 0, len = nodeInfo.length; i < len; i++){
16358                 this.select(nodeInfo[i], true, true);
16359             }
16360             return;
16361         } 
16362         var node = this.getNode(nodeInfo);
16363         if(!node || this.isSelected(node)){
16364             return; // already selected.
16365         }
16366         if(!keepExisting){
16367             this.clearSelections(true);
16368         }
16369         
16370         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16371             Roo.fly(node).addClass(this.selectedClass);
16372             this.selections.push(node);
16373             if(!suppressEvent){
16374                 this.fireEvent("selectionchange", this, this.selections);
16375             }
16376         }
16377         
16378         
16379     },
16380       /**
16381      * Unselects nodes.
16382      * @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
16383      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16384      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16385      */
16386     unselect : function(nodeInfo, keepExisting, suppressEvent)
16387     {
16388         if(nodeInfo instanceof Array){
16389             Roo.each(this.selections, function(s) {
16390                 this.unselect(s, nodeInfo);
16391             }, this);
16392             return;
16393         }
16394         var node = this.getNode(nodeInfo);
16395         if(!node || !this.isSelected(node)){
16396             //Roo.log("not selected");
16397             return; // not selected.
16398         }
16399         // fireevent???
16400         var ns = [];
16401         Roo.each(this.selections, function(s) {
16402             if (s == node ) {
16403                 Roo.fly(node).removeClass(this.selectedClass);
16404
16405                 return;
16406             }
16407             ns.push(s);
16408         },this);
16409         
16410         this.selections= ns;
16411         this.fireEvent("selectionchange", this, this.selections);
16412     },
16413
16414     /**
16415      * Gets a template node.
16416      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16417      * @return {HTMLElement} The node or null if it wasn't found
16418      */
16419     getNode : function(nodeInfo){
16420         if(typeof nodeInfo == "string"){
16421             return document.getElementById(nodeInfo);
16422         }else if(typeof nodeInfo == "number"){
16423             return this.nodes[nodeInfo];
16424         }
16425         return nodeInfo;
16426     },
16427
16428     /**
16429      * Gets a range template nodes.
16430      * @param {Number} startIndex
16431      * @param {Number} endIndex
16432      * @return {Array} An array of nodes
16433      */
16434     getNodes : function(start, end){
16435         var ns = this.nodes;
16436         start = start || 0;
16437         end = typeof end == "undefined" ? ns.length - 1 : end;
16438         var nodes = [];
16439         if(start <= end){
16440             for(var i = start; i <= end; i++){
16441                 nodes.push(ns[i]);
16442             }
16443         } else{
16444             for(var i = start; i >= end; i--){
16445                 nodes.push(ns[i]);
16446             }
16447         }
16448         return nodes;
16449     },
16450
16451     /**
16452      * Finds the index of the passed node
16453      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16454      * @return {Number} The index of the node or -1
16455      */
16456     indexOf : function(node){
16457         node = this.getNode(node);
16458         if(typeof node.nodeIndex == "number"){
16459             return node.nodeIndex;
16460         }
16461         var ns = this.nodes;
16462         for(var i = 0, len = ns.length; i < len; i++){
16463             if(ns[i] == node){
16464                 return i;
16465             }
16466         }
16467         return -1;
16468     }
16469 });
16470 /*
16471  * - LGPL
16472  *
16473  * based on jquery fullcalendar
16474  * 
16475  */
16476
16477 Roo.bootstrap = Roo.bootstrap || {};
16478 /**
16479  * @class Roo.bootstrap.Calendar
16480  * @extends Roo.bootstrap.Component
16481  * Bootstrap Calendar class
16482  * @cfg {Boolean} loadMask (true|false) default false
16483  * @cfg {Object} header generate the user specific header of the calendar, default false
16484
16485  * @constructor
16486  * Create a new Container
16487  * @param {Object} config The config object
16488  */
16489
16490
16491
16492 Roo.bootstrap.Calendar = function(config){
16493     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16494      this.addEvents({
16495         /**
16496              * @event select
16497              * Fires when a date is selected
16498              * @param {DatePicker} this
16499              * @param {Date} date The selected date
16500              */
16501         'select': true,
16502         /**
16503              * @event monthchange
16504              * Fires when the displayed month changes 
16505              * @param {DatePicker} this
16506              * @param {Date} date The selected month
16507              */
16508         'monthchange': true,
16509         /**
16510              * @event evententer
16511              * Fires when mouse over an event
16512              * @param {Calendar} this
16513              * @param {event} Event
16514              */
16515         'evententer': true,
16516         /**
16517              * @event eventleave
16518              * Fires when the mouse leaves an
16519              * @param {Calendar} this
16520              * @param {event}
16521              */
16522         'eventleave': true,
16523         /**
16524              * @event eventclick
16525              * Fires when the mouse click an
16526              * @param {Calendar} this
16527              * @param {event}
16528              */
16529         'eventclick': true
16530         
16531     });
16532
16533 };
16534
16535 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16536     
16537      /**
16538      * @cfg {Number} startDay
16539      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16540      */
16541     startDay : 0,
16542     
16543     loadMask : false,
16544     
16545     header : false,
16546       
16547     getAutoCreate : function(){
16548         
16549         
16550         var fc_button = function(name, corner, style, content ) {
16551             return Roo.apply({},{
16552                 tag : 'span',
16553                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16554                          (corner.length ?
16555                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16556                             ''
16557                         ),
16558                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16559                 unselectable: 'on'
16560             });
16561         };
16562         
16563         var header = {};
16564         
16565         if(!this.header){
16566             header = {
16567                 tag : 'table',
16568                 cls : 'fc-header',
16569                 style : 'width:100%',
16570                 cn : [
16571                     {
16572                         tag: 'tr',
16573                         cn : [
16574                             {
16575                                 tag : 'td',
16576                                 cls : 'fc-header-left',
16577                                 cn : [
16578                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16579                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16580                                     { tag: 'span', cls: 'fc-header-space' },
16581                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16582
16583
16584                                 ]
16585                             },
16586
16587                             {
16588                                 tag : 'td',
16589                                 cls : 'fc-header-center',
16590                                 cn : [
16591                                     {
16592                                         tag: 'span',
16593                                         cls: 'fc-header-title',
16594                                         cn : {
16595                                             tag: 'H2',
16596                                             html : 'month / year'
16597                                         }
16598                                     }
16599
16600                                 ]
16601                             },
16602                             {
16603                                 tag : 'td',
16604                                 cls : 'fc-header-right',
16605                                 cn : [
16606                               /*      fc_button('month', 'left', '', 'month' ),
16607                                     fc_button('week', '', '', 'week' ),
16608                                     fc_button('day', 'right', '', 'day' )
16609                                 */    
16610
16611                                 ]
16612                             }
16613
16614                         ]
16615                     }
16616                 ]
16617             };
16618         }
16619         
16620         header = this.header;
16621         
16622        
16623         var cal_heads = function() {
16624             var ret = [];
16625             // fixme - handle this.
16626             
16627             for (var i =0; i < Date.dayNames.length; i++) {
16628                 var d = Date.dayNames[i];
16629                 ret.push({
16630                     tag: 'th',
16631                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16632                     html : d.substring(0,3)
16633                 });
16634                 
16635             }
16636             ret[0].cls += ' fc-first';
16637             ret[6].cls += ' fc-last';
16638             return ret;
16639         };
16640         var cal_cell = function(n) {
16641             return  {
16642                 tag: 'td',
16643                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16644                 cn : [
16645                     {
16646                         cn : [
16647                             {
16648                                 cls: 'fc-day-number',
16649                                 html: 'D'
16650                             },
16651                             {
16652                                 cls: 'fc-day-content',
16653                              
16654                                 cn : [
16655                                      {
16656                                         style: 'position: relative;' // height: 17px;
16657                                     }
16658                                 ]
16659                             }
16660                             
16661                             
16662                         ]
16663                     }
16664                 ]
16665                 
16666             }
16667         };
16668         var cal_rows = function() {
16669             
16670             var ret = [];
16671             for (var r = 0; r < 6; r++) {
16672                 var row= {
16673                     tag : 'tr',
16674                     cls : 'fc-week',
16675                     cn : []
16676                 };
16677                 
16678                 for (var i =0; i < Date.dayNames.length; i++) {
16679                     var d = Date.dayNames[i];
16680                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16681
16682                 }
16683                 row.cn[0].cls+=' fc-first';
16684                 row.cn[0].cn[0].style = 'min-height:90px';
16685                 row.cn[6].cls+=' fc-last';
16686                 ret.push(row);
16687                 
16688             }
16689             ret[0].cls += ' fc-first';
16690             ret[4].cls += ' fc-prev-last';
16691             ret[5].cls += ' fc-last';
16692             return ret;
16693             
16694         };
16695         
16696         var cal_table = {
16697             tag: 'table',
16698             cls: 'fc-border-separate',
16699             style : 'width:100%',
16700             cellspacing  : 0,
16701             cn : [
16702                 { 
16703                     tag: 'thead',
16704                     cn : [
16705                         { 
16706                             tag: 'tr',
16707                             cls : 'fc-first fc-last',
16708                             cn : cal_heads()
16709                         }
16710                     ]
16711                 },
16712                 { 
16713                     tag: 'tbody',
16714                     cn : cal_rows()
16715                 }
16716                   
16717             ]
16718         };
16719          
16720          var cfg = {
16721             cls : 'fc fc-ltr',
16722             cn : [
16723                 header,
16724                 {
16725                     cls : 'fc-content',
16726                     style : "position: relative;",
16727                     cn : [
16728                         {
16729                             cls : 'fc-view fc-view-month fc-grid',
16730                             style : 'position: relative',
16731                             unselectable : 'on',
16732                             cn : [
16733                                 {
16734                                     cls : 'fc-event-container',
16735                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16736                                 },
16737                                 cal_table
16738                             ]
16739                         }
16740                     ]
16741     
16742                 }
16743            ] 
16744             
16745         };
16746         
16747          
16748         
16749         return cfg;
16750     },
16751     
16752     
16753     initEvents : function()
16754     {
16755         if(!this.store){
16756             throw "can not find store for calendar";
16757         }
16758         
16759         var mark = {
16760             tag: "div",
16761             cls:"x-dlg-mask",
16762             style: "text-align:center",
16763             cn: [
16764                 {
16765                     tag: "div",
16766                     style: "background-color:white;width:50%;margin:250 auto",
16767                     cn: [
16768                         {
16769                             tag: "img",
16770                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16771                         },
16772                         {
16773                             tag: "span",
16774                             html: "Loading"
16775                         }
16776                         
16777                     ]
16778                 }
16779             ]
16780         };
16781         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16782         
16783         var size = this.el.select('.fc-content', true).first().getSize();
16784         this.maskEl.setSize(size.width, size.height);
16785         this.maskEl.enableDisplayMode("block");
16786         if(!this.loadMask){
16787             this.maskEl.hide();
16788         }
16789         
16790         this.store = Roo.factory(this.store, Roo.data);
16791         this.store.on('load', this.onLoad, this);
16792         this.store.on('beforeload', this.onBeforeLoad, this);
16793         
16794         this.resize();
16795         
16796         this.cells = this.el.select('.fc-day',true);
16797         //Roo.log(this.cells);
16798         this.textNodes = this.el.query('.fc-day-number');
16799         this.cells.addClassOnOver('fc-state-hover');
16800         
16801         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16802         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16803         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16804         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16805         
16806         this.on('monthchange', this.onMonthChange, this);
16807         
16808         this.update(new Date().clearTime());
16809     },
16810     
16811     resize : function() {
16812         var sz  = this.el.getSize();
16813         
16814         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16815         this.el.select('.fc-day-content div',true).setHeight(34);
16816     },
16817     
16818     
16819     // private
16820     showPrevMonth : function(e){
16821         this.update(this.activeDate.add("mo", -1));
16822     },
16823     showToday : function(e){
16824         this.update(new Date().clearTime());
16825     },
16826     // private
16827     showNextMonth : function(e){
16828         this.update(this.activeDate.add("mo", 1));
16829     },
16830
16831     // private
16832     showPrevYear : function(){
16833         this.update(this.activeDate.add("y", -1));
16834     },
16835
16836     // private
16837     showNextYear : function(){
16838         this.update(this.activeDate.add("y", 1));
16839     },
16840
16841     
16842    // private
16843     update : function(date)
16844     {
16845         var vd = this.activeDate;
16846         this.activeDate = date;
16847 //        if(vd && this.el){
16848 //            var t = date.getTime();
16849 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16850 //                Roo.log('using add remove');
16851 //                
16852 //                this.fireEvent('monthchange', this, date);
16853 //                
16854 //                this.cells.removeClass("fc-state-highlight");
16855 //                this.cells.each(function(c){
16856 //                   if(c.dateValue == t){
16857 //                       c.addClass("fc-state-highlight");
16858 //                       setTimeout(function(){
16859 //                            try{c.dom.firstChild.focus();}catch(e){}
16860 //                       }, 50);
16861 //                       return false;
16862 //                   }
16863 //                   return true;
16864 //                });
16865 //                return;
16866 //            }
16867 //        }
16868         
16869         var days = date.getDaysInMonth();
16870         
16871         var firstOfMonth = date.getFirstDateOfMonth();
16872         var startingPos = firstOfMonth.getDay()-this.startDay;
16873         
16874         if(startingPos < this.startDay){
16875             startingPos += 7;
16876         }
16877         
16878         var pm = date.add(Date.MONTH, -1);
16879         var prevStart = pm.getDaysInMonth()-startingPos;
16880 //        
16881         this.cells = this.el.select('.fc-day',true);
16882         this.textNodes = this.el.query('.fc-day-number');
16883         this.cells.addClassOnOver('fc-state-hover');
16884         
16885         var cells = this.cells.elements;
16886         var textEls = this.textNodes;
16887         
16888         Roo.each(cells, function(cell){
16889             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16890         });
16891         
16892         days += startingPos;
16893
16894         // convert everything to numbers so it's fast
16895         var day = 86400000;
16896         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16897         //Roo.log(d);
16898         //Roo.log(pm);
16899         //Roo.log(prevStart);
16900         
16901         var today = new Date().clearTime().getTime();
16902         var sel = date.clearTime().getTime();
16903         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16904         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16905         var ddMatch = this.disabledDatesRE;
16906         var ddText = this.disabledDatesText;
16907         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16908         var ddaysText = this.disabledDaysText;
16909         var format = this.format;
16910         
16911         var setCellClass = function(cal, cell){
16912             cell.row = 0;
16913             cell.events = [];
16914             cell.more = [];
16915             //Roo.log('set Cell Class');
16916             cell.title = "";
16917             var t = d.getTime();
16918             
16919             //Roo.log(d);
16920             
16921             cell.dateValue = t;
16922             if(t == today){
16923                 cell.className += " fc-today";
16924                 cell.className += " fc-state-highlight";
16925                 cell.title = cal.todayText;
16926             }
16927             if(t == sel){
16928                 // disable highlight in other month..
16929                 //cell.className += " fc-state-highlight";
16930                 
16931             }
16932             // disabling
16933             if(t < min) {
16934                 cell.className = " fc-state-disabled";
16935                 cell.title = cal.minText;
16936                 return;
16937             }
16938             if(t > max) {
16939                 cell.className = " fc-state-disabled";
16940                 cell.title = cal.maxText;
16941                 return;
16942             }
16943             if(ddays){
16944                 if(ddays.indexOf(d.getDay()) != -1){
16945                     cell.title = ddaysText;
16946                     cell.className = " fc-state-disabled";
16947                 }
16948             }
16949             if(ddMatch && format){
16950                 var fvalue = d.dateFormat(format);
16951                 if(ddMatch.test(fvalue)){
16952                     cell.title = ddText.replace("%0", fvalue);
16953                     cell.className = " fc-state-disabled";
16954                 }
16955             }
16956             
16957             if (!cell.initialClassName) {
16958                 cell.initialClassName = cell.dom.className;
16959             }
16960             
16961             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16962         };
16963
16964         var i = 0;
16965         
16966         for(; i < startingPos; i++) {
16967             textEls[i].innerHTML = (++prevStart);
16968             d.setDate(d.getDate()+1);
16969             
16970             cells[i].className = "fc-past fc-other-month";
16971             setCellClass(this, cells[i]);
16972         }
16973         
16974         var intDay = 0;
16975         
16976         for(; i < days; i++){
16977             intDay = i - startingPos + 1;
16978             textEls[i].innerHTML = (intDay);
16979             d.setDate(d.getDate()+1);
16980             
16981             cells[i].className = ''; // "x-date-active";
16982             setCellClass(this, cells[i]);
16983         }
16984         var extraDays = 0;
16985         
16986         for(; i < 42; i++) {
16987             textEls[i].innerHTML = (++extraDays);
16988             d.setDate(d.getDate()+1);
16989             
16990             cells[i].className = "fc-future fc-other-month";
16991             setCellClass(this, cells[i]);
16992         }
16993         
16994         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16995         
16996         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16997         
16998         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16999         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17000         
17001         if(totalRows != 6){
17002             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17003             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17004         }
17005         
17006         this.fireEvent('monthchange', this, date);
17007         
17008         
17009         /*
17010         if(!this.internalRender){
17011             var main = this.el.dom.firstChild;
17012             var w = main.offsetWidth;
17013             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17014             Roo.fly(main).setWidth(w);
17015             this.internalRender = true;
17016             // opera does not respect the auto grow header center column
17017             // then, after it gets a width opera refuses to recalculate
17018             // without a second pass
17019             if(Roo.isOpera && !this.secondPass){
17020                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17021                 this.secondPass = true;
17022                 this.update.defer(10, this, [date]);
17023             }
17024         }
17025         */
17026         
17027     },
17028     
17029     findCell : function(dt) {
17030         dt = dt.clearTime().getTime();
17031         var ret = false;
17032         this.cells.each(function(c){
17033             //Roo.log("check " +c.dateValue + '?=' + dt);
17034             if(c.dateValue == dt){
17035                 ret = c;
17036                 return false;
17037             }
17038             return true;
17039         });
17040         
17041         return ret;
17042     },
17043     
17044     findCells : function(ev) {
17045         var s = ev.start.clone().clearTime().getTime();
17046        // Roo.log(s);
17047         var e= ev.end.clone().clearTime().getTime();
17048        // Roo.log(e);
17049         var ret = [];
17050         this.cells.each(function(c){
17051              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17052             
17053             if(c.dateValue > e){
17054                 return ;
17055             }
17056             if(c.dateValue < s){
17057                 return ;
17058             }
17059             ret.push(c);
17060         });
17061         
17062         return ret;    
17063     },
17064     
17065 //    findBestRow: function(cells)
17066 //    {
17067 //        var ret = 0;
17068 //        
17069 //        for (var i =0 ; i < cells.length;i++) {
17070 //            ret  = Math.max(cells[i].rows || 0,ret);
17071 //        }
17072 //        return ret;
17073 //        
17074 //    },
17075     
17076     
17077     addItem : function(ev)
17078     {
17079         // look for vertical location slot in
17080         var cells = this.findCells(ev);
17081         
17082 //        ev.row = this.findBestRow(cells);
17083         
17084         // work out the location.
17085         
17086         var crow = false;
17087         var rows = [];
17088         for(var i =0; i < cells.length; i++) {
17089             
17090             cells[i].row = cells[0].row;
17091             
17092             if(i == 0){
17093                 cells[i].row = cells[i].row + 1;
17094             }
17095             
17096             if (!crow) {
17097                 crow = {
17098                     start : cells[i],
17099                     end :  cells[i]
17100                 };
17101                 continue;
17102             }
17103             if (crow.start.getY() == cells[i].getY()) {
17104                 // on same row.
17105                 crow.end = cells[i];
17106                 continue;
17107             }
17108             // different row.
17109             rows.push(crow);
17110             crow = {
17111                 start: cells[i],
17112                 end : cells[i]
17113             };
17114             
17115         }
17116         
17117         rows.push(crow);
17118         ev.els = [];
17119         ev.rows = rows;
17120         ev.cells = cells;
17121         
17122         cells[0].events.push(ev);
17123         
17124         this.calevents.push(ev);
17125     },
17126     
17127     clearEvents: function() {
17128         
17129         if(!this.calevents){
17130             return;
17131         }
17132         
17133         Roo.each(this.cells.elements, function(c){
17134             c.row = 0;
17135             c.events = [];
17136             c.more = [];
17137         });
17138         
17139         Roo.each(this.calevents, function(e) {
17140             Roo.each(e.els, function(el) {
17141                 el.un('mouseenter' ,this.onEventEnter, this);
17142                 el.un('mouseleave' ,this.onEventLeave, this);
17143                 el.remove();
17144             },this);
17145         },this);
17146         
17147         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17148             e.remove();
17149         });
17150         
17151     },
17152     
17153     renderEvents: function()
17154     {   
17155         var _this = this;
17156         
17157         this.cells.each(function(c) {
17158             
17159             if(c.row < 5){
17160                 return;
17161             }
17162             
17163             var ev = c.events;
17164             
17165             var r = 4;
17166             if(c.row != c.events.length){
17167                 r = 4 - (4 - (c.row - c.events.length));
17168             }
17169             
17170             c.events = ev.slice(0, r);
17171             c.more = ev.slice(r);
17172             
17173             if(c.more.length && c.more.length == 1){
17174                 c.events.push(c.more.pop());
17175             }
17176             
17177             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17178             
17179         });
17180             
17181         this.cells.each(function(c) {
17182             
17183             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17184             
17185             
17186             for (var e = 0; e < c.events.length; e++){
17187                 var ev = c.events[e];
17188                 var rows = ev.rows;
17189                 
17190                 for(var i = 0; i < rows.length; i++) {
17191                 
17192                     // how many rows should it span..
17193
17194                     var  cfg = {
17195                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17196                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17197
17198                         unselectable : "on",
17199                         cn : [
17200                             {
17201                                 cls: 'fc-event-inner',
17202                                 cn : [
17203     //                                {
17204     //                                  tag:'span',
17205     //                                  cls: 'fc-event-time',
17206     //                                  html : cells.length > 1 ? '' : ev.time
17207     //                                },
17208                                     {
17209                                       tag:'span',
17210                                       cls: 'fc-event-title',
17211                                       html : String.format('{0}', ev.title)
17212                                     }
17213
17214
17215                                 ]
17216                             },
17217                             {
17218                                 cls: 'ui-resizable-handle ui-resizable-e',
17219                                 html : '&nbsp;&nbsp;&nbsp'
17220                             }
17221
17222                         ]
17223                     };
17224
17225                     if (i == 0) {
17226                         cfg.cls += ' fc-event-start';
17227                     }
17228                     if ((i+1) == rows.length) {
17229                         cfg.cls += ' fc-event-end';
17230                     }
17231
17232                     var ctr = _this.el.select('.fc-event-container',true).first();
17233                     var cg = ctr.createChild(cfg);
17234
17235                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17236                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17237
17238                     var r = (c.more.length) ? 1 : 0;
17239                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17240                     cg.setWidth(ebox.right - sbox.x -2);
17241
17242                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17243                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17244                     cg.on('click', _this.onEventClick, _this, ev);
17245
17246                     ev.els.push(cg);
17247                     
17248                 }
17249                 
17250             }
17251             
17252             
17253             if(c.more.length){
17254                 var  cfg = {
17255                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17256                     style : 'position: absolute',
17257                     unselectable : "on",
17258                     cn : [
17259                         {
17260                             cls: 'fc-event-inner',
17261                             cn : [
17262                                 {
17263                                   tag:'span',
17264                                   cls: 'fc-event-title',
17265                                   html : 'More'
17266                                 }
17267
17268
17269                             ]
17270                         },
17271                         {
17272                             cls: 'ui-resizable-handle ui-resizable-e',
17273                             html : '&nbsp;&nbsp;&nbsp'
17274                         }
17275
17276                     ]
17277                 };
17278
17279                 var ctr = _this.el.select('.fc-event-container',true).first();
17280                 var cg = ctr.createChild(cfg);
17281
17282                 var sbox = c.select('.fc-day-content',true).first().getBox();
17283                 var ebox = c.select('.fc-day-content',true).first().getBox();
17284                 //Roo.log(cg);
17285                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17286                 cg.setWidth(ebox.right - sbox.x -2);
17287
17288                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17289                 
17290             }
17291             
17292         });
17293         
17294         
17295         
17296     },
17297     
17298     onEventEnter: function (e, el,event,d) {
17299         this.fireEvent('evententer', this, el, event);
17300     },
17301     
17302     onEventLeave: function (e, el,event,d) {
17303         this.fireEvent('eventleave', this, el, event);
17304     },
17305     
17306     onEventClick: function (e, el,event,d) {
17307         this.fireEvent('eventclick', this, el, event);
17308     },
17309     
17310     onMonthChange: function () {
17311         this.store.load();
17312     },
17313     
17314     onMoreEventClick: function(e, el, more)
17315     {
17316         var _this = this;
17317         
17318         this.calpopover.placement = 'right';
17319         this.calpopover.setTitle('More');
17320         
17321         this.calpopover.setContent('');
17322         
17323         var ctr = this.calpopover.el.select('.popover-content', true).first();
17324         
17325         Roo.each(more, function(m){
17326             var cfg = {
17327                 cls : 'fc-event-hori fc-event-draggable',
17328                 html : m.title
17329             };
17330             var cg = ctr.createChild(cfg);
17331             
17332             cg.on('click', _this.onEventClick, _this, m);
17333         });
17334         
17335         this.calpopover.show(el);
17336         
17337         
17338     },
17339     
17340     onLoad: function () 
17341     {   
17342         this.calevents = [];
17343         var cal = this;
17344         
17345         if(this.store.getCount() > 0){
17346             this.store.data.each(function(d){
17347                cal.addItem({
17348                     id : d.data.id,
17349                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17350                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17351                     time : d.data.start_time,
17352                     title : d.data.title,
17353                     description : d.data.description,
17354                     venue : d.data.venue
17355                 });
17356             });
17357         }
17358         
17359         this.renderEvents();
17360         
17361         if(this.calevents.length && this.loadMask){
17362             this.maskEl.hide();
17363         }
17364     },
17365     
17366     onBeforeLoad: function()
17367     {
17368         this.clearEvents();
17369         if(this.loadMask){
17370             this.maskEl.show();
17371         }
17372     }
17373 });
17374
17375  
17376  /*
17377  * - LGPL
17378  *
17379  * element
17380  * 
17381  */
17382
17383 /**
17384  * @class Roo.bootstrap.Popover
17385  * @extends Roo.bootstrap.Component
17386  * Bootstrap Popover class
17387  * @cfg {String} html contents of the popover   (or false to use children..)
17388  * @cfg {String} title of popover (or false to hide)
17389  * @cfg {String} placement how it is placed
17390  * @cfg {String} trigger click || hover (or false to trigger manually)
17391  * @cfg {String} over what (parent or false to trigger manually.)
17392  * @cfg {Number} delay - delay before showing
17393  
17394  * @constructor
17395  * Create a new Popover
17396  * @param {Object} config The config object
17397  */
17398
17399 Roo.bootstrap.Popover = function(config){
17400     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17401     
17402     this.addEvents({
17403         // raw events
17404          /**
17405          * @event show
17406          * After the popover show
17407          * 
17408          * @param {Roo.bootstrap.Popover} this
17409          */
17410         "show" : true,
17411         /**
17412          * @event hide
17413          * After the popover hide
17414          * 
17415          * @param {Roo.bootstrap.Popover} this
17416          */
17417         "hide" : true
17418     });
17419 };
17420
17421 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17422     
17423     title: 'Fill in a title',
17424     html: false,
17425     
17426     placement : 'right',
17427     trigger : 'hover', // hover
17428     
17429     delay : 0,
17430     
17431     over: 'parent',
17432     
17433     can_build_overlaid : false,
17434     
17435     getChildContainer : function()
17436     {
17437         return this.el.select('.popover-content',true).first();
17438     },
17439     
17440     getAutoCreate : function(){
17441          
17442         var cfg = {
17443            cls : 'popover roo-dynamic',
17444            style: 'display:block',
17445            cn : [
17446                 {
17447                     cls : 'arrow'
17448                 },
17449                 {
17450                     cls : 'popover-inner',
17451                     cn : [
17452                         {
17453                             tag: 'h3',
17454                             cls: 'popover-title',
17455                             html : this.title
17456                         },
17457                         {
17458                             cls : 'popover-content',
17459                             html : this.html
17460                         }
17461                     ]
17462                     
17463                 }
17464            ]
17465         };
17466         
17467         return cfg;
17468     },
17469     setTitle: function(str)
17470     {
17471         this.title = str;
17472         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17473     },
17474     setContent: function(str)
17475     {
17476         this.html = str;
17477         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17478     },
17479     // as it get's added to the bottom of the page.
17480     onRender : function(ct, position)
17481     {
17482         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17483         if(!this.el){
17484             var cfg = Roo.apply({},  this.getAutoCreate());
17485             cfg.id = Roo.id();
17486             
17487             if (this.cls) {
17488                 cfg.cls += ' ' + this.cls;
17489             }
17490             if (this.style) {
17491                 cfg.style = this.style;
17492             }
17493             //Roo.log("adding to ");
17494             this.el = Roo.get(document.body).createChild(cfg, position);
17495 //            Roo.log(this.el);
17496         }
17497         this.initEvents();
17498     },
17499     
17500     initEvents : function()
17501     {
17502         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17503         this.el.enableDisplayMode('block');
17504         this.el.hide();
17505         if (this.over === false) {
17506             return; 
17507         }
17508         if (this.triggers === false) {
17509             return;
17510         }
17511         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17512         var triggers = this.trigger ? this.trigger.split(' ') : [];
17513         Roo.each(triggers, function(trigger) {
17514         
17515             if (trigger == 'click') {
17516                 on_el.on('click', this.toggle, this);
17517             } else if (trigger != 'manual') {
17518                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17519                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17520       
17521                 on_el.on(eventIn  ,this.enter, this);
17522                 on_el.on(eventOut, this.leave, this);
17523             }
17524         }, this);
17525         
17526     },
17527     
17528     
17529     // private
17530     timeout : null,
17531     hoverState : null,
17532     
17533     toggle : function () {
17534         this.hoverState == 'in' ? this.leave() : this.enter();
17535     },
17536     
17537     enter : function () {
17538         
17539         clearTimeout(this.timeout);
17540     
17541         this.hoverState = 'in';
17542     
17543         if (!this.delay || !this.delay.show) {
17544             this.show();
17545             return;
17546         }
17547         var _t = this;
17548         this.timeout = setTimeout(function () {
17549             if (_t.hoverState == 'in') {
17550                 _t.show();
17551             }
17552         }, this.delay.show)
17553     },
17554     
17555     leave : function() {
17556         clearTimeout(this.timeout);
17557     
17558         this.hoverState = 'out';
17559     
17560         if (!this.delay || !this.delay.hide) {
17561             this.hide();
17562             return;
17563         }
17564         var _t = this;
17565         this.timeout = setTimeout(function () {
17566             if (_t.hoverState == 'out') {
17567                 _t.hide();
17568             }
17569         }, this.delay.hide)
17570     },
17571     
17572     show : function (on_el)
17573     {
17574         if (!on_el) {
17575             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17576         }
17577         
17578         // set content.
17579         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17580         if (this.html !== false) {
17581             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17582         }
17583         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17584         if (!this.title.length) {
17585             this.el.select('.popover-title',true).hide();
17586         }
17587         
17588         var placement = typeof this.placement == 'function' ?
17589             this.placement.call(this, this.el, on_el) :
17590             this.placement;
17591             
17592         var autoToken = /\s?auto?\s?/i;
17593         var autoPlace = autoToken.test(placement);
17594         if (autoPlace) {
17595             placement = placement.replace(autoToken, '') || 'top';
17596         }
17597         
17598         //this.el.detach()
17599         //this.el.setXY([0,0]);
17600         this.el.show();
17601         this.el.dom.style.display='block';
17602         this.el.addClass(placement);
17603         
17604         //this.el.appendTo(on_el);
17605         
17606         var p = this.getPosition();
17607         var box = this.el.getBox();
17608         
17609         if (autoPlace) {
17610             // fixme..
17611         }
17612         var align = Roo.bootstrap.Popover.alignment[placement];
17613         
17614 //        Roo.log(align);
17615         this.el.alignTo(on_el, align[0],align[1]);
17616         //var arrow = this.el.select('.arrow',true).first();
17617         //arrow.set(align[2], 
17618         
17619         this.el.addClass('in');
17620         
17621         
17622         if (this.el.hasClass('fade')) {
17623             // fade it?
17624         }
17625         
17626         this.hoverState = 'in';
17627         
17628         this.fireEvent('show', this);
17629         
17630     },
17631     hide : function()
17632     {
17633         this.el.setXY([0,0]);
17634         this.el.removeClass('in');
17635         this.el.hide();
17636         this.hoverState = null;
17637         
17638         this.fireEvent('hide', this);
17639     }
17640     
17641 });
17642
17643 Roo.bootstrap.Popover.alignment = {
17644     'left' : ['r-l', [-10,0], 'right'],
17645     'right' : ['l-r', [10,0], 'left'],
17646     'bottom' : ['t-b', [0,10], 'top'],
17647     'top' : [ 'b-t', [0,-10], 'bottom']
17648 };
17649
17650  /*
17651  * - LGPL
17652  *
17653  * Progress
17654  * 
17655  */
17656
17657 /**
17658  * @class Roo.bootstrap.Progress
17659  * @extends Roo.bootstrap.Component
17660  * Bootstrap Progress class
17661  * @cfg {Boolean} striped striped of the progress bar
17662  * @cfg {Boolean} active animated of the progress bar
17663  * 
17664  * 
17665  * @constructor
17666  * Create a new Progress
17667  * @param {Object} config The config object
17668  */
17669
17670 Roo.bootstrap.Progress = function(config){
17671     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17672 };
17673
17674 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17675     
17676     striped : false,
17677     active: false,
17678     
17679     getAutoCreate : function(){
17680         var cfg = {
17681             tag: 'div',
17682             cls: 'progress'
17683         };
17684         
17685         
17686         if(this.striped){
17687             cfg.cls += ' progress-striped';
17688         }
17689       
17690         if(this.active){
17691             cfg.cls += ' active';
17692         }
17693         
17694         
17695         return cfg;
17696     }
17697    
17698 });
17699
17700  
17701
17702  /*
17703  * - LGPL
17704  *
17705  * ProgressBar
17706  * 
17707  */
17708
17709 /**
17710  * @class Roo.bootstrap.ProgressBar
17711  * @extends Roo.bootstrap.Component
17712  * Bootstrap ProgressBar class
17713  * @cfg {Number} aria_valuenow aria-value now
17714  * @cfg {Number} aria_valuemin aria-value min
17715  * @cfg {Number} aria_valuemax aria-value max
17716  * @cfg {String} label label for the progress bar
17717  * @cfg {String} panel (success | info | warning | danger )
17718  * @cfg {String} role role of the progress bar
17719  * @cfg {String} sr_only text
17720  * 
17721  * 
17722  * @constructor
17723  * Create a new ProgressBar
17724  * @param {Object} config The config object
17725  */
17726
17727 Roo.bootstrap.ProgressBar = function(config){
17728     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17729 };
17730
17731 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17732     
17733     aria_valuenow : 0,
17734     aria_valuemin : 0,
17735     aria_valuemax : 100,
17736     label : false,
17737     panel : false,
17738     role : false,
17739     sr_only: false,
17740     
17741     getAutoCreate : function()
17742     {
17743         
17744         var cfg = {
17745             tag: 'div',
17746             cls: 'progress-bar',
17747             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17748         };
17749         
17750         if(this.sr_only){
17751             cfg.cn = {
17752                 tag: 'span',
17753                 cls: 'sr-only',
17754                 html: this.sr_only
17755             }
17756         }
17757         
17758         if(this.role){
17759             cfg.role = this.role;
17760         }
17761         
17762         if(this.aria_valuenow){
17763             cfg['aria-valuenow'] = this.aria_valuenow;
17764         }
17765         
17766         if(this.aria_valuemin){
17767             cfg['aria-valuemin'] = this.aria_valuemin;
17768         }
17769         
17770         if(this.aria_valuemax){
17771             cfg['aria-valuemax'] = this.aria_valuemax;
17772         }
17773         
17774         if(this.label && !this.sr_only){
17775             cfg.html = this.label;
17776         }
17777         
17778         if(this.panel){
17779             cfg.cls += ' progress-bar-' + this.panel;
17780         }
17781         
17782         return cfg;
17783     },
17784     
17785     update : function(aria_valuenow)
17786     {
17787         this.aria_valuenow = aria_valuenow;
17788         
17789         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17790     }
17791    
17792 });
17793
17794  
17795
17796  /*
17797  * - LGPL
17798  *
17799  * column
17800  * 
17801  */
17802
17803 /**
17804  * @class Roo.bootstrap.TabGroup
17805  * @extends Roo.bootstrap.Column
17806  * Bootstrap Column class
17807  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17808  * @cfg {Boolean} carousel true to make the group behave like a carousel
17809  * @cfg {Boolean} bullets show bullets for the panels
17810  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17811  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17812  * @cfg {Boolean} showarrow (true|false) show arrow default true
17813  * 
17814  * @constructor
17815  * Create a new TabGroup
17816  * @param {Object} config The config object
17817  */
17818
17819 Roo.bootstrap.TabGroup = function(config){
17820     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17821     if (!this.navId) {
17822         this.navId = Roo.id();
17823     }
17824     this.tabs = [];
17825     Roo.bootstrap.TabGroup.register(this);
17826     
17827 };
17828
17829 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17830     
17831     carousel : false,
17832     transition : false,
17833     bullets : 0,
17834     timer : 0,
17835     autoslide : false,
17836     slideFn : false,
17837     slideOnTouch : false,
17838     showarrow : true,
17839     
17840     getAutoCreate : function()
17841     {
17842         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17843         
17844         cfg.cls += ' tab-content';
17845         
17846         if (this.carousel) {
17847             cfg.cls += ' carousel slide';
17848             
17849             cfg.cn = [{
17850                cls : 'carousel-inner',
17851                cn : []
17852             }];
17853         
17854             if(this.bullets  && !Roo.isTouch){
17855                 
17856                 var bullets = {
17857                     cls : 'carousel-bullets',
17858                     cn : []
17859                 };
17860                
17861                 if(this.bullets_cls){
17862                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17863                 }
17864                 
17865                 bullets.cn.push({
17866                     cls : 'clear'
17867                 });
17868                 
17869                 cfg.cn[0].cn.push(bullets);
17870             }
17871             
17872             if(this.showarrow){
17873                 cfg.cn[0].cn.push({
17874                     tag : 'div',
17875                     class : 'carousel-arrow',
17876                     cn : [
17877                         {
17878                             tag : 'div',
17879                             class : 'carousel-prev',
17880                             cn : [
17881                                 {
17882                                     tag : 'i',
17883                                     class : 'fa fa-chevron-left'
17884                                 }
17885                             ]
17886                         },
17887                         {
17888                             tag : 'div',
17889                             class : 'carousel-next',
17890                             cn : [
17891                                 {
17892                                     tag : 'i',
17893                                     class : 'fa fa-chevron-right'
17894                                 }
17895                             ]
17896                         }
17897                     ]
17898                 });
17899             }
17900             
17901         }
17902         
17903         return cfg;
17904     },
17905     
17906     initEvents:  function()
17907     {
17908 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17909 //            this.el.on("touchstart", this.onTouchStart, this);
17910 //        }
17911         
17912         if(this.autoslide){
17913             var _this = this;
17914             
17915             this.slideFn = window.setInterval(function() {
17916                 _this.showPanelNext();
17917             }, this.timer);
17918         }
17919         
17920         if(this.showarrow){
17921             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17922             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17923         }
17924         
17925         
17926     },
17927     
17928 //    onTouchStart : function(e, el, o)
17929 //    {
17930 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17931 //            return;
17932 //        }
17933 //        
17934 //        this.showPanelNext();
17935 //    },
17936     
17937     
17938     getChildContainer : function()
17939     {
17940         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17941     },
17942     
17943     /**
17944     * register a Navigation item
17945     * @param {Roo.bootstrap.NavItem} the navitem to add
17946     */
17947     register : function(item)
17948     {
17949         this.tabs.push( item);
17950         item.navId = this.navId; // not really needed..
17951         this.addBullet();
17952     
17953     },
17954     
17955     getActivePanel : function()
17956     {
17957         var r = false;
17958         Roo.each(this.tabs, function(t) {
17959             if (t.active) {
17960                 r = t;
17961                 return false;
17962             }
17963             return null;
17964         });
17965         return r;
17966         
17967     },
17968     getPanelByName : function(n)
17969     {
17970         var r = false;
17971         Roo.each(this.tabs, function(t) {
17972             if (t.tabId == n) {
17973                 r = t;
17974                 return false;
17975             }
17976             return null;
17977         });
17978         return r;
17979     },
17980     indexOfPanel : function(p)
17981     {
17982         var r = false;
17983         Roo.each(this.tabs, function(t,i) {
17984             if (t.tabId == p.tabId) {
17985                 r = i;
17986                 return false;
17987             }
17988             return null;
17989         });
17990         return r;
17991     },
17992     /**
17993      * show a specific panel
17994      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17995      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17996      */
17997     showPanel : function (pan)
17998     {
17999         if(this.transition || typeof(pan) == 'undefined'){
18000             Roo.log("waiting for the transitionend");
18001             return;
18002         }
18003         
18004         if (typeof(pan) == 'number') {
18005             pan = this.tabs[pan];
18006         }
18007         
18008         if (typeof(pan) == 'string') {
18009             pan = this.getPanelByName(pan);
18010         }
18011         
18012         var cur = this.getActivePanel();
18013         
18014         if(!pan || !cur){
18015             Roo.log('pan or acitve pan is undefined');
18016             return false;
18017         }
18018         
18019         if (pan.tabId == this.getActivePanel().tabId) {
18020             return true;
18021         }
18022         
18023         if (false === cur.fireEvent('beforedeactivate')) {
18024             return false;
18025         }
18026         
18027         if(this.bullets > 0 && !Roo.isTouch){
18028             this.setActiveBullet(this.indexOfPanel(pan));
18029         }
18030         
18031         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18032             
18033             this.transition = true;
18034             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18035             var lr = dir == 'next' ? 'left' : 'right';
18036             pan.el.addClass(dir); // or prev
18037             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18038             cur.el.addClass(lr); // or right
18039             pan.el.addClass(lr);
18040             
18041             var _this = this;
18042             cur.el.on('transitionend', function() {
18043                 Roo.log("trans end?");
18044                 
18045                 pan.el.removeClass([lr,dir]);
18046                 pan.setActive(true);
18047                 
18048                 cur.el.removeClass([lr]);
18049                 cur.setActive(false);
18050                 
18051                 _this.transition = false;
18052                 
18053             }, this, { single:  true } );
18054             
18055             return true;
18056         }
18057         
18058         cur.setActive(false);
18059         pan.setActive(true);
18060         
18061         return true;
18062         
18063     },
18064     showPanelNext : function()
18065     {
18066         var i = this.indexOfPanel(this.getActivePanel());
18067         
18068         if (i >= this.tabs.length - 1 && !this.autoslide) {
18069             return;
18070         }
18071         
18072         if (i >= this.tabs.length - 1 && this.autoslide) {
18073             i = -1;
18074         }
18075         
18076         this.showPanel(this.tabs[i+1]);
18077     },
18078     
18079     showPanelPrev : function()
18080     {
18081         var i = this.indexOfPanel(this.getActivePanel());
18082         
18083         if (i  < 1 && !this.autoslide) {
18084             return;
18085         }
18086         
18087         if (i < 1 && this.autoslide) {
18088             i = this.tabs.length;
18089         }
18090         
18091         this.showPanel(this.tabs[i-1]);
18092     },
18093     
18094     
18095     addBullet: function()
18096     {
18097         if(!this.bullets || Roo.isTouch){
18098             return;
18099         }
18100         var ctr = this.el.select('.carousel-bullets',true).first();
18101         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18102         var bullet = ctr.createChild({
18103             cls : 'bullet bullet-' + i
18104         },ctr.dom.lastChild);
18105         
18106         
18107         var _this = this;
18108         
18109         bullet.on('click', (function(e, el, o, ii, t){
18110
18111             e.preventDefault();
18112
18113             this.showPanel(ii);
18114
18115             if(this.autoslide && this.slideFn){
18116                 clearInterval(this.slideFn);
18117                 this.slideFn = window.setInterval(function() {
18118                     _this.showPanelNext();
18119                 }, this.timer);
18120             }
18121
18122         }).createDelegate(this, [i, bullet], true));
18123                 
18124         
18125     },
18126      
18127     setActiveBullet : function(i)
18128     {
18129         if(Roo.isTouch){
18130             return;
18131         }
18132         
18133         Roo.each(this.el.select('.bullet', true).elements, function(el){
18134             el.removeClass('selected');
18135         });
18136
18137         var bullet = this.el.select('.bullet-' + i, true).first();
18138         
18139         if(!bullet){
18140             return;
18141         }
18142         
18143         bullet.addClass('selected');
18144     }
18145     
18146     
18147   
18148 });
18149
18150  
18151
18152  
18153  
18154 Roo.apply(Roo.bootstrap.TabGroup, {
18155     
18156     groups: {},
18157      /**
18158     * register a Navigation Group
18159     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18160     */
18161     register : function(navgrp)
18162     {
18163         this.groups[navgrp.navId] = navgrp;
18164         
18165     },
18166     /**
18167     * fetch a Navigation Group based on the navigation ID
18168     * if one does not exist , it will get created.
18169     * @param {string} the navgroup to add
18170     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18171     */
18172     get: function(navId) {
18173         if (typeof(this.groups[navId]) == 'undefined') {
18174             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18175         }
18176         return this.groups[navId] ;
18177     }
18178     
18179     
18180     
18181 });
18182
18183  /*
18184  * - LGPL
18185  *
18186  * TabPanel
18187  * 
18188  */
18189
18190 /**
18191  * @class Roo.bootstrap.TabPanel
18192  * @extends Roo.bootstrap.Component
18193  * Bootstrap TabPanel class
18194  * @cfg {Boolean} active panel active
18195  * @cfg {String} html panel content
18196  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18197  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18198  * @cfg {String} href click to link..
18199  * 
18200  * 
18201  * @constructor
18202  * Create a new TabPanel
18203  * @param {Object} config The config object
18204  */
18205
18206 Roo.bootstrap.TabPanel = function(config){
18207     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18208     this.addEvents({
18209         /**
18210              * @event changed
18211              * Fires when the active status changes
18212              * @param {Roo.bootstrap.TabPanel} this
18213              * @param {Boolean} state the new state
18214             
18215          */
18216         'changed': true,
18217         /**
18218              * @event beforedeactivate
18219              * Fires before a tab is de-activated - can be used to do validation on a form.
18220              * @param {Roo.bootstrap.TabPanel} this
18221              * @return {Boolean} false if there is an error
18222             
18223          */
18224         'beforedeactivate': true
18225      });
18226     
18227     this.tabId = this.tabId || Roo.id();
18228   
18229 };
18230
18231 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18232     
18233     active: false,
18234     html: false,
18235     tabId: false,
18236     navId : false,
18237     href : '',
18238     
18239     getAutoCreate : function(){
18240         var cfg = {
18241             tag: 'div',
18242             // item is needed for carousel - not sure if it has any effect otherwise
18243             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18244             html: this.html || ''
18245         };
18246         
18247         if(this.active){
18248             cfg.cls += ' active';
18249         }
18250         
18251         if(this.tabId){
18252             cfg.tabId = this.tabId;
18253         }
18254         
18255         
18256         return cfg;
18257     },
18258     
18259     initEvents:  function()
18260     {
18261         var p = this.parent();
18262         
18263         this.navId = this.navId || p.navId;
18264         
18265         if (typeof(this.navId) != 'undefined') {
18266             // not really needed.. but just in case.. parent should be a NavGroup.
18267             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18268             
18269             tg.register(this);
18270             
18271             var i = tg.tabs.length - 1;
18272             
18273             if(this.active && tg.bullets > 0 && i < tg.bullets){
18274                 tg.setActiveBullet(i);
18275             }
18276         }
18277         
18278         this.el.on('click', this.onClick, this);
18279         
18280         if(Roo.isTouch){
18281             this.el.on("touchstart", this.onTouchStart, this);
18282             this.el.on("touchmove", this.onTouchMove, this);
18283             this.el.on("touchend", this.onTouchEnd, this);
18284         }
18285         
18286     },
18287     
18288     onRender : function(ct, position)
18289     {
18290         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18291     },
18292     
18293     setActive : function(state)
18294     {
18295         Roo.log("panel - set active " + this.tabId + "=" + state);
18296         
18297         this.active = state;
18298         if (!state) {
18299             this.el.removeClass('active');
18300             
18301         } else  if (!this.el.hasClass('active')) {
18302             this.el.addClass('active');
18303         }
18304         
18305         this.fireEvent('changed', this, state);
18306     },
18307     
18308     onClick : function(e)
18309     {
18310         e.preventDefault();
18311         
18312         if(!this.href.length){
18313             return;
18314         }
18315         
18316         window.location.href = this.href;
18317     },
18318     
18319     startX : 0,
18320     startY : 0,
18321     endX : 0,
18322     endY : 0,
18323     swiping : false,
18324     
18325     onTouchStart : function(e)
18326     {
18327         this.swiping = false;
18328         
18329         this.startX = e.browserEvent.touches[0].clientX;
18330         this.startY = e.browserEvent.touches[0].clientY;
18331     },
18332     
18333     onTouchMove : function(e)
18334     {
18335         this.swiping = true;
18336         
18337         this.endX = e.browserEvent.touches[0].clientX;
18338         this.endY = e.browserEvent.touches[0].clientY;
18339     },
18340     
18341     onTouchEnd : function(e)
18342     {
18343         if(!this.swiping){
18344             this.onClick(e);
18345             return;
18346         }
18347         
18348         var tabGroup = this.parent();
18349         
18350         if(this.endX > this.startX){ // swiping right
18351             tabGroup.showPanelPrev();
18352             return;
18353         }
18354         
18355         if(this.startX > this.endX){ // swiping left
18356             tabGroup.showPanelNext();
18357             return;
18358         }
18359     }
18360     
18361     
18362 });
18363  
18364
18365  
18366
18367  /*
18368  * - LGPL
18369  *
18370  * DateField
18371  * 
18372  */
18373
18374 /**
18375  * @class Roo.bootstrap.DateField
18376  * @extends Roo.bootstrap.Input
18377  * Bootstrap DateField class
18378  * @cfg {Number} weekStart default 0
18379  * @cfg {String} viewMode default empty, (months|years)
18380  * @cfg {String} minViewMode default empty, (months|years)
18381  * @cfg {Number} startDate default -Infinity
18382  * @cfg {Number} endDate default Infinity
18383  * @cfg {Boolean} todayHighlight default false
18384  * @cfg {Boolean} todayBtn default false
18385  * @cfg {Boolean} calendarWeeks default false
18386  * @cfg {Object} daysOfWeekDisabled default empty
18387  * @cfg {Boolean} singleMode default false (true | false)
18388  * 
18389  * @cfg {Boolean} keyboardNavigation default true
18390  * @cfg {String} language default en
18391  * 
18392  * @constructor
18393  * Create a new DateField
18394  * @param {Object} config The config object
18395  */
18396
18397 Roo.bootstrap.DateField = function(config){
18398     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18399      this.addEvents({
18400             /**
18401              * @event show
18402              * Fires when this field show.
18403              * @param {Roo.bootstrap.DateField} this
18404              * @param {Mixed} date The date value
18405              */
18406             show : true,
18407             /**
18408              * @event show
18409              * Fires when this field hide.
18410              * @param {Roo.bootstrap.DateField} this
18411              * @param {Mixed} date The date value
18412              */
18413             hide : true,
18414             /**
18415              * @event select
18416              * Fires when select a date.
18417              * @param {Roo.bootstrap.DateField} this
18418              * @param {Mixed} date The date value
18419              */
18420             select : true,
18421             /**
18422              * @event beforeselect
18423              * Fires when before select a date.
18424              * @param {Roo.bootstrap.DateField} this
18425              * @param {Mixed} date The date value
18426              */
18427             beforeselect : true
18428         });
18429 };
18430
18431 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18432     
18433     /**
18434      * @cfg {String} format
18435      * The default date format string which can be overriden for localization support.  The format must be
18436      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18437      */
18438     format : "m/d/y",
18439     /**
18440      * @cfg {String} altFormats
18441      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18442      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18443      */
18444     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18445     
18446     weekStart : 0,
18447     
18448     viewMode : '',
18449     
18450     minViewMode : '',
18451     
18452     todayHighlight : false,
18453     
18454     todayBtn: false,
18455     
18456     language: 'en',
18457     
18458     keyboardNavigation: true,
18459     
18460     calendarWeeks: false,
18461     
18462     startDate: -Infinity,
18463     
18464     endDate: Infinity,
18465     
18466     daysOfWeekDisabled: [],
18467     
18468     _events: [],
18469     
18470     singleMode : false,
18471     
18472     UTCDate: function()
18473     {
18474         return new Date(Date.UTC.apply(Date, arguments));
18475     },
18476     
18477     UTCToday: function()
18478     {
18479         var today = new Date();
18480         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18481     },
18482     
18483     getDate: function() {
18484             var d = this.getUTCDate();
18485             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18486     },
18487     
18488     getUTCDate: function() {
18489             return this.date;
18490     },
18491     
18492     setDate: function(d) {
18493             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18494     },
18495     
18496     setUTCDate: function(d) {
18497             this.date = d;
18498             this.setValue(this.formatDate(this.date));
18499     },
18500         
18501     onRender: function(ct, position)
18502     {
18503         
18504         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18505         
18506         this.language = this.language || 'en';
18507         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18508         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18509         
18510         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18511         this.format = this.format || 'm/d/y';
18512         this.isInline = false;
18513         this.isInput = true;
18514         this.component = this.el.select('.add-on', true).first() || false;
18515         this.component = (this.component && this.component.length === 0) ? false : this.component;
18516         this.hasInput = this.component && this.inputEl().length;
18517         
18518         if (typeof(this.minViewMode === 'string')) {
18519             switch (this.minViewMode) {
18520                 case 'months':
18521                     this.minViewMode = 1;
18522                     break;
18523                 case 'years':
18524                     this.minViewMode = 2;
18525                     break;
18526                 default:
18527                     this.minViewMode = 0;
18528                     break;
18529             }
18530         }
18531         
18532         if (typeof(this.viewMode === 'string')) {
18533             switch (this.viewMode) {
18534                 case 'months':
18535                     this.viewMode = 1;
18536                     break;
18537                 case 'years':
18538                     this.viewMode = 2;
18539                     break;
18540                 default:
18541                     this.viewMode = 0;
18542                     break;
18543             }
18544         }
18545                 
18546         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18547         
18548 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18549         
18550         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18551         
18552         this.picker().on('mousedown', this.onMousedown, this);
18553         this.picker().on('click', this.onClick, this);
18554         
18555         this.picker().addClass('datepicker-dropdown');
18556         
18557         this.startViewMode = this.viewMode;
18558         
18559         if(this.singleMode){
18560             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18561                 v.setVisibilityMode(Roo.Element.DISPLAY);
18562                 v.hide();
18563             });
18564             
18565             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18566                 v.setStyle('width', '189px');
18567             });
18568         }
18569         
18570         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18571             if(!this.calendarWeeks){
18572                 v.remove();
18573                 return;
18574             }
18575             
18576             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18577             v.attr('colspan', function(i, val){
18578                 return parseInt(val) + 1;
18579             });
18580         });
18581                         
18582         
18583         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18584         
18585         this.setStartDate(this.startDate);
18586         this.setEndDate(this.endDate);
18587         
18588         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18589         
18590         this.fillDow();
18591         this.fillMonths();
18592         this.update();
18593         this.showMode();
18594         
18595         if(this.isInline) {
18596             this.showPopup();
18597         }
18598     },
18599     
18600     picker : function()
18601     {
18602         return this.pickerEl;
18603 //        return this.el.select('.datepicker', true).first();
18604     },
18605     
18606     fillDow: function()
18607     {
18608         var dowCnt = this.weekStart;
18609         
18610         var dow = {
18611             tag: 'tr',
18612             cn: [
18613                 
18614             ]
18615         };
18616         
18617         if(this.calendarWeeks){
18618             dow.cn.push({
18619                 tag: 'th',
18620                 cls: 'cw',
18621                 html: '&nbsp;'
18622             })
18623         }
18624         
18625         while (dowCnt < this.weekStart + 7) {
18626             dow.cn.push({
18627                 tag: 'th',
18628                 cls: 'dow',
18629                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18630             });
18631         }
18632         
18633         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18634     },
18635     
18636     fillMonths: function()
18637     {    
18638         var i = 0;
18639         var months = this.picker().select('>.datepicker-months td', true).first();
18640         
18641         months.dom.innerHTML = '';
18642         
18643         while (i < 12) {
18644             var month = {
18645                 tag: 'span',
18646                 cls: 'month',
18647                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18648             };
18649             
18650             months.createChild(month);
18651         }
18652         
18653     },
18654     
18655     update: function()
18656     {
18657         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;
18658         
18659         if (this.date < this.startDate) {
18660             this.viewDate = new Date(this.startDate);
18661         } else if (this.date > this.endDate) {
18662             this.viewDate = new Date(this.endDate);
18663         } else {
18664             this.viewDate = new Date(this.date);
18665         }
18666         
18667         this.fill();
18668     },
18669     
18670     fill: function() 
18671     {
18672         var d = new Date(this.viewDate),
18673                 year = d.getUTCFullYear(),
18674                 month = d.getUTCMonth(),
18675                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18676                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18677                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18678                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18679                 currentDate = this.date && this.date.valueOf(),
18680                 today = this.UTCToday();
18681         
18682         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18683         
18684 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18685         
18686 //        this.picker.select('>tfoot th.today').
18687 //                                              .text(dates[this.language].today)
18688 //                                              .toggle(this.todayBtn !== false);
18689     
18690         this.updateNavArrows();
18691         this.fillMonths();
18692                                                 
18693         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18694         
18695         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18696          
18697         prevMonth.setUTCDate(day);
18698         
18699         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18700         
18701         var nextMonth = new Date(prevMonth);
18702         
18703         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18704         
18705         nextMonth = nextMonth.valueOf();
18706         
18707         var fillMonths = false;
18708         
18709         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18710         
18711         while(prevMonth.valueOf() <= nextMonth) {
18712             var clsName = '';
18713             
18714             if (prevMonth.getUTCDay() === this.weekStart) {
18715                 if(fillMonths){
18716                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18717                 }
18718                     
18719                 fillMonths = {
18720                     tag: 'tr',
18721                     cn: []
18722                 };
18723                 
18724                 if(this.calendarWeeks){
18725                     // ISO 8601: First week contains first thursday.
18726                     // ISO also states week starts on Monday, but we can be more abstract here.
18727                     var
18728                     // Start of current week: based on weekstart/current date
18729                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18730                     // Thursday of this week
18731                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18732                     // First Thursday of year, year from thursday
18733                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18734                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18735                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18736                     
18737                     fillMonths.cn.push({
18738                         tag: 'td',
18739                         cls: 'cw',
18740                         html: calWeek
18741                     });
18742                 }
18743             }
18744             
18745             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18746                 clsName += ' old';
18747             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18748                 clsName += ' new';
18749             }
18750             if (this.todayHighlight &&
18751                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18752                 prevMonth.getUTCMonth() == today.getMonth() &&
18753                 prevMonth.getUTCDate() == today.getDate()) {
18754                 clsName += ' today';
18755             }
18756             
18757             if (currentDate && prevMonth.valueOf() === currentDate) {
18758                 clsName += ' active';
18759             }
18760             
18761             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18762                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18763                     clsName += ' disabled';
18764             }
18765             
18766             fillMonths.cn.push({
18767                 tag: 'td',
18768                 cls: 'day ' + clsName,
18769                 html: prevMonth.getDate()
18770             });
18771             
18772             prevMonth.setDate(prevMonth.getDate()+1);
18773         }
18774           
18775         var currentYear = this.date && this.date.getUTCFullYear();
18776         var currentMonth = this.date && this.date.getUTCMonth();
18777         
18778         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18779         
18780         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18781             v.removeClass('active');
18782             
18783             if(currentYear === year && k === currentMonth){
18784                 v.addClass('active');
18785             }
18786             
18787             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18788                 v.addClass('disabled');
18789             }
18790             
18791         });
18792         
18793         
18794         year = parseInt(year/10, 10) * 10;
18795         
18796         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18797         
18798         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18799         
18800         year -= 1;
18801         for (var i = -1; i < 11; i++) {
18802             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18803                 tag: 'span',
18804                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18805                 html: year
18806             });
18807             
18808             year += 1;
18809         }
18810     },
18811     
18812     showMode: function(dir) 
18813     {
18814         if (dir) {
18815             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18816         }
18817         
18818         Roo.each(this.picker().select('>div',true).elements, function(v){
18819             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18820             v.hide();
18821         });
18822         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18823     },
18824     
18825     place: function()
18826     {
18827         if(this.isInline) {
18828             return;
18829         }
18830         
18831         this.picker().removeClass(['bottom', 'top']);
18832         
18833         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18834             /*
18835              * place to the top of element!
18836              *
18837              */
18838             
18839             this.picker().addClass('top');
18840             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18841             
18842             return;
18843         }
18844         
18845         this.picker().addClass('bottom');
18846         
18847         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18848     },
18849     
18850     parseDate : function(value)
18851     {
18852         if(!value || value instanceof Date){
18853             return value;
18854         }
18855         var v = Date.parseDate(value, this.format);
18856         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18857             v = Date.parseDate(value, 'Y-m-d');
18858         }
18859         if(!v && this.altFormats){
18860             if(!this.altFormatsArray){
18861                 this.altFormatsArray = this.altFormats.split("|");
18862             }
18863             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18864                 v = Date.parseDate(value, this.altFormatsArray[i]);
18865             }
18866         }
18867         return v;
18868     },
18869     
18870     formatDate : function(date, fmt)
18871     {   
18872         return (!date || !(date instanceof Date)) ?
18873         date : date.dateFormat(fmt || this.format);
18874     },
18875     
18876     onFocus : function()
18877     {
18878         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18879         this.showPopup();
18880     },
18881     
18882     onBlur : function()
18883     {
18884         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18885         
18886         var d = this.inputEl().getValue();
18887         
18888         this.setValue(d);
18889                 
18890         this.hidePopup();
18891     },
18892     
18893     showPopup : function()
18894     {
18895         this.picker().show();
18896         this.update();
18897         this.place();
18898         
18899         this.fireEvent('showpopup', this, this.date);
18900     },
18901     
18902     hidePopup : function()
18903     {
18904         if(this.isInline) {
18905             return;
18906         }
18907         this.picker().hide();
18908         this.viewMode = this.startViewMode;
18909         this.showMode();
18910         
18911         this.fireEvent('hidepopup', this, this.date);
18912         
18913     },
18914     
18915     onMousedown: function(e)
18916     {
18917         e.stopPropagation();
18918         e.preventDefault();
18919     },
18920     
18921     keyup: function(e)
18922     {
18923         Roo.bootstrap.DateField.superclass.keyup.call(this);
18924         this.update();
18925     },
18926
18927     setValue: function(v)
18928     {
18929         if(this.fireEvent('beforeselect', this, v) !== false){
18930             var d = new Date(this.parseDate(v) ).clearTime();
18931         
18932             if(isNaN(d.getTime())){
18933                 this.date = this.viewDate = '';
18934                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18935                 return;
18936             }
18937
18938             v = this.formatDate(d);
18939
18940             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18941
18942             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18943
18944             this.update();
18945
18946             this.fireEvent('select', this, this.date);
18947         }
18948     },
18949     
18950     getValue: function()
18951     {
18952         return this.formatDate(this.date);
18953     },
18954     
18955     fireKey: function(e)
18956     {
18957         if (!this.picker().isVisible()){
18958             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18959                 this.showPopup();
18960             }
18961             return;
18962         }
18963         
18964         var dateChanged = false,
18965         dir, day, month,
18966         newDate, newViewDate;
18967         
18968         switch(e.keyCode){
18969             case 27: // escape
18970                 this.hidePopup();
18971                 e.preventDefault();
18972                 break;
18973             case 37: // left
18974             case 39: // right
18975                 if (!this.keyboardNavigation) {
18976                     break;
18977                 }
18978                 dir = e.keyCode == 37 ? -1 : 1;
18979                 
18980                 if (e.ctrlKey){
18981                     newDate = this.moveYear(this.date, dir);
18982                     newViewDate = this.moveYear(this.viewDate, dir);
18983                 } else if (e.shiftKey){
18984                     newDate = this.moveMonth(this.date, dir);
18985                     newViewDate = this.moveMonth(this.viewDate, dir);
18986                 } else {
18987                     newDate = new Date(this.date);
18988                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18989                     newViewDate = new Date(this.viewDate);
18990                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18991                 }
18992                 if (this.dateWithinRange(newDate)){
18993                     this.date = newDate;
18994                     this.viewDate = newViewDate;
18995                     this.setValue(this.formatDate(this.date));
18996 //                    this.update();
18997                     e.preventDefault();
18998                     dateChanged = true;
18999                 }
19000                 break;
19001             case 38: // up
19002             case 40: // down
19003                 if (!this.keyboardNavigation) {
19004                     break;
19005                 }
19006                 dir = e.keyCode == 38 ? -1 : 1;
19007                 if (e.ctrlKey){
19008                     newDate = this.moveYear(this.date, dir);
19009                     newViewDate = this.moveYear(this.viewDate, dir);
19010                 } else if (e.shiftKey){
19011                     newDate = this.moveMonth(this.date, dir);
19012                     newViewDate = this.moveMonth(this.viewDate, dir);
19013                 } else {
19014                     newDate = new Date(this.date);
19015                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19016                     newViewDate = new Date(this.viewDate);
19017                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19018                 }
19019                 if (this.dateWithinRange(newDate)){
19020                     this.date = newDate;
19021                     this.viewDate = newViewDate;
19022                     this.setValue(this.formatDate(this.date));
19023 //                    this.update();
19024                     e.preventDefault();
19025                     dateChanged = true;
19026                 }
19027                 break;
19028             case 13: // enter
19029                 this.setValue(this.formatDate(this.date));
19030                 this.hidePopup();
19031                 e.preventDefault();
19032                 break;
19033             case 9: // tab
19034                 this.setValue(this.formatDate(this.date));
19035                 this.hidePopup();
19036                 break;
19037             case 16: // shift
19038             case 17: // ctrl
19039             case 18: // alt
19040                 break;
19041             default :
19042                 this.hide();
19043                 
19044         }
19045     },
19046     
19047     
19048     onClick: function(e) 
19049     {
19050         e.stopPropagation();
19051         e.preventDefault();
19052         
19053         var target = e.getTarget();
19054         
19055         if(target.nodeName.toLowerCase() === 'i'){
19056             target = Roo.get(target).dom.parentNode;
19057         }
19058         
19059         var nodeName = target.nodeName;
19060         var className = target.className;
19061         var html = target.innerHTML;
19062         //Roo.log(nodeName);
19063         
19064         switch(nodeName.toLowerCase()) {
19065             case 'th':
19066                 switch(className) {
19067                     case 'switch':
19068                         this.showMode(1);
19069                         break;
19070                     case 'prev':
19071                     case 'next':
19072                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19073                         switch(this.viewMode){
19074                                 case 0:
19075                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19076                                         break;
19077                                 case 1:
19078                                 case 2:
19079                                         this.viewDate = this.moveYear(this.viewDate, dir);
19080                                         break;
19081                         }
19082                         this.fill();
19083                         break;
19084                     case 'today':
19085                         var date = new Date();
19086                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19087 //                        this.fill()
19088                         this.setValue(this.formatDate(this.date));
19089                         
19090                         this.hidePopup();
19091                         break;
19092                 }
19093                 break;
19094             case 'span':
19095                 if (className.indexOf('disabled') < 0) {
19096                     this.viewDate.setUTCDate(1);
19097                     if (className.indexOf('month') > -1) {
19098                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19099                     } else {
19100                         var year = parseInt(html, 10) || 0;
19101                         this.viewDate.setUTCFullYear(year);
19102                         
19103                     }
19104                     
19105                     if(this.singleMode){
19106                         this.setValue(this.formatDate(this.viewDate));
19107                         this.hidePopup();
19108                         return;
19109                     }
19110                     
19111                     this.showMode(-1);
19112                     this.fill();
19113                 }
19114                 break;
19115                 
19116             case 'td':
19117                 //Roo.log(className);
19118                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19119                     var day = parseInt(html, 10) || 1;
19120                     var year = this.viewDate.getUTCFullYear(),
19121                         month = this.viewDate.getUTCMonth();
19122
19123                     if (className.indexOf('old') > -1) {
19124                         if(month === 0 ){
19125                             month = 11;
19126                             year -= 1;
19127                         }else{
19128                             month -= 1;
19129                         }
19130                     } else if (className.indexOf('new') > -1) {
19131                         if (month == 11) {
19132                             month = 0;
19133                             year += 1;
19134                         } else {
19135                             month += 1;
19136                         }
19137                     }
19138                     //Roo.log([year,month,day]);
19139                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19140                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19141 //                    this.fill();
19142                     //Roo.log(this.formatDate(this.date));
19143                     this.setValue(this.formatDate(this.date));
19144                     this.hidePopup();
19145                 }
19146                 break;
19147         }
19148     },
19149     
19150     setStartDate: function(startDate)
19151     {
19152         this.startDate = startDate || -Infinity;
19153         if (this.startDate !== -Infinity) {
19154             this.startDate = this.parseDate(this.startDate);
19155         }
19156         this.update();
19157         this.updateNavArrows();
19158     },
19159
19160     setEndDate: function(endDate)
19161     {
19162         this.endDate = endDate || Infinity;
19163         if (this.endDate !== Infinity) {
19164             this.endDate = this.parseDate(this.endDate);
19165         }
19166         this.update();
19167         this.updateNavArrows();
19168     },
19169     
19170     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19171     {
19172         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19173         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19174             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19175         }
19176         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19177             return parseInt(d, 10);
19178         });
19179         this.update();
19180         this.updateNavArrows();
19181     },
19182     
19183     updateNavArrows: function() 
19184     {
19185         if(this.singleMode){
19186             return;
19187         }
19188         
19189         var d = new Date(this.viewDate),
19190         year = d.getUTCFullYear(),
19191         month = d.getUTCMonth();
19192         
19193         Roo.each(this.picker().select('.prev', true).elements, function(v){
19194             v.show();
19195             switch (this.viewMode) {
19196                 case 0:
19197
19198                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19199                         v.hide();
19200                     }
19201                     break;
19202                 case 1:
19203                 case 2:
19204                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19205                         v.hide();
19206                     }
19207                     break;
19208             }
19209         });
19210         
19211         Roo.each(this.picker().select('.next', true).elements, function(v){
19212             v.show();
19213             switch (this.viewMode) {
19214                 case 0:
19215
19216                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19217                         v.hide();
19218                     }
19219                     break;
19220                 case 1:
19221                 case 2:
19222                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19223                         v.hide();
19224                     }
19225                     break;
19226             }
19227         })
19228     },
19229     
19230     moveMonth: function(date, dir)
19231     {
19232         if (!dir) {
19233             return date;
19234         }
19235         var new_date = new Date(date.valueOf()),
19236         day = new_date.getUTCDate(),
19237         month = new_date.getUTCMonth(),
19238         mag = Math.abs(dir),
19239         new_month, test;
19240         dir = dir > 0 ? 1 : -1;
19241         if (mag == 1){
19242             test = dir == -1
19243             // If going back one month, make sure month is not current month
19244             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19245             ? function(){
19246                 return new_date.getUTCMonth() == month;
19247             }
19248             // If going forward one month, make sure month is as expected
19249             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19250             : function(){
19251                 return new_date.getUTCMonth() != new_month;
19252             };
19253             new_month = month + dir;
19254             new_date.setUTCMonth(new_month);
19255             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19256             if (new_month < 0 || new_month > 11) {
19257                 new_month = (new_month + 12) % 12;
19258             }
19259         } else {
19260             // For magnitudes >1, move one month at a time...
19261             for (var i=0; i<mag; i++) {
19262                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19263                 new_date = this.moveMonth(new_date, dir);
19264             }
19265             // ...then reset the day, keeping it in the new month
19266             new_month = new_date.getUTCMonth();
19267             new_date.setUTCDate(day);
19268             test = function(){
19269                 return new_month != new_date.getUTCMonth();
19270             };
19271         }
19272         // Common date-resetting loop -- if date is beyond end of month, make it
19273         // end of month
19274         while (test()){
19275             new_date.setUTCDate(--day);
19276             new_date.setUTCMonth(new_month);
19277         }
19278         return new_date;
19279     },
19280
19281     moveYear: function(date, dir)
19282     {
19283         return this.moveMonth(date, dir*12);
19284     },
19285
19286     dateWithinRange: function(date)
19287     {
19288         return date >= this.startDate && date <= this.endDate;
19289     },
19290
19291     
19292     remove: function() 
19293     {
19294         this.picker().remove();
19295     },
19296     
19297     validateValue : function(value)
19298     {
19299         if(this.getVisibilityEl().hasClass('hidden')){
19300             return true;
19301         }
19302         
19303         if(value.length < 1)  {
19304             if(this.allowBlank){
19305                 return true;
19306             }
19307             return false;
19308         }
19309         
19310         if(value.length < this.minLength){
19311             return false;
19312         }
19313         if(value.length > this.maxLength){
19314             return false;
19315         }
19316         if(this.vtype){
19317             var vt = Roo.form.VTypes;
19318             if(!vt[this.vtype](value, this)){
19319                 return false;
19320             }
19321         }
19322         if(typeof this.validator == "function"){
19323             var msg = this.validator(value);
19324             if(msg !== true){
19325                 return false;
19326             }
19327         }
19328         
19329         if(this.regex && !this.regex.test(value)){
19330             return false;
19331         }
19332         
19333         if(typeof(this.parseDate(value)) == 'undefined'){
19334             return false;
19335         }
19336         
19337         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19338             return false;
19339         }      
19340         
19341         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19342             return false;
19343         } 
19344         
19345         
19346         return true;
19347     },
19348     
19349     reset : function()
19350     {
19351         this.date = this.viewDate = '';
19352         
19353         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19354     }
19355    
19356 });
19357
19358 Roo.apply(Roo.bootstrap.DateField,  {
19359     
19360     head : {
19361         tag: 'thead',
19362         cn: [
19363         {
19364             tag: 'tr',
19365             cn: [
19366             {
19367                 tag: 'th',
19368                 cls: 'prev',
19369                 html: '<i class="fa fa-arrow-left"/>'
19370             },
19371             {
19372                 tag: 'th',
19373                 cls: 'switch',
19374                 colspan: '5'
19375             },
19376             {
19377                 tag: 'th',
19378                 cls: 'next',
19379                 html: '<i class="fa fa-arrow-right"/>'
19380             }
19381
19382             ]
19383         }
19384         ]
19385     },
19386     
19387     content : {
19388         tag: 'tbody',
19389         cn: [
19390         {
19391             tag: 'tr',
19392             cn: [
19393             {
19394                 tag: 'td',
19395                 colspan: '7'
19396             }
19397             ]
19398         }
19399         ]
19400     },
19401     
19402     footer : {
19403         tag: 'tfoot',
19404         cn: [
19405         {
19406             tag: 'tr',
19407             cn: [
19408             {
19409                 tag: 'th',
19410                 colspan: '7',
19411                 cls: 'today'
19412             }
19413                     
19414             ]
19415         }
19416         ]
19417     },
19418     
19419     dates:{
19420         en: {
19421             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19422             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19423             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19424             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19425             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19426             today: "Today"
19427         }
19428     },
19429     
19430     modes: [
19431     {
19432         clsName: 'days',
19433         navFnc: 'Month',
19434         navStep: 1
19435     },
19436     {
19437         clsName: 'months',
19438         navFnc: 'FullYear',
19439         navStep: 1
19440     },
19441     {
19442         clsName: 'years',
19443         navFnc: 'FullYear',
19444         navStep: 10
19445     }]
19446 });
19447
19448 Roo.apply(Roo.bootstrap.DateField,  {
19449   
19450     template : {
19451         tag: 'div',
19452         cls: 'datepicker dropdown-menu roo-dynamic',
19453         cn: [
19454         {
19455             tag: 'div',
19456             cls: 'datepicker-days',
19457             cn: [
19458             {
19459                 tag: 'table',
19460                 cls: 'table-condensed',
19461                 cn:[
19462                 Roo.bootstrap.DateField.head,
19463                 {
19464                     tag: 'tbody'
19465                 },
19466                 Roo.bootstrap.DateField.footer
19467                 ]
19468             }
19469             ]
19470         },
19471         {
19472             tag: 'div',
19473             cls: 'datepicker-months',
19474             cn: [
19475             {
19476                 tag: 'table',
19477                 cls: 'table-condensed',
19478                 cn:[
19479                 Roo.bootstrap.DateField.head,
19480                 Roo.bootstrap.DateField.content,
19481                 Roo.bootstrap.DateField.footer
19482                 ]
19483             }
19484             ]
19485         },
19486         {
19487             tag: 'div',
19488             cls: 'datepicker-years',
19489             cn: [
19490             {
19491                 tag: 'table',
19492                 cls: 'table-condensed',
19493                 cn:[
19494                 Roo.bootstrap.DateField.head,
19495                 Roo.bootstrap.DateField.content,
19496                 Roo.bootstrap.DateField.footer
19497                 ]
19498             }
19499             ]
19500         }
19501         ]
19502     }
19503 });
19504
19505  
19506
19507  /*
19508  * - LGPL
19509  *
19510  * TimeField
19511  * 
19512  */
19513
19514 /**
19515  * @class Roo.bootstrap.TimeField
19516  * @extends Roo.bootstrap.Input
19517  * Bootstrap DateField class
19518  * 
19519  * 
19520  * @constructor
19521  * Create a new TimeField
19522  * @param {Object} config The config object
19523  */
19524
19525 Roo.bootstrap.TimeField = function(config){
19526     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19527     this.addEvents({
19528             /**
19529              * @event show
19530              * Fires when this field show.
19531              * @param {Roo.bootstrap.DateField} thisthis
19532              * @param {Mixed} date The date value
19533              */
19534             show : true,
19535             /**
19536              * @event show
19537              * Fires when this field hide.
19538              * @param {Roo.bootstrap.DateField} this
19539              * @param {Mixed} date The date value
19540              */
19541             hide : true,
19542             /**
19543              * @event select
19544              * Fires when select a date.
19545              * @param {Roo.bootstrap.DateField} this
19546              * @param {Mixed} date The date value
19547              */
19548             select : true
19549         });
19550 };
19551
19552 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19553     
19554     /**
19555      * @cfg {String} format
19556      * The default time format string which can be overriden for localization support.  The format must be
19557      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19558      */
19559     format : "H:i",
19560        
19561     onRender: function(ct, position)
19562     {
19563         
19564         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19565                 
19566         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19567         
19568         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19569         
19570         this.pop = this.picker().select('>.datepicker-time',true).first();
19571         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19572         
19573         this.picker().on('mousedown', this.onMousedown, this);
19574         this.picker().on('click', this.onClick, this);
19575         
19576         this.picker().addClass('datepicker-dropdown');
19577     
19578         this.fillTime();
19579         this.update();
19580             
19581         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19582         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19583         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19584         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19585         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19586         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19587
19588     },
19589     
19590     fireKey: function(e){
19591         if (!this.picker().isVisible()){
19592             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19593                 this.show();
19594             }
19595             return;
19596         }
19597
19598         e.preventDefault();
19599         
19600         switch(e.keyCode){
19601             case 27: // escape
19602                 this.hide();
19603                 break;
19604             case 37: // left
19605             case 39: // right
19606                 this.onTogglePeriod();
19607                 break;
19608             case 38: // up
19609                 this.onIncrementMinutes();
19610                 break;
19611             case 40: // down
19612                 this.onDecrementMinutes();
19613                 break;
19614             case 13: // enter
19615             case 9: // tab
19616                 this.setTime();
19617                 break;
19618         }
19619     },
19620     
19621     onClick: function(e) {
19622         e.stopPropagation();
19623         e.preventDefault();
19624     },
19625     
19626     picker : function()
19627     {
19628         return this.el.select('.datepicker', true).first();
19629     },
19630     
19631     fillTime: function()
19632     {    
19633         var time = this.pop.select('tbody', true).first();
19634         
19635         time.dom.innerHTML = '';
19636         
19637         time.createChild({
19638             tag: 'tr',
19639             cn: [
19640                 {
19641                     tag: 'td',
19642                     cn: [
19643                         {
19644                             tag: 'a',
19645                             href: '#',
19646                             cls: 'btn',
19647                             cn: [
19648                                 {
19649                                     tag: 'span',
19650                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19651                                 }
19652                             ]
19653                         } 
19654                     ]
19655                 },
19656                 {
19657                     tag: 'td',
19658                     cls: 'separator'
19659                 },
19660                 {
19661                     tag: 'td',
19662                     cn: [
19663                         {
19664                             tag: 'a',
19665                             href: '#',
19666                             cls: 'btn',
19667                             cn: [
19668                                 {
19669                                     tag: 'span',
19670                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19671                                 }
19672                             ]
19673                         }
19674                     ]
19675                 },
19676                 {
19677                     tag: 'td',
19678                     cls: 'separator'
19679                 }
19680             ]
19681         });
19682         
19683         time.createChild({
19684             tag: 'tr',
19685             cn: [
19686                 {
19687                     tag: 'td',
19688                     cn: [
19689                         {
19690                             tag: 'span',
19691                             cls: 'timepicker-hour',
19692                             html: '00'
19693                         }  
19694                     ]
19695                 },
19696                 {
19697                     tag: 'td',
19698                     cls: 'separator',
19699                     html: ':'
19700                 },
19701                 {
19702                     tag: 'td',
19703                     cn: [
19704                         {
19705                             tag: 'span',
19706                             cls: 'timepicker-minute',
19707                             html: '00'
19708                         }  
19709                     ]
19710                 },
19711                 {
19712                     tag: 'td',
19713                     cls: 'separator'
19714                 },
19715                 {
19716                     tag: 'td',
19717                     cn: [
19718                         {
19719                             tag: 'button',
19720                             type: 'button',
19721                             cls: 'btn btn-primary period',
19722                             html: 'AM'
19723                             
19724                         }
19725                     ]
19726                 }
19727             ]
19728         });
19729         
19730         time.createChild({
19731             tag: 'tr',
19732             cn: [
19733                 {
19734                     tag: 'td',
19735                     cn: [
19736                         {
19737                             tag: 'a',
19738                             href: '#',
19739                             cls: 'btn',
19740                             cn: [
19741                                 {
19742                                     tag: 'span',
19743                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19744                                 }
19745                             ]
19746                         }
19747                     ]
19748                 },
19749                 {
19750                     tag: 'td',
19751                     cls: 'separator'
19752                 },
19753                 {
19754                     tag: 'td',
19755                     cn: [
19756                         {
19757                             tag: 'a',
19758                             href: '#',
19759                             cls: 'btn',
19760                             cn: [
19761                                 {
19762                                     tag: 'span',
19763                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19764                                 }
19765                             ]
19766                         }
19767                     ]
19768                 },
19769                 {
19770                     tag: 'td',
19771                     cls: 'separator'
19772                 }
19773             ]
19774         });
19775         
19776     },
19777     
19778     update: function()
19779     {
19780         
19781         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19782         
19783         this.fill();
19784     },
19785     
19786     fill: function() 
19787     {
19788         var hours = this.time.getHours();
19789         var minutes = this.time.getMinutes();
19790         var period = 'AM';
19791         
19792         if(hours > 11){
19793             period = 'PM';
19794         }
19795         
19796         if(hours == 0){
19797             hours = 12;
19798         }
19799         
19800         
19801         if(hours > 12){
19802             hours = hours - 12;
19803         }
19804         
19805         if(hours < 10){
19806             hours = '0' + hours;
19807         }
19808         
19809         if(minutes < 10){
19810             minutes = '0' + minutes;
19811         }
19812         
19813         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19814         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19815         this.pop.select('button', true).first().dom.innerHTML = period;
19816         
19817     },
19818     
19819     place: function()
19820     {   
19821         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19822         
19823         var cls = ['bottom'];
19824         
19825         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19826             cls.pop();
19827             cls.push('top');
19828         }
19829         
19830         cls.push('right');
19831         
19832         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19833             cls.pop();
19834             cls.push('left');
19835         }
19836         
19837         this.picker().addClass(cls.join('-'));
19838         
19839         var _this = this;
19840         
19841         Roo.each(cls, function(c){
19842             if(c == 'bottom'){
19843                 _this.picker().setTop(_this.inputEl().getHeight());
19844                 return;
19845             }
19846             if(c == 'top'){
19847                 _this.picker().setTop(0 - _this.picker().getHeight());
19848                 return;
19849             }
19850             
19851             if(c == 'left'){
19852                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19853                 return;
19854             }
19855             if(c == 'right'){
19856                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19857                 return;
19858             }
19859         });
19860         
19861     },
19862   
19863     onFocus : function()
19864     {
19865         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19866         this.show();
19867     },
19868     
19869     onBlur : function()
19870     {
19871         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19872         this.hide();
19873     },
19874     
19875     show : function()
19876     {
19877         this.picker().show();
19878         this.pop.show();
19879         this.update();
19880         this.place();
19881         
19882         this.fireEvent('show', this, this.date);
19883     },
19884     
19885     hide : function()
19886     {
19887         this.picker().hide();
19888         this.pop.hide();
19889         
19890         this.fireEvent('hide', this, this.date);
19891     },
19892     
19893     setTime : function()
19894     {
19895         this.hide();
19896         this.setValue(this.time.format(this.format));
19897         
19898         this.fireEvent('select', this, this.date);
19899         
19900         
19901     },
19902     
19903     onMousedown: function(e){
19904         e.stopPropagation();
19905         e.preventDefault();
19906     },
19907     
19908     onIncrementHours: function()
19909     {
19910         Roo.log('onIncrementHours');
19911         this.time = this.time.add(Date.HOUR, 1);
19912         this.update();
19913         
19914     },
19915     
19916     onDecrementHours: function()
19917     {
19918         Roo.log('onDecrementHours');
19919         this.time = this.time.add(Date.HOUR, -1);
19920         this.update();
19921     },
19922     
19923     onIncrementMinutes: function()
19924     {
19925         Roo.log('onIncrementMinutes');
19926         this.time = this.time.add(Date.MINUTE, 1);
19927         this.update();
19928     },
19929     
19930     onDecrementMinutes: function()
19931     {
19932         Roo.log('onDecrementMinutes');
19933         this.time = this.time.add(Date.MINUTE, -1);
19934         this.update();
19935     },
19936     
19937     onTogglePeriod: function()
19938     {
19939         Roo.log('onTogglePeriod');
19940         this.time = this.time.add(Date.HOUR, 12);
19941         this.update();
19942     }
19943     
19944    
19945 });
19946
19947 Roo.apply(Roo.bootstrap.TimeField,  {
19948     
19949     content : {
19950         tag: 'tbody',
19951         cn: [
19952             {
19953                 tag: 'tr',
19954                 cn: [
19955                 {
19956                     tag: 'td',
19957                     colspan: '7'
19958                 }
19959                 ]
19960             }
19961         ]
19962     },
19963     
19964     footer : {
19965         tag: 'tfoot',
19966         cn: [
19967             {
19968                 tag: 'tr',
19969                 cn: [
19970                 {
19971                     tag: 'th',
19972                     colspan: '7',
19973                     cls: '',
19974                     cn: [
19975                         {
19976                             tag: 'button',
19977                             cls: 'btn btn-info ok',
19978                             html: 'OK'
19979                         }
19980                     ]
19981                 }
19982
19983                 ]
19984             }
19985         ]
19986     }
19987 });
19988
19989 Roo.apply(Roo.bootstrap.TimeField,  {
19990   
19991     template : {
19992         tag: 'div',
19993         cls: 'datepicker dropdown-menu',
19994         cn: [
19995             {
19996                 tag: 'div',
19997                 cls: 'datepicker-time',
19998                 cn: [
19999                 {
20000                     tag: 'table',
20001                     cls: 'table-condensed',
20002                     cn:[
20003                     Roo.bootstrap.TimeField.content,
20004                     Roo.bootstrap.TimeField.footer
20005                     ]
20006                 }
20007                 ]
20008             }
20009         ]
20010     }
20011 });
20012
20013  
20014
20015  /*
20016  * - LGPL
20017  *
20018  * MonthField
20019  * 
20020  */
20021
20022 /**
20023  * @class Roo.bootstrap.MonthField
20024  * @extends Roo.bootstrap.Input
20025  * Bootstrap MonthField class
20026  * 
20027  * @cfg {String} language default en
20028  * 
20029  * @constructor
20030  * Create a new MonthField
20031  * @param {Object} config The config object
20032  */
20033
20034 Roo.bootstrap.MonthField = function(config){
20035     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20036     
20037     this.addEvents({
20038         /**
20039          * @event show
20040          * Fires when this field show.
20041          * @param {Roo.bootstrap.MonthField} this
20042          * @param {Mixed} date The date value
20043          */
20044         show : true,
20045         /**
20046          * @event show
20047          * Fires when this field hide.
20048          * @param {Roo.bootstrap.MonthField} this
20049          * @param {Mixed} date The date value
20050          */
20051         hide : true,
20052         /**
20053          * @event select
20054          * Fires when select a date.
20055          * @param {Roo.bootstrap.MonthField} this
20056          * @param {String} oldvalue The old value
20057          * @param {String} newvalue The new value
20058          */
20059         select : true
20060     });
20061 };
20062
20063 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20064     
20065     onRender: function(ct, position)
20066     {
20067         
20068         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20069         
20070         this.language = this.language || 'en';
20071         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20072         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20073         
20074         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20075         this.isInline = false;
20076         this.isInput = true;
20077         this.component = this.el.select('.add-on', true).first() || false;
20078         this.component = (this.component && this.component.length === 0) ? false : this.component;
20079         this.hasInput = this.component && this.inputEL().length;
20080         
20081         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20082         
20083         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20084         
20085         this.picker().on('mousedown', this.onMousedown, this);
20086         this.picker().on('click', this.onClick, this);
20087         
20088         this.picker().addClass('datepicker-dropdown');
20089         
20090         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20091             v.setStyle('width', '189px');
20092         });
20093         
20094         this.fillMonths();
20095         
20096         this.update();
20097         
20098         if(this.isInline) {
20099             this.show();
20100         }
20101         
20102     },
20103     
20104     setValue: function(v, suppressEvent)
20105     {   
20106         var o = this.getValue();
20107         
20108         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20109         
20110         this.update();
20111
20112         if(suppressEvent !== true){
20113             this.fireEvent('select', this, o, v);
20114         }
20115         
20116     },
20117     
20118     getValue: function()
20119     {
20120         return this.value;
20121     },
20122     
20123     onClick: function(e) 
20124     {
20125         e.stopPropagation();
20126         e.preventDefault();
20127         
20128         var target = e.getTarget();
20129         
20130         if(target.nodeName.toLowerCase() === 'i'){
20131             target = Roo.get(target).dom.parentNode;
20132         }
20133         
20134         var nodeName = target.nodeName;
20135         var className = target.className;
20136         var html = target.innerHTML;
20137         
20138         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20139             return;
20140         }
20141         
20142         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20143         
20144         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20145         
20146         this.hide();
20147                         
20148     },
20149     
20150     picker : function()
20151     {
20152         return this.pickerEl;
20153     },
20154     
20155     fillMonths: function()
20156     {    
20157         var i = 0;
20158         var months = this.picker().select('>.datepicker-months td', true).first();
20159         
20160         months.dom.innerHTML = '';
20161         
20162         while (i < 12) {
20163             var month = {
20164                 tag: 'span',
20165                 cls: 'month',
20166                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20167             };
20168             
20169             months.createChild(month);
20170         }
20171         
20172     },
20173     
20174     update: function()
20175     {
20176         var _this = this;
20177         
20178         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20179             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20180         }
20181         
20182         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20183             e.removeClass('active');
20184             
20185             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20186                 e.addClass('active');
20187             }
20188         })
20189     },
20190     
20191     place: function()
20192     {
20193         if(this.isInline) {
20194             return;
20195         }
20196         
20197         this.picker().removeClass(['bottom', 'top']);
20198         
20199         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20200             /*
20201              * place to the top of element!
20202              *
20203              */
20204             
20205             this.picker().addClass('top');
20206             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20207             
20208             return;
20209         }
20210         
20211         this.picker().addClass('bottom');
20212         
20213         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20214     },
20215     
20216     onFocus : function()
20217     {
20218         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20219         this.show();
20220     },
20221     
20222     onBlur : function()
20223     {
20224         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20225         
20226         var d = this.inputEl().getValue();
20227         
20228         this.setValue(d);
20229                 
20230         this.hide();
20231     },
20232     
20233     show : function()
20234     {
20235         this.picker().show();
20236         this.picker().select('>.datepicker-months', true).first().show();
20237         this.update();
20238         this.place();
20239         
20240         this.fireEvent('show', this, this.date);
20241     },
20242     
20243     hide : function()
20244     {
20245         if(this.isInline) {
20246             return;
20247         }
20248         this.picker().hide();
20249         this.fireEvent('hide', this, this.date);
20250         
20251     },
20252     
20253     onMousedown: function(e)
20254     {
20255         e.stopPropagation();
20256         e.preventDefault();
20257     },
20258     
20259     keyup: function(e)
20260     {
20261         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20262         this.update();
20263     },
20264
20265     fireKey: function(e)
20266     {
20267         if (!this.picker().isVisible()){
20268             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20269                 this.show();
20270             }
20271             return;
20272         }
20273         
20274         var dir;
20275         
20276         switch(e.keyCode){
20277             case 27: // escape
20278                 this.hide();
20279                 e.preventDefault();
20280                 break;
20281             case 37: // left
20282             case 39: // right
20283                 dir = e.keyCode == 37 ? -1 : 1;
20284                 
20285                 this.vIndex = this.vIndex + dir;
20286                 
20287                 if(this.vIndex < 0){
20288                     this.vIndex = 0;
20289                 }
20290                 
20291                 if(this.vIndex > 11){
20292                     this.vIndex = 11;
20293                 }
20294                 
20295                 if(isNaN(this.vIndex)){
20296                     this.vIndex = 0;
20297                 }
20298                 
20299                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20300                 
20301                 break;
20302             case 38: // up
20303             case 40: // down
20304                 
20305                 dir = e.keyCode == 38 ? -1 : 1;
20306                 
20307                 this.vIndex = this.vIndex + dir * 4;
20308                 
20309                 if(this.vIndex < 0){
20310                     this.vIndex = 0;
20311                 }
20312                 
20313                 if(this.vIndex > 11){
20314                     this.vIndex = 11;
20315                 }
20316                 
20317                 if(isNaN(this.vIndex)){
20318                     this.vIndex = 0;
20319                 }
20320                 
20321                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20322                 break;
20323                 
20324             case 13: // enter
20325                 
20326                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20327                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20328                 }
20329                 
20330                 this.hide();
20331                 e.preventDefault();
20332                 break;
20333             case 9: // tab
20334                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20335                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20336                 }
20337                 this.hide();
20338                 break;
20339             case 16: // shift
20340             case 17: // ctrl
20341             case 18: // alt
20342                 break;
20343             default :
20344                 this.hide();
20345                 
20346         }
20347     },
20348     
20349     remove: function() 
20350     {
20351         this.picker().remove();
20352     }
20353    
20354 });
20355
20356 Roo.apply(Roo.bootstrap.MonthField,  {
20357     
20358     content : {
20359         tag: 'tbody',
20360         cn: [
20361         {
20362             tag: 'tr',
20363             cn: [
20364             {
20365                 tag: 'td',
20366                 colspan: '7'
20367             }
20368             ]
20369         }
20370         ]
20371     },
20372     
20373     dates:{
20374         en: {
20375             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20376             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20377         }
20378     }
20379 });
20380
20381 Roo.apply(Roo.bootstrap.MonthField,  {
20382   
20383     template : {
20384         tag: 'div',
20385         cls: 'datepicker dropdown-menu roo-dynamic',
20386         cn: [
20387             {
20388                 tag: 'div',
20389                 cls: 'datepicker-months',
20390                 cn: [
20391                 {
20392                     tag: 'table',
20393                     cls: 'table-condensed',
20394                     cn:[
20395                         Roo.bootstrap.DateField.content
20396                     ]
20397                 }
20398                 ]
20399             }
20400         ]
20401     }
20402 });
20403
20404  
20405
20406  
20407  /*
20408  * - LGPL
20409  *
20410  * CheckBox
20411  * 
20412  */
20413
20414 /**
20415  * @class Roo.bootstrap.CheckBox
20416  * @extends Roo.bootstrap.Input
20417  * Bootstrap CheckBox class
20418  * 
20419  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20420  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20421  * @cfg {String} boxLabel The text that appears beside the checkbox
20422  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20423  * @cfg {Boolean} checked initnal the element
20424  * @cfg {Boolean} inline inline the element (default false)
20425  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20426  * @cfg {String} tooltip label tooltip
20427  * 
20428  * @constructor
20429  * Create a new CheckBox
20430  * @param {Object} config The config object
20431  */
20432
20433 Roo.bootstrap.CheckBox = function(config){
20434     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20435    
20436     this.addEvents({
20437         /**
20438         * @event check
20439         * Fires when the element is checked or unchecked.
20440         * @param {Roo.bootstrap.CheckBox} this This input
20441         * @param {Boolean} checked The new checked value
20442         */
20443        check : true,
20444        /**
20445         * @event click
20446         * Fires when the element is click.
20447         * @param {Roo.bootstrap.CheckBox} this This input
20448         */
20449        click : true
20450     });
20451     
20452 };
20453
20454 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20455   
20456     inputType: 'checkbox',
20457     inputValue: 1,
20458     valueOff: 0,
20459     boxLabel: false,
20460     checked: false,
20461     weight : false,
20462     inline: false,
20463     tooltip : '',
20464     
20465     getAutoCreate : function()
20466     {
20467         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20468         
20469         var id = Roo.id();
20470         
20471         var cfg = {};
20472         
20473         cfg.cls = 'form-group ' + this.inputType; //input-group
20474         
20475         if(this.inline){
20476             cfg.cls += ' ' + this.inputType + '-inline';
20477         }
20478         
20479         var input =  {
20480             tag: 'input',
20481             id : id,
20482             type : this.inputType,
20483             value : this.inputValue,
20484             cls : 'roo-' + this.inputType, //'form-box',
20485             placeholder : this.placeholder || ''
20486             
20487         };
20488         
20489         if(this.inputType != 'radio'){
20490             var hidden =  {
20491                 tag: 'input',
20492                 type : 'hidden',
20493                 cls : 'roo-hidden-value',
20494                 value : this.checked ? this.inputValue : this.valueOff
20495             };
20496         }
20497         
20498             
20499         if (this.weight) { // Validity check?
20500             cfg.cls += " " + this.inputType + "-" + this.weight;
20501         }
20502         
20503         if (this.disabled) {
20504             input.disabled=true;
20505         }
20506         
20507         if(this.checked){
20508             input.checked = this.checked;
20509         }
20510         
20511         if (this.name) {
20512             
20513             input.name = this.name;
20514             
20515             if(this.inputType != 'radio'){
20516                 hidden.name = this.name;
20517                 input.name = '_hidden_' + this.name;
20518             }
20519         }
20520         
20521         if (this.size) {
20522             input.cls += ' input-' + this.size;
20523         }
20524         
20525         var settings=this;
20526         
20527         ['xs','sm','md','lg'].map(function(size){
20528             if (settings[size]) {
20529                 cfg.cls += ' col-' + size + '-' + settings[size];
20530             }
20531         });
20532         
20533         var inputblock = input;
20534          
20535         if (this.before || this.after) {
20536             
20537             inputblock = {
20538                 cls : 'input-group',
20539                 cn :  [] 
20540             };
20541             
20542             if (this.before) {
20543                 inputblock.cn.push({
20544                     tag :'span',
20545                     cls : 'input-group-addon',
20546                     html : this.before
20547                 });
20548             }
20549             
20550             inputblock.cn.push(input);
20551             
20552             if(this.inputType != 'radio'){
20553                 inputblock.cn.push(hidden);
20554             }
20555             
20556             if (this.after) {
20557                 inputblock.cn.push({
20558                     tag :'span',
20559                     cls : 'input-group-addon',
20560                     html : this.after
20561                 });
20562             }
20563             
20564         }
20565         
20566         if (align ==='left' && this.fieldLabel.length) {
20567 //                Roo.log("left and has label");
20568             cfg.cn = [
20569                 {
20570                     tag: 'label',
20571                     'for' :  id,
20572                     cls : 'control-label',
20573                     html : this.fieldLabel
20574                 },
20575                 {
20576                     cls : "", 
20577                     cn: [
20578                         inputblock
20579                     ]
20580                 }
20581             ];
20582             
20583             if(this.labelWidth > 12){
20584                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20585             }
20586             
20587             if(this.labelWidth < 13 && this.labelmd == 0){
20588                 this.labelmd = this.labelWidth;
20589             }
20590             
20591             if(this.labellg > 0){
20592                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20593                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20594             }
20595             
20596             if(this.labelmd > 0){
20597                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20598                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20599             }
20600             
20601             if(this.labelsm > 0){
20602                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20603                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20604             }
20605             
20606             if(this.labelxs > 0){
20607                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20608                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20609             }
20610             
20611         } else if ( this.fieldLabel.length) {
20612 //                Roo.log(" label");
20613                 cfg.cn = [
20614                    
20615                     {
20616                         tag: this.boxLabel ? 'span' : 'label',
20617                         'for': id,
20618                         cls: 'control-label box-input-label',
20619                         //cls : 'input-group-addon',
20620                         html : this.fieldLabel
20621                     },
20622                     
20623                     inputblock
20624                     
20625                 ];
20626
20627         } else {
20628             
20629 //                Roo.log(" no label && no align");
20630                 cfg.cn = [  inputblock ] ;
20631                 
20632                 
20633         }
20634         
20635         if(this.boxLabel){
20636              var boxLabelCfg = {
20637                 tag: 'label',
20638                 //'for': id, // box label is handled by onclick - so no for...
20639                 cls: 'box-label',
20640                 html: this.boxLabel
20641             };
20642             
20643             if(this.tooltip){
20644                 boxLabelCfg.tooltip = this.tooltip;
20645             }
20646              
20647             cfg.cn.push(boxLabelCfg);
20648         }
20649         
20650         if(this.inputType != 'radio'){
20651             cfg.cn.push(hidden);
20652         }
20653         
20654         return cfg;
20655         
20656     },
20657     
20658     /**
20659      * return the real input element.
20660      */
20661     inputEl: function ()
20662     {
20663         return this.el.select('input.roo-' + this.inputType,true).first();
20664     },
20665     hiddenEl: function ()
20666     {
20667         return this.el.select('input.roo-hidden-value',true).first();
20668     },
20669     
20670     labelEl: function()
20671     {
20672         return this.el.select('label.control-label',true).first();
20673     },
20674     /* depricated... */
20675     
20676     label: function()
20677     {
20678         return this.labelEl();
20679     },
20680     
20681     boxLabelEl: function()
20682     {
20683         return this.el.select('label.box-label',true).first();
20684     },
20685     
20686     initEvents : function()
20687     {
20688 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20689         
20690         this.inputEl().on('click', this.onClick,  this);
20691         
20692         if (this.boxLabel) { 
20693             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20694         }
20695         
20696         this.startValue = this.getValue();
20697         
20698         if(this.groupId){
20699             Roo.bootstrap.CheckBox.register(this);
20700         }
20701     },
20702     
20703     onClick : function(e)
20704     {   
20705         if(this.fireEvent('click', this, e) !== false){
20706             this.setChecked(!this.checked);
20707         }
20708         
20709     },
20710     
20711     setChecked : function(state,suppressEvent)
20712     {
20713         this.startValue = this.getValue();
20714
20715         if(this.inputType == 'radio'){
20716             
20717             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20718                 e.dom.checked = false;
20719             });
20720             
20721             this.inputEl().dom.checked = true;
20722             
20723             this.inputEl().dom.value = this.inputValue;
20724             
20725             if(suppressEvent !== true){
20726                 this.fireEvent('check', this, true);
20727             }
20728             
20729             this.validate();
20730             
20731             return;
20732         }
20733         
20734         this.checked = state;
20735         
20736         this.inputEl().dom.checked = state;
20737         
20738         
20739         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20740         
20741         if(suppressEvent !== true){
20742             this.fireEvent('check', this, state);
20743         }
20744         
20745         this.validate();
20746     },
20747     
20748     getValue : function()
20749     {
20750         if(this.inputType == 'radio'){
20751             return this.getGroupValue();
20752         }
20753         
20754         return this.hiddenEl().dom.value;
20755         
20756     },
20757     
20758     getGroupValue : function()
20759     {
20760         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20761             return '';
20762         }
20763         
20764         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20765     },
20766     
20767     setValue : function(v,suppressEvent)
20768     {
20769         if(this.inputType == 'radio'){
20770             this.setGroupValue(v, suppressEvent);
20771             return;
20772         }
20773         
20774         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20775         
20776         this.validate();
20777     },
20778     
20779     setGroupValue : function(v, suppressEvent)
20780     {
20781         this.startValue = this.getValue();
20782         
20783         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20784             e.dom.checked = false;
20785             
20786             if(e.dom.value == v){
20787                 e.dom.checked = true;
20788             }
20789         });
20790         
20791         if(suppressEvent !== true){
20792             this.fireEvent('check', this, true);
20793         }
20794
20795         this.validate();
20796         
20797         return;
20798     },
20799     
20800     validate : function()
20801     {
20802         if(this.getVisibilityEl().hasClass('hidden')){
20803             return true;
20804         }
20805         
20806         if(
20807                 this.disabled || 
20808                 (this.inputType == 'radio' && this.validateRadio()) ||
20809                 (this.inputType == 'checkbox' && this.validateCheckbox())
20810         ){
20811             this.markValid();
20812             return true;
20813         }
20814         
20815         this.markInvalid();
20816         return false;
20817     },
20818     
20819     validateRadio : function()
20820     {
20821         if(this.getVisibilityEl().hasClass('hidden')){
20822             return true;
20823         }
20824         
20825         if(this.allowBlank){
20826             return true;
20827         }
20828         
20829         var valid = false;
20830         
20831         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20832             if(!e.dom.checked){
20833                 return;
20834             }
20835             
20836             valid = true;
20837             
20838             return false;
20839         });
20840         
20841         return valid;
20842     },
20843     
20844     validateCheckbox : function()
20845     {
20846         if(!this.groupId){
20847             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20848             //return (this.getValue() == this.inputValue) ? true : false;
20849         }
20850         
20851         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20852         
20853         if(!group){
20854             return false;
20855         }
20856         
20857         var r = false;
20858         
20859         for(var i in group){
20860             if(group[i].el.isVisible(true)){
20861                 r = false;
20862                 break;
20863             }
20864             
20865             r = true;
20866         }
20867         
20868         for(var i in group){
20869             if(r){
20870                 break;
20871             }
20872             
20873             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20874         }
20875         
20876         return r;
20877     },
20878     
20879     /**
20880      * Mark this field as valid
20881      */
20882     markValid : function()
20883     {
20884         var _this = this;
20885         
20886         this.fireEvent('valid', this);
20887         
20888         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20889         
20890         if(this.groupId){
20891             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20892         }
20893         
20894         if(label){
20895             label.markValid();
20896         }
20897
20898         if(this.inputType == 'radio'){
20899             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20900                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20901                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20902             });
20903             
20904             return;
20905         }
20906
20907         if(!this.groupId){
20908             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20909             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20910             return;
20911         }
20912         
20913         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20914         
20915         if(!group){
20916             return;
20917         }
20918         
20919         for(var i in group){
20920             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20921             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20922         }
20923     },
20924     
20925      /**
20926      * Mark this field as invalid
20927      * @param {String} msg The validation message
20928      */
20929     markInvalid : function(msg)
20930     {
20931         if(this.allowBlank){
20932             return;
20933         }
20934         
20935         var _this = this;
20936         
20937         this.fireEvent('invalid', this, msg);
20938         
20939         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20940         
20941         if(this.groupId){
20942             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20943         }
20944         
20945         if(label){
20946             label.markInvalid();
20947         }
20948             
20949         if(this.inputType == 'radio'){
20950             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20951                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20952                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20953             });
20954             
20955             return;
20956         }
20957         
20958         if(!this.groupId){
20959             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20960             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20961             return;
20962         }
20963         
20964         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20965         
20966         if(!group){
20967             return;
20968         }
20969         
20970         for(var i in group){
20971             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20972             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20973         }
20974         
20975     },
20976     
20977     clearInvalid : function()
20978     {
20979         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20980         
20981         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20982         
20983         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20984         
20985         if (label && label.iconEl) {
20986             label.iconEl.removeClass(label.validClass);
20987             label.iconEl.removeClass(label.invalidClass);
20988         }
20989     },
20990     
20991     disable : function()
20992     {
20993         if(this.inputType != 'radio'){
20994             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20995             return;
20996         }
20997         
20998         var _this = this;
20999         
21000         if(this.rendered){
21001             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21002                 _this.getActionEl().addClass(this.disabledClass);
21003                 e.dom.disabled = true;
21004             });
21005         }
21006         
21007         this.disabled = true;
21008         this.fireEvent("disable", this);
21009         return this;
21010     },
21011
21012     enable : function()
21013     {
21014         if(this.inputType != 'radio'){
21015             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21016             return;
21017         }
21018         
21019         var _this = this;
21020         
21021         if(this.rendered){
21022             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21023                 _this.getActionEl().removeClass(this.disabledClass);
21024                 e.dom.disabled = false;
21025             });
21026         }
21027         
21028         this.disabled = false;
21029         this.fireEvent("enable", this);
21030         return this;
21031     },
21032     
21033     setBoxLabel : function(v)
21034     {
21035         this.boxLabel = v;
21036         
21037         if(this.rendered){
21038             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21039         }
21040     }
21041
21042 });
21043
21044 Roo.apply(Roo.bootstrap.CheckBox, {
21045     
21046     groups: {},
21047     
21048      /**
21049     * register a CheckBox Group
21050     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21051     */
21052     register : function(checkbox)
21053     {
21054         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21055             this.groups[checkbox.groupId] = {};
21056         }
21057         
21058         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21059             return;
21060         }
21061         
21062         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21063         
21064     },
21065     /**
21066     * fetch a CheckBox Group based on the group ID
21067     * @param {string} the group ID
21068     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21069     */
21070     get: function(groupId) {
21071         if (typeof(this.groups[groupId]) == 'undefined') {
21072             return false;
21073         }
21074         
21075         return this.groups[groupId] ;
21076     }
21077     
21078     
21079 });
21080 /*
21081  * - LGPL
21082  *
21083  * RadioItem
21084  * 
21085  */
21086
21087 /**
21088  * @class Roo.bootstrap.Radio
21089  * @extends Roo.bootstrap.Component
21090  * Bootstrap Radio class
21091  * @cfg {String} boxLabel - the label associated
21092  * @cfg {String} value - the value of radio
21093  * 
21094  * @constructor
21095  * Create a new Radio
21096  * @param {Object} config The config object
21097  */
21098 Roo.bootstrap.Radio = function(config){
21099     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21100     
21101 };
21102
21103 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21104     
21105     boxLabel : '',
21106     
21107     value : '',
21108     
21109     getAutoCreate : function()
21110     {
21111         var cfg = {
21112             tag : 'div',
21113             cls : 'form-group radio',
21114             cn : [
21115                 {
21116                     tag : 'label',
21117                     cls : 'box-label',
21118                     html : this.boxLabel
21119                 }
21120             ]
21121         };
21122         
21123         return cfg;
21124     },
21125     
21126     initEvents : function() 
21127     {
21128         this.parent().register(this);
21129         
21130         this.el.on('click', this.onClick, this);
21131         
21132     },
21133     
21134     onClick : function(e)
21135     {
21136         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21137             this.setChecked(true);
21138         }
21139     },
21140     
21141     setChecked : function(state, suppressEvent)
21142     {
21143         this.parent().setValue(this.value, suppressEvent);
21144         
21145     },
21146     
21147     setBoxLabel : function(v)
21148     {
21149         this.boxLabel = v;
21150         
21151         if(this.rendered){
21152             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21153         }
21154     }
21155     
21156 });
21157  
21158
21159  /*
21160  * - LGPL
21161  *
21162  * Input
21163  * 
21164  */
21165
21166 /**
21167  * @class Roo.bootstrap.SecurePass
21168  * @extends Roo.bootstrap.Input
21169  * Bootstrap SecurePass class
21170  *
21171  * 
21172  * @constructor
21173  * Create a new SecurePass
21174  * @param {Object} config The config object
21175  */
21176  
21177 Roo.bootstrap.SecurePass = function (config) {
21178     // these go here, so the translation tool can replace them..
21179     this.errors = {
21180         PwdEmpty: "Please type a password, and then retype it to confirm.",
21181         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21182         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21183         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21184         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21185         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21186         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21187         TooWeak: "Your password is Too Weak."
21188     },
21189     this.meterLabel = "Password strength:";
21190     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21191     this.meterClass = [
21192         "roo-password-meter-tooweak", 
21193         "roo-password-meter-weak", 
21194         "roo-password-meter-medium", 
21195         "roo-password-meter-strong", 
21196         "roo-password-meter-grey"
21197     ];
21198     
21199     this.errors = {};
21200     
21201     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21202 }
21203
21204 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21205     /**
21206      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21207      * {
21208      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21209      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21210      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21211      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21212      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21213      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21214      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21215      * })
21216      */
21217     // private
21218     
21219     meterWidth: 300,
21220     errorMsg :'',    
21221     errors: false,
21222     imageRoot: '/',
21223     /**
21224      * @cfg {String/Object} Label for the strength meter (defaults to
21225      * 'Password strength:')
21226      */
21227     // private
21228     meterLabel: '',
21229     /**
21230      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21231      * ['Weak', 'Medium', 'Strong'])
21232      */
21233     // private    
21234     pwdStrengths: false,    
21235     // private
21236     strength: 0,
21237     // private
21238     _lastPwd: null,
21239     // private
21240     kCapitalLetter: 0,
21241     kSmallLetter: 1,
21242     kDigit: 2,
21243     kPunctuation: 3,
21244     
21245     insecure: false,
21246     // private
21247     initEvents: function ()
21248     {
21249         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21250
21251         if (this.el.is('input[type=password]') && Roo.isSafari) {
21252             this.el.on('keydown', this.SafariOnKeyDown, this);
21253         }
21254
21255         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21256     },
21257     // private
21258     onRender: function (ct, position)
21259     {
21260         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21261         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21262         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21263
21264         this.trigger.createChild({
21265                    cn: [
21266                     {
21267                     //id: 'PwdMeter',
21268                     tag: 'div',
21269                     cls: 'roo-password-meter-grey col-xs-12',
21270                     style: {
21271                         //width: 0,
21272                         //width: this.meterWidth + 'px'                                                
21273                         }
21274                     },
21275                     {                            
21276                          cls: 'roo-password-meter-text'                          
21277                     }
21278                 ]            
21279         });
21280
21281          
21282         if (this.hideTrigger) {
21283             this.trigger.setDisplayed(false);
21284         }
21285         this.setSize(this.width || '', this.height || '');
21286     },
21287     // private
21288     onDestroy: function ()
21289     {
21290         if (this.trigger) {
21291             this.trigger.removeAllListeners();
21292             this.trigger.remove();
21293         }
21294         if (this.wrap) {
21295             this.wrap.remove();
21296         }
21297         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21298     },
21299     // private
21300     checkStrength: function ()
21301     {
21302         var pwd = this.inputEl().getValue();
21303         if (pwd == this._lastPwd) {
21304             return;
21305         }
21306
21307         var strength;
21308         if (this.ClientSideStrongPassword(pwd)) {
21309             strength = 3;
21310         } else if (this.ClientSideMediumPassword(pwd)) {
21311             strength = 2;
21312         } else if (this.ClientSideWeakPassword(pwd)) {
21313             strength = 1;
21314         } else {
21315             strength = 0;
21316         }
21317         
21318         Roo.log('strength1: ' + strength);
21319         
21320         //var pm = this.trigger.child('div/div/div').dom;
21321         var pm = this.trigger.child('div/div');
21322         pm.removeClass(this.meterClass);
21323         pm.addClass(this.meterClass[strength]);
21324                 
21325         
21326         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21327                 
21328         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21329         
21330         this._lastPwd = pwd;
21331     },
21332     reset: function ()
21333     {
21334         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21335         
21336         this._lastPwd = '';
21337         
21338         var pm = this.trigger.child('div/div');
21339         pm.removeClass(this.meterClass);
21340         pm.addClass('roo-password-meter-grey');        
21341         
21342         
21343         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21344         
21345         pt.innerHTML = '';
21346         this.inputEl().dom.type='password';
21347     },
21348     // private
21349     validateValue: function (value)
21350     {
21351         
21352         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21353             return false;
21354         }
21355         if (value.length == 0) {
21356             if (this.allowBlank) {
21357                 this.clearInvalid();
21358                 return true;
21359             }
21360
21361             this.markInvalid(this.errors.PwdEmpty);
21362             this.errorMsg = this.errors.PwdEmpty;
21363             return false;
21364         }
21365         
21366         if(this.insecure){
21367             return true;
21368         }
21369         
21370         if ('[\x21-\x7e]*'.match(value)) {
21371             this.markInvalid(this.errors.PwdBadChar);
21372             this.errorMsg = this.errors.PwdBadChar;
21373             return false;
21374         }
21375         if (value.length < 6) {
21376             this.markInvalid(this.errors.PwdShort);
21377             this.errorMsg = this.errors.PwdShort;
21378             return false;
21379         }
21380         if (value.length > 16) {
21381             this.markInvalid(this.errors.PwdLong);
21382             this.errorMsg = this.errors.PwdLong;
21383             return false;
21384         }
21385         var strength;
21386         if (this.ClientSideStrongPassword(value)) {
21387             strength = 3;
21388         } else if (this.ClientSideMediumPassword(value)) {
21389             strength = 2;
21390         } else if (this.ClientSideWeakPassword(value)) {
21391             strength = 1;
21392         } else {
21393             strength = 0;
21394         }
21395
21396         
21397         if (strength < 2) {
21398             //this.markInvalid(this.errors.TooWeak);
21399             this.errorMsg = this.errors.TooWeak;
21400             //return false;
21401         }
21402         
21403         
21404         console.log('strength2: ' + strength);
21405         
21406         //var pm = this.trigger.child('div/div/div').dom;
21407         
21408         var pm = this.trigger.child('div/div');
21409         pm.removeClass(this.meterClass);
21410         pm.addClass(this.meterClass[strength]);
21411                 
21412         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21413                 
21414         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21415         
21416         this.errorMsg = ''; 
21417         return true;
21418     },
21419     // private
21420     CharacterSetChecks: function (type)
21421     {
21422         this.type = type;
21423         this.fResult = false;
21424     },
21425     // private
21426     isctype: function (character, type)
21427     {
21428         switch (type) {  
21429             case this.kCapitalLetter:
21430                 if (character >= 'A' && character <= 'Z') {
21431                     return true;
21432                 }
21433                 break;
21434             
21435             case this.kSmallLetter:
21436                 if (character >= 'a' && character <= 'z') {
21437                     return true;
21438                 }
21439                 break;
21440             
21441             case this.kDigit:
21442                 if (character >= '0' && character <= '9') {
21443                     return true;
21444                 }
21445                 break;
21446             
21447             case this.kPunctuation:
21448                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21449                     return true;
21450                 }
21451                 break;
21452             
21453             default:
21454                 return false;
21455         }
21456
21457     },
21458     // private
21459     IsLongEnough: function (pwd, size)
21460     {
21461         return !(pwd == null || isNaN(size) || pwd.length < size);
21462     },
21463     // private
21464     SpansEnoughCharacterSets: function (word, nb)
21465     {
21466         if (!this.IsLongEnough(word, nb))
21467         {
21468             return false;
21469         }
21470
21471         var characterSetChecks = new Array(
21472             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21473             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21474         );
21475         
21476         for (var index = 0; index < word.length; ++index) {
21477             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21478                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21479                     characterSetChecks[nCharSet].fResult = true;
21480                     break;
21481                 }
21482             }
21483         }
21484
21485         var nCharSets = 0;
21486         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21487             if (characterSetChecks[nCharSet].fResult) {
21488                 ++nCharSets;
21489             }
21490         }
21491
21492         if (nCharSets < nb) {
21493             return false;
21494         }
21495         return true;
21496     },
21497     // private
21498     ClientSideStrongPassword: function (pwd)
21499     {
21500         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21501     },
21502     // private
21503     ClientSideMediumPassword: function (pwd)
21504     {
21505         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21506     },
21507     // private
21508     ClientSideWeakPassword: function (pwd)
21509     {
21510         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21511     }
21512           
21513 })//<script type="text/javascript">
21514
21515 /*
21516  * Based  Ext JS Library 1.1.1
21517  * Copyright(c) 2006-2007, Ext JS, LLC.
21518  * LGPL
21519  *
21520  */
21521  
21522 /**
21523  * @class Roo.HtmlEditorCore
21524  * @extends Roo.Component
21525  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21526  *
21527  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21528  */
21529
21530 Roo.HtmlEditorCore = function(config){
21531     
21532     
21533     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21534     
21535     
21536     this.addEvents({
21537         /**
21538          * @event initialize
21539          * Fires when the editor is fully initialized (including the iframe)
21540          * @param {Roo.HtmlEditorCore} this
21541          */
21542         initialize: true,
21543         /**
21544          * @event activate
21545          * Fires when the editor is first receives the focus. Any insertion must wait
21546          * until after this event.
21547          * @param {Roo.HtmlEditorCore} this
21548          */
21549         activate: true,
21550          /**
21551          * @event beforesync
21552          * Fires before the textarea is updated with content from the editor iframe. Return false
21553          * to cancel the sync.
21554          * @param {Roo.HtmlEditorCore} this
21555          * @param {String} html
21556          */
21557         beforesync: true,
21558          /**
21559          * @event beforepush
21560          * Fires before the iframe editor is updated with content from the textarea. Return false
21561          * to cancel the push.
21562          * @param {Roo.HtmlEditorCore} this
21563          * @param {String} html
21564          */
21565         beforepush: true,
21566          /**
21567          * @event sync
21568          * Fires when the textarea is updated with content from the editor iframe.
21569          * @param {Roo.HtmlEditorCore} this
21570          * @param {String} html
21571          */
21572         sync: true,
21573          /**
21574          * @event push
21575          * Fires when the iframe editor is updated with content from the textarea.
21576          * @param {Roo.HtmlEditorCore} this
21577          * @param {String} html
21578          */
21579         push: true,
21580         
21581         /**
21582          * @event editorevent
21583          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21584          * @param {Roo.HtmlEditorCore} this
21585          */
21586         editorevent: true
21587         
21588     });
21589     
21590     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21591     
21592     // defaults : white / black...
21593     this.applyBlacklists();
21594     
21595     
21596     
21597 };
21598
21599
21600 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21601
21602
21603      /**
21604      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21605      */
21606     
21607     owner : false,
21608     
21609      /**
21610      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21611      *                        Roo.resizable.
21612      */
21613     resizable : false,
21614      /**
21615      * @cfg {Number} height (in pixels)
21616      */   
21617     height: 300,
21618    /**
21619      * @cfg {Number} width (in pixels)
21620      */   
21621     width: 500,
21622     
21623     /**
21624      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21625      * 
21626      */
21627     stylesheets: false,
21628     
21629     // id of frame..
21630     frameId: false,
21631     
21632     // private properties
21633     validationEvent : false,
21634     deferHeight: true,
21635     initialized : false,
21636     activated : false,
21637     sourceEditMode : false,
21638     onFocus : Roo.emptyFn,
21639     iframePad:3,
21640     hideMode:'offsets',
21641     
21642     clearUp: true,
21643     
21644     // blacklist + whitelisted elements..
21645     black: false,
21646     white: false,
21647      
21648     bodyCls : '',
21649
21650     /**
21651      * Protected method that will not generally be called directly. It
21652      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21653      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21654      */
21655     getDocMarkup : function(){
21656         // body styles..
21657         var st = '';
21658         
21659         // inherit styels from page...?? 
21660         if (this.stylesheets === false) {
21661             
21662             Roo.get(document.head).select('style').each(function(node) {
21663                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21664             });
21665             
21666             Roo.get(document.head).select('link').each(function(node) { 
21667                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21668             });
21669             
21670         } else if (!this.stylesheets.length) {
21671                 // simple..
21672                 st = '<style type="text/css">' +
21673                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21674                    '</style>';
21675         } else { 
21676             st = '<style type="text/css">' +
21677                     this.stylesheets +
21678                 '</style>';
21679         }
21680         
21681         st +=  '<style type="text/css">' +
21682             'IMG { cursor: pointer } ' +
21683         '</style>';
21684
21685         var cls = 'roo-htmleditor-body';
21686         
21687         if(this.bodyCls.length){
21688             cls += ' ' + this.bodyCls;
21689         }
21690         
21691         return '<html><head>' + st  +
21692             //<style type="text/css">' +
21693             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21694             //'</style>' +
21695             ' </head><body class="' +  cls + '"></body></html>';
21696     },
21697
21698     // private
21699     onRender : function(ct, position)
21700     {
21701         var _t = this;
21702         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21703         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21704         
21705         
21706         this.el.dom.style.border = '0 none';
21707         this.el.dom.setAttribute('tabIndex', -1);
21708         this.el.addClass('x-hidden hide');
21709         
21710         
21711         
21712         if(Roo.isIE){ // fix IE 1px bogus margin
21713             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21714         }
21715        
21716         
21717         this.frameId = Roo.id();
21718         
21719          
21720         
21721         var iframe = this.owner.wrap.createChild({
21722             tag: 'iframe',
21723             cls: 'form-control', // bootstrap..
21724             id: this.frameId,
21725             name: this.frameId,
21726             frameBorder : 'no',
21727             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21728         }, this.el
21729         );
21730         
21731         
21732         this.iframe = iframe.dom;
21733
21734          this.assignDocWin();
21735         
21736         this.doc.designMode = 'on';
21737        
21738         this.doc.open();
21739         this.doc.write(this.getDocMarkup());
21740         this.doc.close();
21741
21742         
21743         var task = { // must defer to wait for browser to be ready
21744             run : function(){
21745                 //console.log("run task?" + this.doc.readyState);
21746                 this.assignDocWin();
21747                 if(this.doc.body || this.doc.readyState == 'complete'){
21748                     try {
21749                         this.doc.designMode="on";
21750                     } catch (e) {
21751                         return;
21752                     }
21753                     Roo.TaskMgr.stop(task);
21754                     this.initEditor.defer(10, this);
21755                 }
21756             },
21757             interval : 10,
21758             duration: 10000,
21759             scope: this
21760         };
21761         Roo.TaskMgr.start(task);
21762
21763     },
21764
21765     // private
21766     onResize : function(w, h)
21767     {
21768          Roo.log('resize: ' +w + ',' + h );
21769         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21770         if(!this.iframe){
21771             return;
21772         }
21773         if(typeof w == 'number'){
21774             
21775             this.iframe.style.width = w + 'px';
21776         }
21777         if(typeof h == 'number'){
21778             
21779             this.iframe.style.height = h + 'px';
21780             if(this.doc){
21781                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21782             }
21783         }
21784         
21785     },
21786
21787     /**
21788      * Toggles the editor between standard and source edit mode.
21789      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21790      */
21791     toggleSourceEdit : function(sourceEditMode){
21792         
21793         this.sourceEditMode = sourceEditMode === true;
21794         
21795         if(this.sourceEditMode){
21796  
21797             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21798             
21799         }else{
21800             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21801             //this.iframe.className = '';
21802             this.deferFocus();
21803         }
21804         //this.setSize(this.owner.wrap.getSize());
21805         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21806     },
21807
21808     
21809   
21810
21811     /**
21812      * Protected method that will not generally be called directly. If you need/want
21813      * custom HTML cleanup, this is the method you should override.
21814      * @param {String} html The HTML to be cleaned
21815      * return {String} The cleaned HTML
21816      */
21817     cleanHtml : function(html){
21818         html = String(html);
21819         if(html.length > 5){
21820             if(Roo.isSafari){ // strip safari nonsense
21821                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21822             }
21823         }
21824         if(html == '&nbsp;'){
21825             html = '';
21826         }
21827         return html;
21828     },
21829
21830     /**
21831      * HTML Editor -> Textarea
21832      * Protected method that will not generally be called directly. Syncs the contents
21833      * of the editor iframe with the textarea.
21834      */
21835     syncValue : function(){
21836         if(this.initialized){
21837             var bd = (this.doc.body || this.doc.documentElement);
21838             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21839             var html = bd.innerHTML;
21840             if(Roo.isSafari){
21841                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21842                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21843                 if(m && m[1]){
21844                     html = '<div style="'+m[0]+'">' + html + '</div>';
21845                 }
21846             }
21847             html = this.cleanHtml(html);
21848             // fix up the special chars.. normaly like back quotes in word...
21849             // however we do not want to do this with chinese..
21850             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21851                 var cc = b.charCodeAt();
21852                 if (
21853                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21854                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21855                     (cc >= 0xf900 && cc < 0xfb00 )
21856                 ) {
21857                         return b;
21858                 }
21859                 return "&#"+cc+";" 
21860             });
21861             if(this.owner.fireEvent('beforesync', this, html) !== false){
21862                 this.el.dom.value = html;
21863                 this.owner.fireEvent('sync', this, html);
21864             }
21865         }
21866     },
21867
21868     /**
21869      * Protected method that will not generally be called directly. Pushes the value of the textarea
21870      * into the iframe editor.
21871      */
21872     pushValue : function(){
21873         if(this.initialized){
21874             var v = this.el.dom.value.trim();
21875             
21876 //            if(v.length < 1){
21877 //                v = '&#160;';
21878 //            }
21879             
21880             if(this.owner.fireEvent('beforepush', this, v) !== false){
21881                 var d = (this.doc.body || this.doc.documentElement);
21882                 d.innerHTML = v;
21883                 this.cleanUpPaste();
21884                 this.el.dom.value = d.innerHTML;
21885                 this.owner.fireEvent('push', this, v);
21886             }
21887         }
21888     },
21889
21890     // private
21891     deferFocus : function(){
21892         this.focus.defer(10, this);
21893     },
21894
21895     // doc'ed in Field
21896     focus : function(){
21897         if(this.win && !this.sourceEditMode){
21898             this.win.focus();
21899         }else{
21900             this.el.focus();
21901         }
21902     },
21903     
21904     assignDocWin: function()
21905     {
21906         var iframe = this.iframe;
21907         
21908          if(Roo.isIE){
21909             this.doc = iframe.contentWindow.document;
21910             this.win = iframe.contentWindow;
21911         } else {
21912 //            if (!Roo.get(this.frameId)) {
21913 //                return;
21914 //            }
21915 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21916 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21917             
21918             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21919                 return;
21920             }
21921             
21922             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21923             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21924         }
21925     },
21926     
21927     // private
21928     initEditor : function(){
21929         //console.log("INIT EDITOR");
21930         this.assignDocWin();
21931         
21932         
21933         
21934         this.doc.designMode="on";
21935         this.doc.open();
21936         this.doc.write(this.getDocMarkup());
21937         this.doc.close();
21938         
21939         var dbody = (this.doc.body || this.doc.documentElement);
21940         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21941         // this copies styles from the containing element into thsi one..
21942         // not sure why we need all of this..
21943         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21944         
21945         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21946         //ss['background-attachment'] = 'fixed'; // w3c
21947         dbody.bgProperties = 'fixed'; // ie
21948         //Roo.DomHelper.applyStyles(dbody, ss);
21949         Roo.EventManager.on(this.doc, {
21950             //'mousedown': this.onEditorEvent,
21951             'mouseup': this.onEditorEvent,
21952             'dblclick': this.onEditorEvent,
21953             'click': this.onEditorEvent,
21954             'keyup': this.onEditorEvent,
21955             buffer:100,
21956             scope: this
21957         });
21958         if(Roo.isGecko){
21959             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21960         }
21961         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21962             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21963         }
21964         this.initialized = true;
21965
21966         this.owner.fireEvent('initialize', this);
21967         this.pushValue();
21968     },
21969
21970     // private
21971     onDestroy : function(){
21972         
21973         
21974         
21975         if(this.rendered){
21976             
21977             //for (var i =0; i < this.toolbars.length;i++) {
21978             //    // fixme - ask toolbars for heights?
21979             //    this.toolbars[i].onDestroy();
21980            // }
21981             
21982             //this.wrap.dom.innerHTML = '';
21983             //this.wrap.remove();
21984         }
21985     },
21986
21987     // private
21988     onFirstFocus : function(){
21989         
21990         this.assignDocWin();
21991         
21992         
21993         this.activated = true;
21994          
21995     
21996         if(Roo.isGecko){ // prevent silly gecko errors
21997             this.win.focus();
21998             var s = this.win.getSelection();
21999             if(!s.focusNode || s.focusNode.nodeType != 3){
22000                 var r = s.getRangeAt(0);
22001                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22002                 r.collapse(true);
22003                 this.deferFocus();
22004             }
22005             try{
22006                 this.execCmd('useCSS', true);
22007                 this.execCmd('styleWithCSS', false);
22008             }catch(e){}
22009         }
22010         this.owner.fireEvent('activate', this);
22011     },
22012
22013     // private
22014     adjustFont: function(btn){
22015         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22016         //if(Roo.isSafari){ // safari
22017         //    adjust *= 2;
22018        // }
22019         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22020         if(Roo.isSafari){ // safari
22021             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22022             v =  (v < 10) ? 10 : v;
22023             v =  (v > 48) ? 48 : v;
22024             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22025             
22026         }
22027         
22028         
22029         v = Math.max(1, v+adjust);
22030         
22031         this.execCmd('FontSize', v  );
22032     },
22033
22034     onEditorEvent : function(e)
22035     {
22036         this.owner.fireEvent('editorevent', this, e);
22037       //  this.updateToolbar();
22038         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22039     },
22040
22041     insertTag : function(tg)
22042     {
22043         // could be a bit smarter... -> wrap the current selected tRoo..
22044         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22045             
22046             range = this.createRange(this.getSelection());
22047             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22048             wrappingNode.appendChild(range.extractContents());
22049             range.insertNode(wrappingNode);
22050
22051             return;
22052             
22053             
22054             
22055         }
22056         this.execCmd("formatblock",   tg);
22057         
22058     },
22059     
22060     insertText : function(txt)
22061     {
22062         
22063         
22064         var range = this.createRange();
22065         range.deleteContents();
22066                //alert(Sender.getAttribute('label'));
22067                
22068         range.insertNode(this.doc.createTextNode(txt));
22069     } ,
22070     
22071      
22072
22073     /**
22074      * Executes a Midas editor command on the editor document and performs necessary focus and
22075      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22076      * @param {String} cmd The Midas command
22077      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22078      */
22079     relayCmd : function(cmd, value){
22080         this.win.focus();
22081         this.execCmd(cmd, value);
22082         this.owner.fireEvent('editorevent', this);
22083         //this.updateToolbar();
22084         this.owner.deferFocus();
22085     },
22086
22087     /**
22088      * Executes a Midas editor command directly on the editor document.
22089      * For visual commands, you should use {@link #relayCmd} instead.
22090      * <b>This should only be called after the editor is initialized.</b>
22091      * @param {String} cmd The Midas command
22092      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22093      */
22094     execCmd : function(cmd, value){
22095         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22096         this.syncValue();
22097     },
22098  
22099  
22100    
22101     /**
22102      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22103      * to insert tRoo.
22104      * @param {String} text | dom node.. 
22105      */
22106     insertAtCursor : function(text)
22107     {
22108         
22109         if(!this.activated){
22110             return;
22111         }
22112         /*
22113         if(Roo.isIE){
22114             this.win.focus();
22115             var r = this.doc.selection.createRange();
22116             if(r){
22117                 r.collapse(true);
22118                 r.pasteHTML(text);
22119                 this.syncValue();
22120                 this.deferFocus();
22121             
22122             }
22123             return;
22124         }
22125         */
22126         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22127             this.win.focus();
22128             
22129             
22130             // from jquery ui (MIT licenced)
22131             var range, node;
22132             var win = this.win;
22133             
22134             if (win.getSelection && win.getSelection().getRangeAt) {
22135                 range = win.getSelection().getRangeAt(0);
22136                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22137                 range.insertNode(node);
22138             } else if (win.document.selection && win.document.selection.createRange) {
22139                 // no firefox support
22140                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22141                 win.document.selection.createRange().pasteHTML(txt);
22142             } else {
22143                 // no firefox support
22144                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22145                 this.execCmd('InsertHTML', txt);
22146             } 
22147             
22148             this.syncValue();
22149             
22150             this.deferFocus();
22151         }
22152     },
22153  // private
22154     mozKeyPress : function(e){
22155         if(e.ctrlKey){
22156             var c = e.getCharCode(), cmd;
22157           
22158             if(c > 0){
22159                 c = String.fromCharCode(c).toLowerCase();
22160                 switch(c){
22161                     case 'b':
22162                         cmd = 'bold';
22163                         break;
22164                     case 'i':
22165                         cmd = 'italic';
22166                         break;
22167                     
22168                     case 'u':
22169                         cmd = 'underline';
22170                         break;
22171                     
22172                     case 'v':
22173                         this.cleanUpPaste.defer(100, this);
22174                         return;
22175                         
22176                 }
22177                 if(cmd){
22178                     this.win.focus();
22179                     this.execCmd(cmd);
22180                     this.deferFocus();
22181                     e.preventDefault();
22182                 }
22183                 
22184             }
22185         }
22186     },
22187
22188     // private
22189     fixKeys : function(){ // load time branching for fastest keydown performance
22190         if(Roo.isIE){
22191             return function(e){
22192                 var k = e.getKey(), r;
22193                 if(k == e.TAB){
22194                     e.stopEvent();
22195                     r = this.doc.selection.createRange();
22196                     if(r){
22197                         r.collapse(true);
22198                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22199                         this.deferFocus();
22200                     }
22201                     return;
22202                 }
22203                 
22204                 if(k == e.ENTER){
22205                     r = this.doc.selection.createRange();
22206                     if(r){
22207                         var target = r.parentElement();
22208                         if(!target || target.tagName.toLowerCase() != 'li'){
22209                             e.stopEvent();
22210                             r.pasteHTML('<br />');
22211                             r.collapse(false);
22212                             r.select();
22213                         }
22214                     }
22215                 }
22216                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22217                     this.cleanUpPaste.defer(100, this);
22218                     return;
22219                 }
22220                 
22221                 
22222             };
22223         }else if(Roo.isOpera){
22224             return function(e){
22225                 var k = e.getKey();
22226                 if(k == e.TAB){
22227                     e.stopEvent();
22228                     this.win.focus();
22229                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22230                     this.deferFocus();
22231                 }
22232                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22233                     this.cleanUpPaste.defer(100, this);
22234                     return;
22235                 }
22236                 
22237             };
22238         }else if(Roo.isSafari){
22239             return function(e){
22240                 var k = e.getKey();
22241                 
22242                 if(k == e.TAB){
22243                     e.stopEvent();
22244                     this.execCmd('InsertText','\t');
22245                     this.deferFocus();
22246                     return;
22247                 }
22248                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22249                     this.cleanUpPaste.defer(100, this);
22250                     return;
22251                 }
22252                 
22253              };
22254         }
22255     }(),
22256     
22257     getAllAncestors: function()
22258     {
22259         var p = this.getSelectedNode();
22260         var a = [];
22261         if (!p) {
22262             a.push(p); // push blank onto stack..
22263             p = this.getParentElement();
22264         }
22265         
22266         
22267         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22268             a.push(p);
22269             p = p.parentNode;
22270         }
22271         a.push(this.doc.body);
22272         return a;
22273     },
22274     lastSel : false,
22275     lastSelNode : false,
22276     
22277     
22278     getSelection : function() 
22279     {
22280         this.assignDocWin();
22281         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22282     },
22283     
22284     getSelectedNode: function() 
22285     {
22286         // this may only work on Gecko!!!
22287         
22288         // should we cache this!!!!
22289         
22290         
22291         
22292          
22293         var range = this.createRange(this.getSelection()).cloneRange();
22294         
22295         if (Roo.isIE) {
22296             var parent = range.parentElement();
22297             while (true) {
22298                 var testRange = range.duplicate();
22299                 testRange.moveToElementText(parent);
22300                 if (testRange.inRange(range)) {
22301                     break;
22302                 }
22303                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22304                     break;
22305                 }
22306                 parent = parent.parentElement;
22307             }
22308             return parent;
22309         }
22310         
22311         // is ancestor a text element.
22312         var ac =  range.commonAncestorContainer;
22313         if (ac.nodeType == 3) {
22314             ac = ac.parentNode;
22315         }
22316         
22317         var ar = ac.childNodes;
22318          
22319         var nodes = [];
22320         var other_nodes = [];
22321         var has_other_nodes = false;
22322         for (var i=0;i<ar.length;i++) {
22323             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22324                 continue;
22325             }
22326             // fullly contained node.
22327             
22328             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22329                 nodes.push(ar[i]);
22330                 continue;
22331             }
22332             
22333             // probably selected..
22334             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22335                 other_nodes.push(ar[i]);
22336                 continue;
22337             }
22338             // outer..
22339             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22340                 continue;
22341             }
22342             
22343             
22344             has_other_nodes = true;
22345         }
22346         if (!nodes.length && other_nodes.length) {
22347             nodes= other_nodes;
22348         }
22349         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22350             return false;
22351         }
22352         
22353         return nodes[0];
22354     },
22355     createRange: function(sel)
22356     {
22357         // this has strange effects when using with 
22358         // top toolbar - not sure if it's a great idea.
22359         //this.editor.contentWindow.focus();
22360         if (typeof sel != "undefined") {
22361             try {
22362                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22363             } catch(e) {
22364                 return this.doc.createRange();
22365             }
22366         } else {
22367             return this.doc.createRange();
22368         }
22369     },
22370     getParentElement: function()
22371     {
22372         
22373         this.assignDocWin();
22374         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22375         
22376         var range = this.createRange(sel);
22377          
22378         try {
22379             var p = range.commonAncestorContainer;
22380             while (p.nodeType == 3) { // text node
22381                 p = p.parentNode;
22382             }
22383             return p;
22384         } catch (e) {
22385             return null;
22386         }
22387     
22388     },
22389     /***
22390      *
22391      * Range intersection.. the hard stuff...
22392      *  '-1' = before
22393      *  '0' = hits..
22394      *  '1' = after.
22395      *         [ -- selected range --- ]
22396      *   [fail]                        [fail]
22397      *
22398      *    basically..
22399      *      if end is before start or  hits it. fail.
22400      *      if start is after end or hits it fail.
22401      *
22402      *   if either hits (but other is outside. - then it's not 
22403      *   
22404      *    
22405      **/
22406     
22407     
22408     // @see http://www.thismuchiknow.co.uk/?p=64.
22409     rangeIntersectsNode : function(range, node)
22410     {
22411         var nodeRange = node.ownerDocument.createRange();
22412         try {
22413             nodeRange.selectNode(node);
22414         } catch (e) {
22415             nodeRange.selectNodeContents(node);
22416         }
22417     
22418         var rangeStartRange = range.cloneRange();
22419         rangeStartRange.collapse(true);
22420     
22421         var rangeEndRange = range.cloneRange();
22422         rangeEndRange.collapse(false);
22423     
22424         var nodeStartRange = nodeRange.cloneRange();
22425         nodeStartRange.collapse(true);
22426     
22427         var nodeEndRange = nodeRange.cloneRange();
22428         nodeEndRange.collapse(false);
22429     
22430         return rangeStartRange.compareBoundaryPoints(
22431                  Range.START_TO_START, nodeEndRange) == -1 &&
22432                rangeEndRange.compareBoundaryPoints(
22433                  Range.START_TO_START, nodeStartRange) == 1;
22434         
22435          
22436     },
22437     rangeCompareNode : function(range, node)
22438     {
22439         var nodeRange = node.ownerDocument.createRange();
22440         try {
22441             nodeRange.selectNode(node);
22442         } catch (e) {
22443             nodeRange.selectNodeContents(node);
22444         }
22445         
22446         
22447         range.collapse(true);
22448     
22449         nodeRange.collapse(true);
22450      
22451         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22452         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22453          
22454         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22455         
22456         var nodeIsBefore   =  ss == 1;
22457         var nodeIsAfter    = ee == -1;
22458         
22459         if (nodeIsBefore && nodeIsAfter) {
22460             return 0; // outer
22461         }
22462         if (!nodeIsBefore && nodeIsAfter) {
22463             return 1; //right trailed.
22464         }
22465         
22466         if (nodeIsBefore && !nodeIsAfter) {
22467             return 2;  // left trailed.
22468         }
22469         // fully contined.
22470         return 3;
22471     },
22472
22473     // private? - in a new class?
22474     cleanUpPaste :  function()
22475     {
22476         // cleans up the whole document..
22477         Roo.log('cleanuppaste');
22478         
22479         this.cleanUpChildren(this.doc.body);
22480         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22481         if (clean != this.doc.body.innerHTML) {
22482             this.doc.body.innerHTML = clean;
22483         }
22484         
22485     },
22486     
22487     cleanWordChars : function(input) {// change the chars to hex code
22488         var he = Roo.HtmlEditorCore;
22489         
22490         var output = input;
22491         Roo.each(he.swapCodes, function(sw) { 
22492             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22493             
22494             output = output.replace(swapper, sw[1]);
22495         });
22496         
22497         return output;
22498     },
22499     
22500     
22501     cleanUpChildren : function (n)
22502     {
22503         if (!n.childNodes.length) {
22504             return;
22505         }
22506         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22507            this.cleanUpChild(n.childNodes[i]);
22508         }
22509     },
22510     
22511     
22512         
22513     
22514     cleanUpChild : function (node)
22515     {
22516         var ed = this;
22517         //console.log(node);
22518         if (node.nodeName == "#text") {
22519             // clean up silly Windows -- stuff?
22520             return; 
22521         }
22522         if (node.nodeName == "#comment") {
22523             node.parentNode.removeChild(node);
22524             // clean up silly Windows -- stuff?
22525             return; 
22526         }
22527         var lcname = node.tagName.toLowerCase();
22528         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22529         // whitelist of tags..
22530         
22531         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22532             // remove node.
22533             node.parentNode.removeChild(node);
22534             return;
22535             
22536         }
22537         
22538         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22539         
22540         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22541         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22542         
22543         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22544         //    remove_keep_children = true;
22545         //}
22546         
22547         if (remove_keep_children) {
22548             this.cleanUpChildren(node);
22549             // inserts everything just before this node...
22550             while (node.childNodes.length) {
22551                 var cn = node.childNodes[0];
22552                 node.removeChild(cn);
22553                 node.parentNode.insertBefore(cn, node);
22554             }
22555             node.parentNode.removeChild(node);
22556             return;
22557         }
22558         
22559         if (!node.attributes || !node.attributes.length) {
22560             this.cleanUpChildren(node);
22561             return;
22562         }
22563         
22564         function cleanAttr(n,v)
22565         {
22566             
22567             if (v.match(/^\./) || v.match(/^\//)) {
22568                 return;
22569             }
22570             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22571                 return;
22572             }
22573             if (v.match(/^#/)) {
22574                 return;
22575             }
22576 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22577             node.removeAttribute(n);
22578             
22579         }
22580         
22581         var cwhite = this.cwhite;
22582         var cblack = this.cblack;
22583             
22584         function cleanStyle(n,v)
22585         {
22586             if (v.match(/expression/)) { //XSS?? should we even bother..
22587                 node.removeAttribute(n);
22588                 return;
22589             }
22590             
22591             var parts = v.split(/;/);
22592             var clean = [];
22593             
22594             Roo.each(parts, function(p) {
22595                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22596                 if (!p.length) {
22597                     return true;
22598                 }
22599                 var l = p.split(':').shift().replace(/\s+/g,'');
22600                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22601                 
22602                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22603 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22604                     //node.removeAttribute(n);
22605                     return true;
22606                 }
22607                 //Roo.log()
22608                 // only allow 'c whitelisted system attributes'
22609                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22610 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22611                     //node.removeAttribute(n);
22612                     return true;
22613                 }
22614                 
22615                 
22616                  
22617                 
22618                 clean.push(p);
22619                 return true;
22620             });
22621             if (clean.length) { 
22622                 node.setAttribute(n, clean.join(';'));
22623             } else {
22624                 node.removeAttribute(n);
22625             }
22626             
22627         }
22628         
22629         
22630         for (var i = node.attributes.length-1; i > -1 ; i--) {
22631             var a = node.attributes[i];
22632             //console.log(a);
22633             
22634             if (a.name.toLowerCase().substr(0,2)=='on')  {
22635                 node.removeAttribute(a.name);
22636                 continue;
22637             }
22638             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22639                 node.removeAttribute(a.name);
22640                 continue;
22641             }
22642             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22643                 cleanAttr(a.name,a.value); // fixme..
22644                 continue;
22645             }
22646             if (a.name == 'style') {
22647                 cleanStyle(a.name,a.value);
22648                 continue;
22649             }
22650             /// clean up MS crap..
22651             // tecnically this should be a list of valid class'es..
22652             
22653             
22654             if (a.name == 'class') {
22655                 if (a.value.match(/^Mso/)) {
22656                     node.className = '';
22657                 }
22658                 
22659                 if (a.value.match(/^body$/)) {
22660                     node.className = '';
22661                 }
22662                 continue;
22663             }
22664             
22665             // style cleanup!?
22666             // class cleanup?
22667             
22668         }
22669         
22670         
22671         this.cleanUpChildren(node);
22672         
22673         
22674     },
22675     
22676     /**
22677      * Clean up MS wordisms...
22678      */
22679     cleanWord : function(node)
22680     {
22681         
22682         
22683         if (!node) {
22684             this.cleanWord(this.doc.body);
22685             return;
22686         }
22687         if (node.nodeName == "#text") {
22688             // clean up silly Windows -- stuff?
22689             return; 
22690         }
22691         if (node.nodeName == "#comment") {
22692             node.parentNode.removeChild(node);
22693             // clean up silly Windows -- stuff?
22694             return; 
22695         }
22696         
22697         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22698             node.parentNode.removeChild(node);
22699             return;
22700         }
22701         
22702         // remove - but keep children..
22703         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22704             while (node.childNodes.length) {
22705                 var cn = node.childNodes[0];
22706                 node.removeChild(cn);
22707                 node.parentNode.insertBefore(cn, node);
22708             }
22709             node.parentNode.removeChild(node);
22710             this.iterateChildren(node, this.cleanWord);
22711             return;
22712         }
22713         // clean styles
22714         if (node.className.length) {
22715             
22716             var cn = node.className.split(/\W+/);
22717             var cna = [];
22718             Roo.each(cn, function(cls) {
22719                 if (cls.match(/Mso[a-zA-Z]+/)) {
22720                     return;
22721                 }
22722                 cna.push(cls);
22723             });
22724             node.className = cna.length ? cna.join(' ') : '';
22725             if (!cna.length) {
22726                 node.removeAttribute("class");
22727             }
22728         }
22729         
22730         if (node.hasAttribute("lang")) {
22731             node.removeAttribute("lang");
22732         }
22733         
22734         if (node.hasAttribute("style")) {
22735             
22736             var styles = node.getAttribute("style").split(";");
22737             var nstyle = [];
22738             Roo.each(styles, function(s) {
22739                 if (!s.match(/:/)) {
22740                     return;
22741                 }
22742                 var kv = s.split(":");
22743                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22744                     return;
22745                 }
22746                 // what ever is left... we allow.
22747                 nstyle.push(s);
22748             });
22749             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22750             if (!nstyle.length) {
22751                 node.removeAttribute('style');
22752             }
22753         }
22754         this.iterateChildren(node, this.cleanWord);
22755         
22756         
22757         
22758     },
22759     /**
22760      * iterateChildren of a Node, calling fn each time, using this as the scole..
22761      * @param {DomNode} node node to iterate children of.
22762      * @param {Function} fn method of this class to call on each item.
22763      */
22764     iterateChildren : function(node, fn)
22765     {
22766         if (!node.childNodes.length) {
22767                 return;
22768         }
22769         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22770            fn.call(this, node.childNodes[i])
22771         }
22772     },
22773     
22774     
22775     /**
22776      * cleanTableWidths.
22777      *
22778      * Quite often pasting from word etc.. results in tables with column and widths.
22779      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22780      *
22781      */
22782     cleanTableWidths : function(node)
22783     {
22784          
22785          
22786         if (!node) {
22787             this.cleanTableWidths(this.doc.body);
22788             return;
22789         }
22790         
22791         // ignore list...
22792         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22793             return; 
22794         }
22795         Roo.log(node.tagName);
22796         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22797             this.iterateChildren(node, this.cleanTableWidths);
22798             return;
22799         }
22800         if (node.hasAttribute('width')) {
22801             node.removeAttribute('width');
22802         }
22803         
22804          
22805         if (node.hasAttribute("style")) {
22806             // pretty basic...
22807             
22808             var styles = node.getAttribute("style").split(";");
22809             var nstyle = [];
22810             Roo.each(styles, function(s) {
22811                 if (!s.match(/:/)) {
22812                     return;
22813                 }
22814                 var kv = s.split(":");
22815                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22816                     return;
22817                 }
22818                 // what ever is left... we allow.
22819                 nstyle.push(s);
22820             });
22821             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22822             if (!nstyle.length) {
22823                 node.removeAttribute('style');
22824             }
22825         }
22826         
22827         this.iterateChildren(node, this.cleanTableWidths);
22828         
22829         
22830     },
22831     
22832     
22833     
22834     
22835     domToHTML : function(currentElement, depth, nopadtext) {
22836         
22837         depth = depth || 0;
22838         nopadtext = nopadtext || false;
22839     
22840         if (!currentElement) {
22841             return this.domToHTML(this.doc.body);
22842         }
22843         
22844         //Roo.log(currentElement);
22845         var j;
22846         var allText = false;
22847         var nodeName = currentElement.nodeName;
22848         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22849         
22850         if  (nodeName == '#text') {
22851             
22852             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22853         }
22854         
22855         
22856         var ret = '';
22857         if (nodeName != 'BODY') {
22858              
22859             var i = 0;
22860             // Prints the node tagName, such as <A>, <IMG>, etc
22861             if (tagName) {
22862                 var attr = [];
22863                 for(i = 0; i < currentElement.attributes.length;i++) {
22864                     // quoting?
22865                     var aname = currentElement.attributes.item(i).name;
22866                     if (!currentElement.attributes.item(i).value.length) {
22867                         continue;
22868                     }
22869                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22870                 }
22871                 
22872                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22873             } 
22874             else {
22875                 
22876                 // eack
22877             }
22878         } else {
22879             tagName = false;
22880         }
22881         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22882             return ret;
22883         }
22884         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22885             nopadtext = true;
22886         }
22887         
22888         
22889         // Traverse the tree
22890         i = 0;
22891         var currentElementChild = currentElement.childNodes.item(i);
22892         var allText = true;
22893         var innerHTML  = '';
22894         lastnode = '';
22895         while (currentElementChild) {
22896             // Formatting code (indent the tree so it looks nice on the screen)
22897             var nopad = nopadtext;
22898             if (lastnode == 'SPAN') {
22899                 nopad  = true;
22900             }
22901             // text
22902             if  (currentElementChild.nodeName == '#text') {
22903                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22904                 toadd = nopadtext ? toadd : toadd.trim();
22905                 if (!nopad && toadd.length > 80) {
22906                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22907                 }
22908                 innerHTML  += toadd;
22909                 
22910                 i++;
22911                 currentElementChild = currentElement.childNodes.item(i);
22912                 lastNode = '';
22913                 continue;
22914             }
22915             allText = false;
22916             
22917             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22918                 
22919             // Recursively traverse the tree structure of the child node
22920             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22921             lastnode = currentElementChild.nodeName;
22922             i++;
22923             currentElementChild=currentElement.childNodes.item(i);
22924         }
22925         
22926         ret += innerHTML;
22927         
22928         if (!allText) {
22929                 // The remaining code is mostly for formatting the tree
22930             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22931         }
22932         
22933         
22934         if (tagName) {
22935             ret+= "</"+tagName+">";
22936         }
22937         return ret;
22938         
22939     },
22940         
22941     applyBlacklists : function()
22942     {
22943         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22944         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22945         
22946         this.white = [];
22947         this.black = [];
22948         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22949             if (b.indexOf(tag) > -1) {
22950                 return;
22951             }
22952             this.white.push(tag);
22953             
22954         }, this);
22955         
22956         Roo.each(w, function(tag) {
22957             if (b.indexOf(tag) > -1) {
22958                 return;
22959             }
22960             if (this.white.indexOf(tag) > -1) {
22961                 return;
22962             }
22963             this.white.push(tag);
22964             
22965         }, this);
22966         
22967         
22968         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22969             if (w.indexOf(tag) > -1) {
22970                 return;
22971             }
22972             this.black.push(tag);
22973             
22974         }, this);
22975         
22976         Roo.each(b, function(tag) {
22977             if (w.indexOf(tag) > -1) {
22978                 return;
22979             }
22980             if (this.black.indexOf(tag) > -1) {
22981                 return;
22982             }
22983             this.black.push(tag);
22984             
22985         }, this);
22986         
22987         
22988         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22989         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22990         
22991         this.cwhite = [];
22992         this.cblack = [];
22993         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22994             if (b.indexOf(tag) > -1) {
22995                 return;
22996             }
22997             this.cwhite.push(tag);
22998             
22999         }, this);
23000         
23001         Roo.each(w, function(tag) {
23002             if (b.indexOf(tag) > -1) {
23003                 return;
23004             }
23005             if (this.cwhite.indexOf(tag) > -1) {
23006                 return;
23007             }
23008             this.cwhite.push(tag);
23009             
23010         }, this);
23011         
23012         
23013         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23014             if (w.indexOf(tag) > -1) {
23015                 return;
23016             }
23017             this.cblack.push(tag);
23018             
23019         }, this);
23020         
23021         Roo.each(b, function(tag) {
23022             if (w.indexOf(tag) > -1) {
23023                 return;
23024             }
23025             if (this.cblack.indexOf(tag) > -1) {
23026                 return;
23027             }
23028             this.cblack.push(tag);
23029             
23030         }, this);
23031     },
23032     
23033     setStylesheets : function(stylesheets)
23034     {
23035         if(typeof(stylesheets) == 'string'){
23036             Roo.get(this.iframe.contentDocument.head).createChild({
23037                 tag : 'link',
23038                 rel : 'stylesheet',
23039                 type : 'text/css',
23040                 href : stylesheets
23041             });
23042             
23043             return;
23044         }
23045         var _this = this;
23046      
23047         Roo.each(stylesheets, function(s) {
23048             if(!s.length){
23049                 return;
23050             }
23051             
23052             Roo.get(_this.iframe.contentDocument.head).createChild({
23053                 tag : 'link',
23054                 rel : 'stylesheet',
23055                 type : 'text/css',
23056                 href : s
23057             });
23058         });
23059
23060         
23061     },
23062     
23063     removeStylesheets : function()
23064     {
23065         var _this = this;
23066         
23067         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23068             s.remove();
23069         });
23070     },
23071     
23072     setStyle : function(style)
23073     {
23074         Roo.get(this.iframe.contentDocument.head).createChild({
23075             tag : 'style',
23076             type : 'text/css',
23077             html : style
23078         });
23079
23080         return;
23081     }
23082     
23083     // hide stuff that is not compatible
23084     /**
23085      * @event blur
23086      * @hide
23087      */
23088     /**
23089      * @event change
23090      * @hide
23091      */
23092     /**
23093      * @event focus
23094      * @hide
23095      */
23096     /**
23097      * @event specialkey
23098      * @hide
23099      */
23100     /**
23101      * @cfg {String} fieldClass @hide
23102      */
23103     /**
23104      * @cfg {String} focusClass @hide
23105      */
23106     /**
23107      * @cfg {String} autoCreate @hide
23108      */
23109     /**
23110      * @cfg {String} inputType @hide
23111      */
23112     /**
23113      * @cfg {String} invalidClass @hide
23114      */
23115     /**
23116      * @cfg {String} invalidText @hide
23117      */
23118     /**
23119      * @cfg {String} msgFx @hide
23120      */
23121     /**
23122      * @cfg {String} validateOnBlur @hide
23123      */
23124 });
23125
23126 Roo.HtmlEditorCore.white = [
23127         'area', 'br', 'img', 'input', 'hr', 'wbr',
23128         
23129        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23130        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23131        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23132        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23133        'table',   'ul',         'xmp', 
23134        
23135        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23136       'thead',   'tr', 
23137      
23138       'dir', 'menu', 'ol', 'ul', 'dl',
23139        
23140       'embed',  'object'
23141 ];
23142
23143
23144 Roo.HtmlEditorCore.black = [
23145     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23146         'applet', // 
23147         'base',   'basefont', 'bgsound', 'blink',  'body', 
23148         'frame',  'frameset', 'head',    'html',   'ilayer', 
23149         'iframe', 'layer',  'link',     'meta',    'object',   
23150         'script', 'style' ,'title',  'xml' // clean later..
23151 ];
23152 Roo.HtmlEditorCore.clean = [
23153     'script', 'style', 'title', 'xml'
23154 ];
23155 Roo.HtmlEditorCore.remove = [
23156     'font'
23157 ];
23158 // attributes..
23159
23160 Roo.HtmlEditorCore.ablack = [
23161     'on'
23162 ];
23163     
23164 Roo.HtmlEditorCore.aclean = [ 
23165     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23166 ];
23167
23168 // protocols..
23169 Roo.HtmlEditorCore.pwhite= [
23170         'http',  'https',  'mailto'
23171 ];
23172
23173 // white listed style attributes.
23174 Roo.HtmlEditorCore.cwhite= [
23175       //  'text-align', /// default is to allow most things..
23176       
23177          
23178 //        'font-size'//??
23179 ];
23180
23181 // black listed style attributes.
23182 Roo.HtmlEditorCore.cblack= [
23183       //  'font-size' -- this can be set by the project 
23184 ];
23185
23186
23187 Roo.HtmlEditorCore.swapCodes   =[ 
23188     [    8211, "--" ], 
23189     [    8212, "--" ], 
23190     [    8216,  "'" ],  
23191     [    8217, "'" ],  
23192     [    8220, '"' ],  
23193     [    8221, '"' ],  
23194     [    8226, "*" ],  
23195     [    8230, "..." ]
23196 ]; 
23197
23198     /*
23199  * - LGPL
23200  *
23201  * HtmlEditor
23202  * 
23203  */
23204
23205 /**
23206  * @class Roo.bootstrap.HtmlEditor
23207  * @extends Roo.bootstrap.TextArea
23208  * Bootstrap HtmlEditor class
23209
23210  * @constructor
23211  * Create a new HtmlEditor
23212  * @param {Object} config The config object
23213  */
23214
23215 Roo.bootstrap.HtmlEditor = function(config){
23216     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23217     if (!this.toolbars) {
23218         this.toolbars = [];
23219     }
23220     
23221     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23222     this.addEvents({
23223             /**
23224              * @event initialize
23225              * Fires when the editor is fully initialized (including the iframe)
23226              * @param {HtmlEditor} this
23227              */
23228             initialize: true,
23229             /**
23230              * @event activate
23231              * Fires when the editor is first receives the focus. Any insertion must wait
23232              * until after this event.
23233              * @param {HtmlEditor} this
23234              */
23235             activate: true,
23236              /**
23237              * @event beforesync
23238              * Fires before the textarea is updated with content from the editor iframe. Return false
23239              * to cancel the sync.
23240              * @param {HtmlEditor} this
23241              * @param {String} html
23242              */
23243             beforesync: true,
23244              /**
23245              * @event beforepush
23246              * Fires before the iframe editor is updated with content from the textarea. Return false
23247              * to cancel the push.
23248              * @param {HtmlEditor} this
23249              * @param {String} html
23250              */
23251             beforepush: true,
23252              /**
23253              * @event sync
23254              * Fires when the textarea is updated with content from the editor iframe.
23255              * @param {HtmlEditor} this
23256              * @param {String} html
23257              */
23258             sync: true,
23259              /**
23260              * @event push
23261              * Fires when the iframe editor is updated with content from the textarea.
23262              * @param {HtmlEditor} this
23263              * @param {String} html
23264              */
23265             push: true,
23266              /**
23267              * @event editmodechange
23268              * Fires when the editor switches edit modes
23269              * @param {HtmlEditor} this
23270              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23271              */
23272             editmodechange: true,
23273             /**
23274              * @event editorevent
23275              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23276              * @param {HtmlEditor} this
23277              */
23278             editorevent: true,
23279             /**
23280              * @event firstfocus
23281              * Fires when on first focus - needed by toolbars..
23282              * @param {HtmlEditor} this
23283              */
23284             firstfocus: true,
23285             /**
23286              * @event autosave
23287              * Auto save the htmlEditor value as a file into Events
23288              * @param {HtmlEditor} this
23289              */
23290             autosave: true,
23291             /**
23292              * @event savedpreview
23293              * preview the saved version of htmlEditor
23294              * @param {HtmlEditor} this
23295              */
23296             savedpreview: true
23297         });
23298 };
23299
23300
23301 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23302     
23303     
23304       /**
23305      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23306      */
23307     toolbars : false,
23308     
23309      /**
23310     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23311     */
23312     btns : [],
23313    
23314      /**
23315      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23316      *                        Roo.resizable.
23317      */
23318     resizable : false,
23319      /**
23320      * @cfg {Number} height (in pixels)
23321      */   
23322     height: 300,
23323    /**
23324      * @cfg {Number} width (in pixels)
23325      */   
23326     width: false,
23327     
23328     /**
23329      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23330      * 
23331      */
23332     stylesheets: false,
23333     
23334     // id of frame..
23335     frameId: false,
23336     
23337     // private properties
23338     validationEvent : false,
23339     deferHeight: true,
23340     initialized : false,
23341     activated : false,
23342     
23343     onFocus : Roo.emptyFn,
23344     iframePad:3,
23345     hideMode:'offsets',
23346     
23347     tbContainer : false,
23348     
23349     bodyCls : '',
23350     
23351     toolbarContainer :function() {
23352         return this.wrap.select('.x-html-editor-tb',true).first();
23353     },
23354
23355     /**
23356      * Protected method that will not generally be called directly. It
23357      * is called when the editor creates its toolbar. Override this method if you need to
23358      * add custom toolbar buttons.
23359      * @param {HtmlEditor} editor
23360      */
23361     createToolbar : function(){
23362         Roo.log('renewing');
23363         Roo.log("create toolbars");
23364         
23365         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23366         this.toolbars[0].render(this.toolbarContainer());
23367         
23368         return;
23369         
23370 //        if (!editor.toolbars || !editor.toolbars.length) {
23371 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23372 //        }
23373 //        
23374 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23375 //            editor.toolbars[i] = Roo.factory(
23376 //                    typeof(editor.toolbars[i]) == 'string' ?
23377 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23378 //                Roo.bootstrap.HtmlEditor);
23379 //            editor.toolbars[i].init(editor);
23380 //        }
23381     },
23382
23383      
23384     // private
23385     onRender : function(ct, position)
23386     {
23387        // Roo.log("Call onRender: " + this.xtype);
23388         var _t = this;
23389         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23390       
23391         this.wrap = this.inputEl().wrap({
23392             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23393         });
23394         
23395         this.editorcore.onRender(ct, position);
23396          
23397         if (this.resizable) {
23398             this.resizeEl = new Roo.Resizable(this.wrap, {
23399                 pinned : true,
23400                 wrap: true,
23401                 dynamic : true,
23402                 minHeight : this.height,
23403                 height: this.height,
23404                 handles : this.resizable,
23405                 width: this.width,
23406                 listeners : {
23407                     resize : function(r, w, h) {
23408                         _t.onResize(w,h); // -something
23409                     }
23410                 }
23411             });
23412             
23413         }
23414         this.createToolbar(this);
23415        
23416         
23417         if(!this.width && this.resizable){
23418             this.setSize(this.wrap.getSize());
23419         }
23420         if (this.resizeEl) {
23421             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23422             // should trigger onReize..
23423         }
23424         
23425     },
23426
23427     // private
23428     onResize : function(w, h)
23429     {
23430         Roo.log('resize: ' +w + ',' + h );
23431         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23432         var ew = false;
23433         var eh = false;
23434         
23435         if(this.inputEl() ){
23436             if(typeof w == 'number'){
23437                 var aw = w - this.wrap.getFrameWidth('lr');
23438                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23439                 ew = aw;
23440             }
23441             if(typeof h == 'number'){
23442                  var tbh = -11;  // fixme it needs to tool bar size!
23443                 for (var i =0; i < this.toolbars.length;i++) {
23444                     // fixme - ask toolbars for heights?
23445                     tbh += this.toolbars[i].el.getHeight();
23446                     //if (this.toolbars[i].footer) {
23447                     //    tbh += this.toolbars[i].footer.el.getHeight();
23448                     //}
23449                 }
23450               
23451                 
23452                 
23453                 
23454                 
23455                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23456                 ah -= 5; // knock a few pixes off for look..
23457                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23458                 var eh = ah;
23459             }
23460         }
23461         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23462         this.editorcore.onResize(ew,eh);
23463         
23464     },
23465
23466     /**
23467      * Toggles the editor between standard and source edit mode.
23468      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23469      */
23470     toggleSourceEdit : function(sourceEditMode)
23471     {
23472         this.editorcore.toggleSourceEdit(sourceEditMode);
23473         
23474         if(this.editorcore.sourceEditMode){
23475             Roo.log('editor - showing textarea');
23476             
23477 //            Roo.log('in');
23478 //            Roo.log(this.syncValue());
23479             this.syncValue();
23480             this.inputEl().removeClass(['hide', 'x-hidden']);
23481             this.inputEl().dom.removeAttribute('tabIndex');
23482             this.inputEl().focus();
23483         }else{
23484             Roo.log('editor - hiding textarea');
23485 //            Roo.log('out')
23486 //            Roo.log(this.pushValue()); 
23487             this.pushValue();
23488             
23489             this.inputEl().addClass(['hide', 'x-hidden']);
23490             this.inputEl().dom.setAttribute('tabIndex', -1);
23491             //this.deferFocus();
23492         }
23493          
23494         if(this.resizable){
23495             this.setSize(this.wrap.getSize());
23496         }
23497         
23498         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23499     },
23500  
23501     // private (for BoxComponent)
23502     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23503
23504     // private (for BoxComponent)
23505     getResizeEl : function(){
23506         return this.wrap;
23507     },
23508
23509     // private (for BoxComponent)
23510     getPositionEl : function(){
23511         return this.wrap;
23512     },
23513
23514     // private
23515     initEvents : function(){
23516         this.originalValue = this.getValue();
23517     },
23518
23519 //    /**
23520 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23521 //     * @method
23522 //     */
23523 //    markInvalid : Roo.emptyFn,
23524 //    /**
23525 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23526 //     * @method
23527 //     */
23528 //    clearInvalid : Roo.emptyFn,
23529
23530     setValue : function(v){
23531         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23532         this.editorcore.pushValue();
23533     },
23534
23535      
23536     // private
23537     deferFocus : function(){
23538         this.focus.defer(10, this);
23539     },
23540
23541     // doc'ed in Field
23542     focus : function(){
23543         this.editorcore.focus();
23544         
23545     },
23546       
23547
23548     // private
23549     onDestroy : function(){
23550         
23551         
23552         
23553         if(this.rendered){
23554             
23555             for (var i =0; i < this.toolbars.length;i++) {
23556                 // fixme - ask toolbars for heights?
23557                 this.toolbars[i].onDestroy();
23558             }
23559             
23560             this.wrap.dom.innerHTML = '';
23561             this.wrap.remove();
23562         }
23563     },
23564
23565     // private
23566     onFirstFocus : function(){
23567         //Roo.log("onFirstFocus");
23568         this.editorcore.onFirstFocus();
23569          for (var i =0; i < this.toolbars.length;i++) {
23570             this.toolbars[i].onFirstFocus();
23571         }
23572         
23573     },
23574     
23575     // private
23576     syncValue : function()
23577     {   
23578         this.editorcore.syncValue();
23579     },
23580     
23581     pushValue : function()
23582     {   
23583         this.editorcore.pushValue();
23584     }
23585      
23586     
23587     // hide stuff that is not compatible
23588     /**
23589      * @event blur
23590      * @hide
23591      */
23592     /**
23593      * @event change
23594      * @hide
23595      */
23596     /**
23597      * @event focus
23598      * @hide
23599      */
23600     /**
23601      * @event specialkey
23602      * @hide
23603      */
23604     /**
23605      * @cfg {String} fieldClass @hide
23606      */
23607     /**
23608      * @cfg {String} focusClass @hide
23609      */
23610     /**
23611      * @cfg {String} autoCreate @hide
23612      */
23613     /**
23614      * @cfg {String} inputType @hide
23615      */
23616     /**
23617      * @cfg {String} invalidClass @hide
23618      */
23619     /**
23620      * @cfg {String} invalidText @hide
23621      */
23622     /**
23623      * @cfg {String} msgFx @hide
23624      */
23625     /**
23626      * @cfg {String} validateOnBlur @hide
23627      */
23628 });
23629  
23630     
23631    
23632    
23633    
23634       
23635 Roo.namespace('Roo.bootstrap.htmleditor');
23636 /**
23637  * @class Roo.bootstrap.HtmlEditorToolbar1
23638  * Basic Toolbar
23639  * 
23640  * Usage:
23641  *
23642  new Roo.bootstrap.HtmlEditor({
23643     ....
23644     toolbars : [
23645         new Roo.bootstrap.HtmlEditorToolbar1({
23646             disable : { fonts: 1 , format: 1, ..., ... , ...],
23647             btns : [ .... ]
23648         })
23649     }
23650      
23651  * 
23652  * @cfg {Object} disable List of elements to disable..
23653  * @cfg {Array} btns List of additional buttons.
23654  * 
23655  * 
23656  * NEEDS Extra CSS? 
23657  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23658  */
23659  
23660 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23661 {
23662     
23663     Roo.apply(this, config);
23664     
23665     // default disabled, based on 'good practice'..
23666     this.disable = this.disable || {};
23667     Roo.applyIf(this.disable, {
23668         fontSize : true,
23669         colors : true,
23670         specialElements : true
23671     });
23672     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23673     
23674     this.editor = config.editor;
23675     this.editorcore = config.editor.editorcore;
23676     
23677     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23678     
23679     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23680     // dont call parent... till later.
23681 }
23682 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23683      
23684     bar : true,
23685     
23686     editor : false,
23687     editorcore : false,
23688     
23689     
23690     formats : [
23691         "p" ,  
23692         "h1","h2","h3","h4","h5","h6", 
23693         "pre", "code", 
23694         "abbr", "acronym", "address", "cite", "samp", "var",
23695         'div','span'
23696     ],
23697     
23698     onRender : function(ct, position)
23699     {
23700        // Roo.log("Call onRender: " + this.xtype);
23701         
23702        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23703        Roo.log(this.el);
23704        this.el.dom.style.marginBottom = '0';
23705        var _this = this;
23706        var editorcore = this.editorcore;
23707        var editor= this.editor;
23708        
23709        var children = [];
23710        var btn = function(id,cmd , toggle, handler, html){
23711        
23712             var  event = toggle ? 'toggle' : 'click';
23713        
23714             var a = {
23715                 size : 'sm',
23716                 xtype: 'Button',
23717                 xns: Roo.bootstrap,
23718                 glyphicon : id,
23719                 cmd : id || cmd,
23720                 enableToggle:toggle !== false,
23721                 html : html || '',
23722                 pressed : toggle ? false : null,
23723                 listeners : {}
23724             };
23725             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23726                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23727             };
23728             children.push(a);
23729             return a;
23730        }
23731        
23732     //    var cb_box = function...
23733         
23734         var style = {
23735                 xtype: 'Button',
23736                 size : 'sm',
23737                 xns: Roo.bootstrap,
23738                 glyphicon : 'font',
23739                 //html : 'submit'
23740                 menu : {
23741                     xtype: 'Menu',
23742                     xns: Roo.bootstrap,
23743                     items:  []
23744                 }
23745         };
23746         Roo.each(this.formats, function(f) {
23747             style.menu.items.push({
23748                 xtype :'MenuItem',
23749                 xns: Roo.bootstrap,
23750                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23751                 tagname : f,
23752                 listeners : {
23753                     click : function()
23754                     {
23755                         editorcore.insertTag(this.tagname);
23756                         editor.focus();
23757                     }
23758                 }
23759                 
23760             });
23761         });
23762         children.push(style);   
23763         
23764         btn('bold',false,true);
23765         btn('italic',false,true);
23766         btn('align-left', 'justifyleft',true);
23767         btn('align-center', 'justifycenter',true);
23768         btn('align-right' , 'justifyright',true);
23769         btn('link', false, false, function(btn) {
23770             //Roo.log("create link?");
23771             var url = prompt(this.createLinkText, this.defaultLinkValue);
23772             if(url && url != 'http:/'+'/'){
23773                 this.editorcore.relayCmd('createlink', url);
23774             }
23775         }),
23776         btn('list','insertunorderedlist',true);
23777         btn('pencil', false,true, function(btn){
23778                 Roo.log(this);
23779                 this.toggleSourceEdit(btn.pressed);
23780         });
23781         
23782         if (this.editor.btns.length > 0) {
23783             for (var i = 0; i<this.editor.btns.length; i++) {
23784                 children.push(this.editor.btns[i]);
23785             }
23786         }
23787         
23788         /*
23789         var cog = {
23790                 xtype: 'Button',
23791                 size : 'sm',
23792                 xns: Roo.bootstrap,
23793                 glyphicon : 'cog',
23794                 //html : 'submit'
23795                 menu : {
23796                     xtype: 'Menu',
23797                     xns: Roo.bootstrap,
23798                     items:  []
23799                 }
23800         };
23801         
23802         cog.menu.items.push({
23803             xtype :'MenuItem',
23804             xns: Roo.bootstrap,
23805             html : Clean styles,
23806             tagname : f,
23807             listeners : {
23808                 click : function()
23809                 {
23810                     editorcore.insertTag(this.tagname);
23811                     editor.focus();
23812                 }
23813             }
23814             
23815         });
23816        */
23817         
23818          
23819        this.xtype = 'NavSimplebar';
23820         
23821         for(var i=0;i< children.length;i++) {
23822             
23823             this.buttons.add(this.addxtypeChild(children[i]));
23824             
23825         }
23826         
23827         editor.on('editorevent', this.updateToolbar, this);
23828     },
23829     onBtnClick : function(id)
23830     {
23831        this.editorcore.relayCmd(id);
23832        this.editorcore.focus();
23833     },
23834     
23835     /**
23836      * Protected method that will not generally be called directly. It triggers
23837      * a toolbar update by reading the markup state of the current selection in the editor.
23838      */
23839     updateToolbar: function(){
23840
23841         if(!this.editorcore.activated){
23842             this.editor.onFirstFocus(); // is this neeed?
23843             return;
23844         }
23845
23846         var btns = this.buttons; 
23847         var doc = this.editorcore.doc;
23848         btns.get('bold').setActive(doc.queryCommandState('bold'));
23849         btns.get('italic').setActive(doc.queryCommandState('italic'));
23850         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23851         
23852         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23853         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23854         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23855         
23856         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23857         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23858          /*
23859         
23860         var ans = this.editorcore.getAllAncestors();
23861         if (this.formatCombo) {
23862             
23863             
23864             var store = this.formatCombo.store;
23865             this.formatCombo.setValue("");
23866             for (var i =0; i < ans.length;i++) {
23867                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23868                     // select it..
23869                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23870                     break;
23871                 }
23872             }
23873         }
23874         
23875         
23876         
23877         // hides menus... - so this cant be on a menu...
23878         Roo.bootstrap.MenuMgr.hideAll();
23879         */
23880         Roo.bootstrap.MenuMgr.hideAll();
23881         //this.editorsyncValue();
23882     },
23883     onFirstFocus: function() {
23884         this.buttons.each(function(item){
23885            item.enable();
23886         });
23887     },
23888     toggleSourceEdit : function(sourceEditMode){
23889         
23890           
23891         if(sourceEditMode){
23892             Roo.log("disabling buttons");
23893            this.buttons.each( function(item){
23894                 if(item.cmd != 'pencil'){
23895                     item.disable();
23896                 }
23897             });
23898           
23899         }else{
23900             Roo.log("enabling buttons");
23901             if(this.editorcore.initialized){
23902                 this.buttons.each( function(item){
23903                     item.enable();
23904                 });
23905             }
23906             
23907         }
23908         Roo.log("calling toggole on editor");
23909         // tell the editor that it's been pressed..
23910         this.editor.toggleSourceEdit(sourceEditMode);
23911        
23912     }
23913 });
23914
23915
23916
23917
23918
23919 /**
23920  * @class Roo.bootstrap.Table.AbstractSelectionModel
23921  * @extends Roo.util.Observable
23922  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23923  * implemented by descendant classes.  This class should not be directly instantiated.
23924  * @constructor
23925  */
23926 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23927     this.locked = false;
23928     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23929 };
23930
23931
23932 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23933     /** @ignore Called by the grid automatically. Do not call directly. */
23934     init : function(grid){
23935         this.grid = grid;
23936         this.initEvents();
23937     },
23938
23939     /**
23940      * Locks the selections.
23941      */
23942     lock : function(){
23943         this.locked = true;
23944     },
23945
23946     /**
23947      * Unlocks the selections.
23948      */
23949     unlock : function(){
23950         this.locked = false;
23951     },
23952
23953     /**
23954      * Returns true if the selections are locked.
23955      * @return {Boolean}
23956      */
23957     isLocked : function(){
23958         return this.locked;
23959     }
23960 });
23961 /**
23962  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23963  * @class Roo.bootstrap.Table.RowSelectionModel
23964  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23965  * It supports multiple selections and keyboard selection/navigation. 
23966  * @constructor
23967  * @param {Object} config
23968  */
23969
23970 Roo.bootstrap.Table.RowSelectionModel = function(config){
23971     Roo.apply(this, config);
23972     this.selections = new Roo.util.MixedCollection(false, function(o){
23973         return o.id;
23974     });
23975
23976     this.last = false;
23977     this.lastActive = false;
23978
23979     this.addEvents({
23980         /**
23981              * @event selectionchange
23982              * Fires when the selection changes
23983              * @param {SelectionModel} this
23984              */
23985             "selectionchange" : true,
23986         /**
23987              * @event afterselectionchange
23988              * Fires after the selection changes (eg. by key press or clicking)
23989              * @param {SelectionModel} this
23990              */
23991             "afterselectionchange" : true,
23992         /**
23993              * @event beforerowselect
23994              * Fires when a row is selected being selected, return false to cancel.
23995              * @param {SelectionModel} this
23996              * @param {Number} rowIndex The selected index
23997              * @param {Boolean} keepExisting False if other selections will be cleared
23998              */
23999             "beforerowselect" : true,
24000         /**
24001              * @event rowselect
24002              * Fires when a row is selected.
24003              * @param {SelectionModel} this
24004              * @param {Number} rowIndex The selected index
24005              * @param {Roo.data.Record} r The record
24006              */
24007             "rowselect" : true,
24008         /**
24009              * @event rowdeselect
24010              * Fires when a row is deselected.
24011              * @param {SelectionModel} this
24012              * @param {Number} rowIndex The selected index
24013              */
24014         "rowdeselect" : true
24015     });
24016     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24017     this.locked = false;
24018  };
24019
24020 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24021     /**
24022      * @cfg {Boolean} singleSelect
24023      * True to allow selection of only one row at a time (defaults to false)
24024      */
24025     singleSelect : false,
24026
24027     // private
24028     initEvents : function()
24029     {
24030
24031         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24032         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24033         //}else{ // allow click to work like normal
24034          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24035         //}
24036         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24037         this.grid.on("rowclick", this.handleMouseDown, this);
24038         
24039         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24040             "up" : function(e){
24041                 if(!e.shiftKey){
24042                     this.selectPrevious(e.shiftKey);
24043                 }else if(this.last !== false && this.lastActive !== false){
24044                     var last = this.last;
24045                     this.selectRange(this.last,  this.lastActive-1);
24046                     this.grid.getView().focusRow(this.lastActive);
24047                     if(last !== false){
24048                         this.last = last;
24049                     }
24050                 }else{
24051                     this.selectFirstRow();
24052                 }
24053                 this.fireEvent("afterselectionchange", this);
24054             },
24055             "down" : function(e){
24056                 if(!e.shiftKey){
24057                     this.selectNext(e.shiftKey);
24058                 }else if(this.last !== false && this.lastActive !== false){
24059                     var last = this.last;
24060                     this.selectRange(this.last,  this.lastActive+1);
24061                     this.grid.getView().focusRow(this.lastActive);
24062                     if(last !== false){
24063                         this.last = last;
24064                     }
24065                 }else{
24066                     this.selectFirstRow();
24067                 }
24068                 this.fireEvent("afterselectionchange", this);
24069             },
24070             scope: this
24071         });
24072         this.grid.store.on('load', function(){
24073             this.selections.clear();
24074         },this);
24075         /*
24076         var view = this.grid.view;
24077         view.on("refresh", this.onRefresh, this);
24078         view.on("rowupdated", this.onRowUpdated, this);
24079         view.on("rowremoved", this.onRemove, this);
24080         */
24081     },
24082
24083     // private
24084     onRefresh : function()
24085     {
24086         var ds = this.grid.store, i, v = this.grid.view;
24087         var s = this.selections;
24088         s.each(function(r){
24089             if((i = ds.indexOfId(r.id)) != -1){
24090                 v.onRowSelect(i);
24091             }else{
24092                 s.remove(r);
24093             }
24094         });
24095     },
24096
24097     // private
24098     onRemove : function(v, index, r){
24099         this.selections.remove(r);
24100     },
24101
24102     // private
24103     onRowUpdated : function(v, index, r){
24104         if(this.isSelected(r)){
24105             v.onRowSelect(index);
24106         }
24107     },
24108
24109     /**
24110      * Select records.
24111      * @param {Array} records The records to select
24112      * @param {Boolean} keepExisting (optional) True to keep existing selections
24113      */
24114     selectRecords : function(records, keepExisting)
24115     {
24116         if(!keepExisting){
24117             this.clearSelections();
24118         }
24119             var ds = this.grid.store;
24120         for(var i = 0, len = records.length; i < len; i++){
24121             this.selectRow(ds.indexOf(records[i]), true);
24122         }
24123     },
24124
24125     /**
24126      * Gets the number of selected rows.
24127      * @return {Number}
24128      */
24129     getCount : function(){
24130         return this.selections.length;
24131     },
24132
24133     /**
24134      * Selects the first row in the grid.
24135      */
24136     selectFirstRow : function(){
24137         this.selectRow(0);
24138     },
24139
24140     /**
24141      * Select the last row.
24142      * @param {Boolean} keepExisting (optional) True to keep existing selections
24143      */
24144     selectLastRow : function(keepExisting){
24145         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24146         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24147     },
24148
24149     /**
24150      * Selects the row immediately following the last selected row.
24151      * @param {Boolean} keepExisting (optional) True to keep existing selections
24152      */
24153     selectNext : function(keepExisting)
24154     {
24155             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24156             this.selectRow(this.last+1, keepExisting);
24157             this.grid.getView().focusRow(this.last);
24158         }
24159     },
24160
24161     /**
24162      * Selects the row that precedes the last selected row.
24163      * @param {Boolean} keepExisting (optional) True to keep existing selections
24164      */
24165     selectPrevious : function(keepExisting){
24166         if(this.last){
24167             this.selectRow(this.last-1, keepExisting);
24168             this.grid.getView().focusRow(this.last);
24169         }
24170     },
24171
24172     /**
24173      * Returns the selected records
24174      * @return {Array} Array of selected records
24175      */
24176     getSelections : function(){
24177         return [].concat(this.selections.items);
24178     },
24179
24180     /**
24181      * Returns the first selected record.
24182      * @return {Record}
24183      */
24184     getSelected : function(){
24185         return this.selections.itemAt(0);
24186     },
24187
24188
24189     /**
24190      * Clears all selections.
24191      */
24192     clearSelections : function(fast)
24193     {
24194         if(this.locked) {
24195             return;
24196         }
24197         if(fast !== true){
24198                 var ds = this.grid.store;
24199             var s = this.selections;
24200             s.each(function(r){
24201                 this.deselectRow(ds.indexOfId(r.id));
24202             }, this);
24203             s.clear();
24204         }else{
24205             this.selections.clear();
24206         }
24207         this.last = false;
24208     },
24209
24210
24211     /**
24212      * Selects all rows.
24213      */
24214     selectAll : function(){
24215         if(this.locked) {
24216             return;
24217         }
24218         this.selections.clear();
24219         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24220             this.selectRow(i, true);
24221         }
24222     },
24223
24224     /**
24225      * Returns True if there is a selection.
24226      * @return {Boolean}
24227      */
24228     hasSelection : function(){
24229         return this.selections.length > 0;
24230     },
24231
24232     /**
24233      * Returns True if the specified row is selected.
24234      * @param {Number/Record} record The record or index of the record to check
24235      * @return {Boolean}
24236      */
24237     isSelected : function(index){
24238             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24239         return (r && this.selections.key(r.id) ? true : false);
24240     },
24241
24242     /**
24243      * Returns True if the specified record id is selected.
24244      * @param {String} id The id of record to check
24245      * @return {Boolean}
24246      */
24247     isIdSelected : function(id){
24248         return (this.selections.key(id) ? true : false);
24249     },
24250
24251
24252     // private
24253     handleMouseDBClick : function(e, t){
24254         
24255     },
24256     // private
24257     handleMouseDown : function(e, t)
24258     {
24259             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24260         if(this.isLocked() || rowIndex < 0 ){
24261             return;
24262         };
24263         if(e.shiftKey && this.last !== false){
24264             var last = this.last;
24265             this.selectRange(last, rowIndex, e.ctrlKey);
24266             this.last = last; // reset the last
24267             t.focus();
24268     
24269         }else{
24270             var isSelected = this.isSelected(rowIndex);
24271             //Roo.log("select row:" + rowIndex);
24272             if(isSelected){
24273                 this.deselectRow(rowIndex);
24274             } else {
24275                         this.selectRow(rowIndex, true);
24276             }
24277     
24278             /*
24279                 if(e.button !== 0 && isSelected){
24280                 alert('rowIndex 2: ' + rowIndex);
24281                     view.focusRow(rowIndex);
24282                 }else if(e.ctrlKey && isSelected){
24283                     this.deselectRow(rowIndex);
24284                 }else if(!isSelected){
24285                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24286                     view.focusRow(rowIndex);
24287                 }
24288             */
24289         }
24290         this.fireEvent("afterselectionchange", this);
24291     },
24292     // private
24293     handleDragableRowClick :  function(grid, rowIndex, e) 
24294     {
24295         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24296             this.selectRow(rowIndex, false);
24297             grid.view.focusRow(rowIndex);
24298              this.fireEvent("afterselectionchange", this);
24299         }
24300     },
24301     
24302     /**
24303      * Selects multiple rows.
24304      * @param {Array} rows Array of the indexes of the row to select
24305      * @param {Boolean} keepExisting (optional) True to keep existing selections
24306      */
24307     selectRows : function(rows, keepExisting){
24308         if(!keepExisting){
24309             this.clearSelections();
24310         }
24311         for(var i = 0, len = rows.length; i < len; i++){
24312             this.selectRow(rows[i], true);
24313         }
24314     },
24315
24316     /**
24317      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24318      * @param {Number} startRow The index of the first row in the range
24319      * @param {Number} endRow The index of the last row in the range
24320      * @param {Boolean} keepExisting (optional) True to retain existing selections
24321      */
24322     selectRange : function(startRow, endRow, keepExisting){
24323         if(this.locked) {
24324             return;
24325         }
24326         if(!keepExisting){
24327             this.clearSelections();
24328         }
24329         if(startRow <= endRow){
24330             for(var i = startRow; i <= endRow; i++){
24331                 this.selectRow(i, true);
24332             }
24333         }else{
24334             for(var i = startRow; i >= endRow; i--){
24335                 this.selectRow(i, true);
24336             }
24337         }
24338     },
24339
24340     /**
24341      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24342      * @param {Number} startRow The index of the first row in the range
24343      * @param {Number} endRow The index of the last row in the range
24344      */
24345     deselectRange : function(startRow, endRow, preventViewNotify){
24346         if(this.locked) {
24347             return;
24348         }
24349         for(var i = startRow; i <= endRow; i++){
24350             this.deselectRow(i, preventViewNotify);
24351         }
24352     },
24353
24354     /**
24355      * Selects a row.
24356      * @param {Number} row The index of the row to select
24357      * @param {Boolean} keepExisting (optional) True to keep existing selections
24358      */
24359     selectRow : function(index, keepExisting, preventViewNotify)
24360     {
24361             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24362             return;
24363         }
24364         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24365             if(!keepExisting || this.singleSelect){
24366                 this.clearSelections();
24367             }
24368             
24369             var r = this.grid.store.getAt(index);
24370             //console.log('selectRow - record id :' + r.id);
24371             
24372             this.selections.add(r);
24373             this.last = this.lastActive = index;
24374             if(!preventViewNotify){
24375                 var proxy = new Roo.Element(
24376                                 this.grid.getRowDom(index)
24377                 );
24378                 proxy.addClass('bg-info info');
24379             }
24380             this.fireEvent("rowselect", this, index, r);
24381             this.fireEvent("selectionchange", this);
24382         }
24383     },
24384
24385     /**
24386      * Deselects a row.
24387      * @param {Number} row The index of the row to deselect
24388      */
24389     deselectRow : function(index, preventViewNotify)
24390     {
24391         if(this.locked) {
24392             return;
24393         }
24394         if(this.last == index){
24395             this.last = false;
24396         }
24397         if(this.lastActive == index){
24398             this.lastActive = false;
24399         }
24400         
24401         var r = this.grid.store.getAt(index);
24402         if (!r) {
24403             return;
24404         }
24405         
24406         this.selections.remove(r);
24407         //.console.log('deselectRow - record id :' + r.id);
24408         if(!preventViewNotify){
24409         
24410             var proxy = new Roo.Element(
24411                 this.grid.getRowDom(index)
24412             );
24413             proxy.removeClass('bg-info info');
24414         }
24415         this.fireEvent("rowdeselect", this, index);
24416         this.fireEvent("selectionchange", this);
24417     },
24418
24419     // private
24420     restoreLast : function(){
24421         if(this._last){
24422             this.last = this._last;
24423         }
24424     },
24425
24426     // private
24427     acceptsNav : function(row, col, cm){
24428         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24429     },
24430
24431     // private
24432     onEditorKey : function(field, e){
24433         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24434         if(k == e.TAB){
24435             e.stopEvent();
24436             ed.completeEdit();
24437             if(e.shiftKey){
24438                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24439             }else{
24440                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24441             }
24442         }else if(k == e.ENTER && !e.ctrlKey){
24443             e.stopEvent();
24444             ed.completeEdit();
24445             if(e.shiftKey){
24446                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24447             }else{
24448                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24449             }
24450         }else if(k == e.ESC){
24451             ed.cancelEdit();
24452         }
24453         if(newCell){
24454             g.startEditing(newCell[0], newCell[1]);
24455         }
24456     }
24457 });
24458 /*
24459  * Based on:
24460  * Ext JS Library 1.1.1
24461  * Copyright(c) 2006-2007, Ext JS, LLC.
24462  *
24463  * Originally Released Under LGPL - original licence link has changed is not relivant.
24464  *
24465  * Fork - LGPL
24466  * <script type="text/javascript">
24467  */
24468  
24469 /**
24470  * @class Roo.bootstrap.PagingToolbar
24471  * @extends Roo.bootstrap.NavSimplebar
24472  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24473  * @constructor
24474  * Create a new PagingToolbar
24475  * @param {Object} config The config object
24476  * @param {Roo.data.Store} store
24477  */
24478 Roo.bootstrap.PagingToolbar = function(config)
24479 {
24480     // old args format still supported... - xtype is prefered..
24481         // created from xtype...
24482     
24483     this.ds = config.dataSource;
24484     
24485     if (config.store && !this.ds) {
24486         this.store= Roo.factory(config.store, Roo.data);
24487         this.ds = this.store;
24488         this.ds.xmodule = this.xmodule || false;
24489     }
24490     
24491     this.toolbarItems = [];
24492     if (config.items) {
24493         this.toolbarItems = config.items;
24494     }
24495     
24496     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24497     
24498     this.cursor = 0;
24499     
24500     if (this.ds) { 
24501         this.bind(this.ds);
24502     }
24503     
24504     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24505     
24506 };
24507
24508 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24509     /**
24510      * @cfg {Roo.data.Store} dataSource
24511      * The underlying data store providing the paged data
24512      */
24513     /**
24514      * @cfg {String/HTMLElement/Element} container
24515      * container The id or element that will contain the toolbar
24516      */
24517     /**
24518      * @cfg {Boolean} displayInfo
24519      * True to display the displayMsg (defaults to false)
24520      */
24521     /**
24522      * @cfg {Number} pageSize
24523      * The number of records to display per page (defaults to 20)
24524      */
24525     pageSize: 20,
24526     /**
24527      * @cfg {String} displayMsg
24528      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24529      */
24530     displayMsg : 'Displaying {0} - {1} of {2}',
24531     /**
24532      * @cfg {String} emptyMsg
24533      * The message to display when no records are found (defaults to "No data to display")
24534      */
24535     emptyMsg : 'No data to display',
24536     /**
24537      * Customizable piece of the default paging text (defaults to "Page")
24538      * @type String
24539      */
24540     beforePageText : "Page",
24541     /**
24542      * Customizable piece of the default paging text (defaults to "of %0")
24543      * @type String
24544      */
24545     afterPageText : "of {0}",
24546     /**
24547      * Customizable piece of the default paging text (defaults to "First Page")
24548      * @type String
24549      */
24550     firstText : "First Page",
24551     /**
24552      * Customizable piece of the default paging text (defaults to "Previous Page")
24553      * @type String
24554      */
24555     prevText : "Previous Page",
24556     /**
24557      * Customizable piece of the default paging text (defaults to "Next Page")
24558      * @type String
24559      */
24560     nextText : "Next Page",
24561     /**
24562      * Customizable piece of the default paging text (defaults to "Last Page")
24563      * @type String
24564      */
24565     lastText : "Last Page",
24566     /**
24567      * Customizable piece of the default paging text (defaults to "Refresh")
24568      * @type String
24569      */
24570     refreshText : "Refresh",
24571
24572     buttons : false,
24573     // private
24574     onRender : function(ct, position) 
24575     {
24576         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24577         this.navgroup.parentId = this.id;
24578         this.navgroup.onRender(this.el, null);
24579         // add the buttons to the navgroup
24580         
24581         if(this.displayInfo){
24582             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24583             this.displayEl = this.el.select('.x-paging-info', true).first();
24584 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24585 //            this.displayEl = navel.el.select('span',true).first();
24586         }
24587         
24588         var _this = this;
24589         
24590         if(this.buttons){
24591             Roo.each(_this.buttons, function(e){ // this might need to use render????
24592                Roo.factory(e).render(_this.el);
24593             });
24594         }
24595             
24596         Roo.each(_this.toolbarItems, function(e) {
24597             _this.navgroup.addItem(e);
24598         });
24599         
24600         
24601         this.first = this.navgroup.addItem({
24602             tooltip: this.firstText,
24603             cls: "prev",
24604             icon : 'fa fa-backward',
24605             disabled: true,
24606             preventDefault: true,
24607             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24608         });
24609         
24610         this.prev =  this.navgroup.addItem({
24611             tooltip: this.prevText,
24612             cls: "prev",
24613             icon : 'fa fa-step-backward',
24614             disabled: true,
24615             preventDefault: true,
24616             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24617         });
24618     //this.addSeparator();
24619         
24620         
24621         var field = this.navgroup.addItem( {
24622             tagtype : 'span',
24623             cls : 'x-paging-position',
24624             
24625             html : this.beforePageText  +
24626                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24627                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24628          } ); //?? escaped?
24629         
24630         this.field = field.el.select('input', true).first();
24631         this.field.on("keydown", this.onPagingKeydown, this);
24632         this.field.on("focus", function(){this.dom.select();});
24633     
24634     
24635         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24636         //this.field.setHeight(18);
24637         //this.addSeparator();
24638         this.next = this.navgroup.addItem({
24639             tooltip: this.nextText,
24640             cls: "next",
24641             html : ' <i class="fa fa-step-forward">',
24642             disabled: true,
24643             preventDefault: true,
24644             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24645         });
24646         this.last = this.navgroup.addItem({
24647             tooltip: this.lastText,
24648             icon : 'fa fa-forward',
24649             cls: "next",
24650             disabled: true,
24651             preventDefault: true,
24652             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24653         });
24654     //this.addSeparator();
24655         this.loading = this.navgroup.addItem({
24656             tooltip: this.refreshText,
24657             icon: 'fa fa-refresh',
24658             preventDefault: true,
24659             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24660         });
24661         
24662     },
24663
24664     // private
24665     updateInfo : function(){
24666         if(this.displayEl){
24667             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24668             var msg = count == 0 ?
24669                 this.emptyMsg :
24670                 String.format(
24671                     this.displayMsg,
24672                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24673                 );
24674             this.displayEl.update(msg);
24675         }
24676     },
24677
24678     // private
24679     onLoad : function(ds, r, o)
24680     {
24681         this.cursor = o.params.start ? o.params.start : 0;
24682         
24683         var d = this.getPageData(),
24684             ap = d.activePage,
24685             ps = d.pages;
24686         
24687         
24688         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24689         this.field.dom.value = ap;
24690         this.first.setDisabled(ap == 1);
24691         this.prev.setDisabled(ap == 1);
24692         this.next.setDisabled(ap == ps);
24693         this.last.setDisabled(ap == ps);
24694         this.loading.enable();
24695         this.updateInfo();
24696     },
24697
24698     // private
24699     getPageData : function(){
24700         var total = this.ds.getTotalCount();
24701         return {
24702             total : total,
24703             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24704             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24705         };
24706     },
24707
24708     // private
24709     onLoadError : function(){
24710         this.loading.enable();
24711     },
24712
24713     // private
24714     onPagingKeydown : function(e){
24715         var k = e.getKey();
24716         var d = this.getPageData();
24717         if(k == e.RETURN){
24718             var v = this.field.dom.value, pageNum;
24719             if(!v || isNaN(pageNum = parseInt(v, 10))){
24720                 this.field.dom.value = d.activePage;
24721                 return;
24722             }
24723             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24724             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24725             e.stopEvent();
24726         }
24727         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))
24728         {
24729           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24730           this.field.dom.value = pageNum;
24731           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24732           e.stopEvent();
24733         }
24734         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24735         {
24736           var v = this.field.dom.value, pageNum; 
24737           var increment = (e.shiftKey) ? 10 : 1;
24738           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24739                 increment *= -1;
24740           }
24741           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24742             this.field.dom.value = d.activePage;
24743             return;
24744           }
24745           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24746           {
24747             this.field.dom.value = parseInt(v, 10) + increment;
24748             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24749             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24750           }
24751           e.stopEvent();
24752         }
24753     },
24754
24755     // private
24756     beforeLoad : function(){
24757         if(this.loading){
24758             this.loading.disable();
24759         }
24760     },
24761
24762     // private
24763     onClick : function(which){
24764         
24765         var ds = this.ds;
24766         if (!ds) {
24767             return;
24768         }
24769         
24770         switch(which){
24771             case "first":
24772                 ds.load({params:{start: 0, limit: this.pageSize}});
24773             break;
24774             case "prev":
24775                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24776             break;
24777             case "next":
24778                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24779             break;
24780             case "last":
24781                 var total = ds.getTotalCount();
24782                 var extra = total % this.pageSize;
24783                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24784                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24785             break;
24786             case "refresh":
24787                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24788             break;
24789         }
24790     },
24791
24792     /**
24793      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24794      * @param {Roo.data.Store} store The data store to unbind
24795      */
24796     unbind : function(ds){
24797         ds.un("beforeload", this.beforeLoad, this);
24798         ds.un("load", this.onLoad, this);
24799         ds.un("loadexception", this.onLoadError, this);
24800         ds.un("remove", this.updateInfo, this);
24801         ds.un("add", this.updateInfo, this);
24802         this.ds = undefined;
24803     },
24804
24805     /**
24806      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24807      * @param {Roo.data.Store} store The data store to bind
24808      */
24809     bind : function(ds){
24810         ds.on("beforeload", this.beforeLoad, this);
24811         ds.on("load", this.onLoad, this);
24812         ds.on("loadexception", this.onLoadError, this);
24813         ds.on("remove", this.updateInfo, this);
24814         ds.on("add", this.updateInfo, this);
24815         this.ds = ds;
24816     }
24817 });/*
24818  * - LGPL
24819  *
24820  * element
24821  * 
24822  */
24823
24824 /**
24825  * @class Roo.bootstrap.MessageBar
24826  * @extends Roo.bootstrap.Component
24827  * Bootstrap MessageBar class
24828  * @cfg {String} html contents of the MessageBar
24829  * @cfg {String} weight (info | success | warning | danger) default info
24830  * @cfg {String} beforeClass insert the bar before the given class
24831  * @cfg {Boolean} closable (true | false) default false
24832  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24833  * 
24834  * @constructor
24835  * Create a new Element
24836  * @param {Object} config The config object
24837  */
24838
24839 Roo.bootstrap.MessageBar = function(config){
24840     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24841 };
24842
24843 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24844     
24845     html: '',
24846     weight: 'info',
24847     closable: false,
24848     fixed: false,
24849     beforeClass: 'bootstrap-sticky-wrap',
24850     
24851     getAutoCreate : function(){
24852         
24853         var cfg = {
24854             tag: 'div',
24855             cls: 'alert alert-dismissable alert-' + this.weight,
24856             cn: [
24857                 {
24858                     tag: 'span',
24859                     cls: 'message',
24860                     html: this.html || ''
24861                 }
24862             ]
24863         };
24864         
24865         if(this.fixed){
24866             cfg.cls += ' alert-messages-fixed';
24867         }
24868         
24869         if(this.closable){
24870             cfg.cn.push({
24871                 tag: 'button',
24872                 cls: 'close',
24873                 html: 'x'
24874             });
24875         }
24876         
24877         return cfg;
24878     },
24879     
24880     onRender : function(ct, position)
24881     {
24882         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24883         
24884         if(!this.el){
24885             var cfg = Roo.apply({},  this.getAutoCreate());
24886             cfg.id = Roo.id();
24887             
24888             if (this.cls) {
24889                 cfg.cls += ' ' + this.cls;
24890             }
24891             if (this.style) {
24892                 cfg.style = this.style;
24893             }
24894             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24895             
24896             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24897         }
24898         
24899         this.el.select('>button.close').on('click', this.hide, this);
24900         
24901     },
24902     
24903     show : function()
24904     {
24905         if (!this.rendered) {
24906             this.render();
24907         }
24908         
24909         this.el.show();
24910         
24911         this.fireEvent('show', this);
24912         
24913     },
24914     
24915     hide : function()
24916     {
24917         if (!this.rendered) {
24918             this.render();
24919         }
24920         
24921         this.el.hide();
24922         
24923         this.fireEvent('hide', this);
24924     },
24925     
24926     update : function()
24927     {
24928 //        var e = this.el.dom.firstChild;
24929 //        
24930 //        if(this.closable){
24931 //            e = e.nextSibling;
24932 //        }
24933 //        
24934 //        e.data = this.html || '';
24935
24936         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24937     }
24938    
24939 });
24940
24941  
24942
24943      /*
24944  * - LGPL
24945  *
24946  * Graph
24947  * 
24948  */
24949
24950
24951 /**
24952  * @class Roo.bootstrap.Graph
24953  * @extends Roo.bootstrap.Component
24954  * Bootstrap Graph class
24955 > Prameters
24956  -sm {number} sm 4
24957  -md {number} md 5
24958  @cfg {String} graphtype  bar | vbar | pie
24959  @cfg {number} g_x coodinator | centre x (pie)
24960  @cfg {number} g_y coodinator | centre y (pie)
24961  @cfg {number} g_r radius (pie)
24962  @cfg {number} g_height height of the chart (respected by all elements in the set)
24963  @cfg {number} g_width width of the chart (respected by all elements in the set)
24964  @cfg {Object} title The title of the chart
24965     
24966  -{Array}  values
24967  -opts (object) options for the chart 
24968      o {
24969      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24970      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24971      o vgutter (number)
24972      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.
24973      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24974      o to
24975      o stretch (boolean)
24976      o }
24977  -opts (object) options for the pie
24978      o{
24979      o cut
24980      o startAngle (number)
24981      o endAngle (number)
24982      } 
24983  *
24984  * @constructor
24985  * Create a new Input
24986  * @param {Object} config The config object
24987  */
24988
24989 Roo.bootstrap.Graph = function(config){
24990     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24991     
24992     this.addEvents({
24993         // img events
24994         /**
24995          * @event click
24996          * The img click event for the img.
24997          * @param {Roo.EventObject} e
24998          */
24999         "click" : true
25000     });
25001 };
25002
25003 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25004     
25005     sm: 4,
25006     md: 5,
25007     graphtype: 'bar',
25008     g_height: 250,
25009     g_width: 400,
25010     g_x: 50,
25011     g_y: 50,
25012     g_r: 30,
25013     opts:{
25014         //g_colors: this.colors,
25015         g_type: 'soft',
25016         g_gutter: '20%'
25017
25018     },
25019     title : false,
25020
25021     getAutoCreate : function(){
25022         
25023         var cfg = {
25024             tag: 'div',
25025             html : null
25026         };
25027         
25028         
25029         return  cfg;
25030     },
25031
25032     onRender : function(ct,position){
25033         
25034         
25035         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25036         
25037         if (typeof(Raphael) == 'undefined') {
25038             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25039             return;
25040         }
25041         
25042         this.raphael = Raphael(this.el.dom);
25043         
25044                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25045                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25046                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25047                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25048                 /*
25049                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25050                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25051                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25052                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25053                 
25054                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25055                 r.barchart(330, 10, 300, 220, data1);
25056                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25057                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25058                 */
25059                 
25060                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25061                 // r.barchart(30, 30, 560, 250,  xdata, {
25062                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25063                 //     axis : "0 0 1 1",
25064                 //     axisxlabels :  xdata
25065                 //     //yvalues : cols,
25066                    
25067                 // });
25068 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25069 //        
25070 //        this.load(null,xdata,{
25071 //                axis : "0 0 1 1",
25072 //                axisxlabels :  xdata
25073 //                });
25074
25075     },
25076
25077     load : function(graphtype,xdata,opts)
25078     {
25079         this.raphael.clear();
25080         if(!graphtype) {
25081             graphtype = this.graphtype;
25082         }
25083         if(!opts){
25084             opts = this.opts;
25085         }
25086         var r = this.raphael,
25087             fin = function () {
25088                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25089             },
25090             fout = function () {
25091                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25092             },
25093             pfin = function() {
25094                 this.sector.stop();
25095                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25096
25097                 if (this.label) {
25098                     this.label[0].stop();
25099                     this.label[0].attr({ r: 7.5 });
25100                     this.label[1].attr({ "font-weight": 800 });
25101                 }
25102             },
25103             pfout = function() {
25104                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25105
25106                 if (this.label) {
25107                     this.label[0].animate({ r: 5 }, 500, "bounce");
25108                     this.label[1].attr({ "font-weight": 400 });
25109                 }
25110             };
25111
25112         switch(graphtype){
25113             case 'bar':
25114                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25115                 break;
25116             case 'hbar':
25117                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25118                 break;
25119             case 'pie':
25120 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25121 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25122 //            
25123                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25124                 
25125                 break;
25126
25127         }
25128         
25129         if(this.title){
25130             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25131         }
25132         
25133     },
25134     
25135     setTitle: function(o)
25136     {
25137         this.title = o;
25138     },
25139     
25140     initEvents: function() {
25141         
25142         if(!this.href){
25143             this.el.on('click', this.onClick, this);
25144         }
25145     },
25146     
25147     onClick : function(e)
25148     {
25149         Roo.log('img onclick');
25150         this.fireEvent('click', this, e);
25151     }
25152    
25153 });
25154
25155  
25156 /*
25157  * - LGPL
25158  *
25159  * numberBox
25160  * 
25161  */
25162 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25163
25164 /**
25165  * @class Roo.bootstrap.dash.NumberBox
25166  * @extends Roo.bootstrap.Component
25167  * Bootstrap NumberBox class
25168  * @cfg {String} headline Box headline
25169  * @cfg {String} content Box content
25170  * @cfg {String} icon Box icon
25171  * @cfg {String} footer Footer text
25172  * @cfg {String} fhref Footer href
25173  * 
25174  * @constructor
25175  * Create a new NumberBox
25176  * @param {Object} config The config object
25177  */
25178
25179
25180 Roo.bootstrap.dash.NumberBox = function(config){
25181     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25182     
25183 };
25184
25185 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25186     
25187     headline : '',
25188     content : '',
25189     icon : '',
25190     footer : '',
25191     fhref : '',
25192     ficon : '',
25193     
25194     getAutoCreate : function(){
25195         
25196         var cfg = {
25197             tag : 'div',
25198             cls : 'small-box ',
25199             cn : [
25200                 {
25201                     tag : 'div',
25202                     cls : 'inner',
25203                     cn :[
25204                         {
25205                             tag : 'h3',
25206                             cls : 'roo-headline',
25207                             html : this.headline
25208                         },
25209                         {
25210                             tag : 'p',
25211                             cls : 'roo-content',
25212                             html : this.content
25213                         }
25214                     ]
25215                 }
25216             ]
25217         };
25218         
25219         if(this.icon){
25220             cfg.cn.push({
25221                 tag : 'div',
25222                 cls : 'icon',
25223                 cn :[
25224                     {
25225                         tag : 'i',
25226                         cls : 'ion ' + this.icon
25227                     }
25228                 ]
25229             });
25230         }
25231         
25232         if(this.footer){
25233             var footer = {
25234                 tag : 'a',
25235                 cls : 'small-box-footer',
25236                 href : this.fhref || '#',
25237                 html : this.footer
25238             };
25239             
25240             cfg.cn.push(footer);
25241             
25242         }
25243         
25244         return  cfg;
25245     },
25246
25247     onRender : function(ct,position){
25248         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25249
25250
25251        
25252                 
25253     },
25254
25255     setHeadline: function (value)
25256     {
25257         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25258     },
25259     
25260     setFooter: function (value, href)
25261     {
25262         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25263         
25264         if(href){
25265             this.el.select('a.small-box-footer',true).first().attr('href', href);
25266         }
25267         
25268     },
25269
25270     setContent: function (value)
25271     {
25272         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25273     },
25274
25275     initEvents: function() 
25276     {   
25277         
25278     }
25279     
25280 });
25281
25282  
25283 /*
25284  * - LGPL
25285  *
25286  * TabBox
25287  * 
25288  */
25289 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25290
25291 /**
25292  * @class Roo.bootstrap.dash.TabBox
25293  * @extends Roo.bootstrap.Component
25294  * Bootstrap TabBox class
25295  * @cfg {String} title Title of the TabBox
25296  * @cfg {String} icon Icon of the TabBox
25297  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25298  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25299  * 
25300  * @constructor
25301  * Create a new TabBox
25302  * @param {Object} config The config object
25303  */
25304
25305
25306 Roo.bootstrap.dash.TabBox = function(config){
25307     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25308     this.addEvents({
25309         // raw events
25310         /**
25311          * @event addpane
25312          * When a pane is added
25313          * @param {Roo.bootstrap.dash.TabPane} pane
25314          */
25315         "addpane" : true,
25316         /**
25317          * @event activatepane
25318          * When a pane is activated
25319          * @param {Roo.bootstrap.dash.TabPane} pane
25320          */
25321         "activatepane" : true
25322         
25323          
25324     });
25325     
25326     this.panes = [];
25327 };
25328
25329 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25330
25331     title : '',
25332     icon : false,
25333     showtabs : true,
25334     tabScrollable : false,
25335     
25336     getChildContainer : function()
25337     {
25338         return this.el.select('.tab-content', true).first();
25339     },
25340     
25341     getAutoCreate : function(){
25342         
25343         var header = {
25344             tag: 'li',
25345             cls: 'pull-left header',
25346             html: this.title,
25347             cn : []
25348         };
25349         
25350         if(this.icon){
25351             header.cn.push({
25352                 tag: 'i',
25353                 cls: 'fa ' + this.icon
25354             });
25355         }
25356         
25357         var h = {
25358             tag: 'ul',
25359             cls: 'nav nav-tabs pull-right',
25360             cn: [
25361                 header
25362             ]
25363         };
25364         
25365         if(this.tabScrollable){
25366             h = {
25367                 tag: 'div',
25368                 cls: 'tab-header',
25369                 cn: [
25370                     {
25371                         tag: 'ul',
25372                         cls: 'nav nav-tabs pull-right',
25373                         cn: [
25374                             header
25375                         ]
25376                     }
25377                 ]
25378             };
25379         }
25380         
25381         var cfg = {
25382             tag: 'div',
25383             cls: 'nav-tabs-custom',
25384             cn: [
25385                 h,
25386                 {
25387                     tag: 'div',
25388                     cls: 'tab-content no-padding',
25389                     cn: []
25390                 }
25391             ]
25392         };
25393
25394         return  cfg;
25395     },
25396     initEvents : function()
25397     {
25398         //Roo.log('add add pane handler');
25399         this.on('addpane', this.onAddPane, this);
25400     },
25401      /**
25402      * Updates the box title
25403      * @param {String} html to set the title to.
25404      */
25405     setTitle : function(value)
25406     {
25407         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25408     },
25409     onAddPane : function(pane)
25410     {
25411         this.panes.push(pane);
25412         //Roo.log('addpane');
25413         //Roo.log(pane);
25414         // tabs are rendere left to right..
25415         if(!this.showtabs){
25416             return;
25417         }
25418         
25419         var ctr = this.el.select('.nav-tabs', true).first();
25420          
25421          
25422         var existing = ctr.select('.nav-tab',true);
25423         var qty = existing.getCount();;
25424         
25425         
25426         var tab = ctr.createChild({
25427             tag : 'li',
25428             cls : 'nav-tab' + (qty ? '' : ' active'),
25429             cn : [
25430                 {
25431                     tag : 'a',
25432                     href:'#',
25433                     html : pane.title
25434                 }
25435             ]
25436         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25437         pane.tab = tab;
25438         
25439         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25440         if (!qty) {
25441             pane.el.addClass('active');
25442         }
25443         
25444                 
25445     },
25446     onTabClick : function(ev,un,ob,pane)
25447     {
25448         //Roo.log('tab - prev default');
25449         ev.preventDefault();
25450         
25451         
25452         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25453         pane.tab.addClass('active');
25454         //Roo.log(pane.title);
25455         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25456         // technically we should have a deactivate event.. but maybe add later.
25457         // and it should not de-activate the selected tab...
25458         this.fireEvent('activatepane', pane);
25459         pane.el.addClass('active');
25460         pane.fireEvent('activate');
25461         
25462         
25463     },
25464     
25465     getActivePane : function()
25466     {
25467         var r = false;
25468         Roo.each(this.panes, function(p) {
25469             if(p.el.hasClass('active')){
25470                 r = p;
25471                 return false;
25472             }
25473             
25474             return;
25475         });
25476         
25477         return r;
25478     }
25479     
25480     
25481 });
25482
25483  
25484 /*
25485  * - LGPL
25486  *
25487  * Tab pane
25488  * 
25489  */
25490 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25491 /**
25492  * @class Roo.bootstrap.TabPane
25493  * @extends Roo.bootstrap.Component
25494  * Bootstrap TabPane class
25495  * @cfg {Boolean} active (false | true) Default false
25496  * @cfg {String} title title of panel
25497
25498  * 
25499  * @constructor
25500  * Create a new TabPane
25501  * @param {Object} config The config object
25502  */
25503
25504 Roo.bootstrap.dash.TabPane = function(config){
25505     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25506     
25507     this.addEvents({
25508         // raw events
25509         /**
25510          * @event activate
25511          * When a pane is activated
25512          * @param {Roo.bootstrap.dash.TabPane} pane
25513          */
25514         "activate" : true
25515          
25516     });
25517 };
25518
25519 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25520     
25521     active : false,
25522     title : '',
25523     
25524     // the tabBox that this is attached to.
25525     tab : false,
25526      
25527     getAutoCreate : function() 
25528     {
25529         var cfg = {
25530             tag: 'div',
25531             cls: 'tab-pane'
25532         };
25533         
25534         if(this.active){
25535             cfg.cls += ' active';
25536         }
25537         
25538         return cfg;
25539     },
25540     initEvents  : function()
25541     {
25542         //Roo.log('trigger add pane handler');
25543         this.parent().fireEvent('addpane', this)
25544     },
25545     
25546      /**
25547      * Updates the tab title 
25548      * @param {String} html to set the title to.
25549      */
25550     setTitle: function(str)
25551     {
25552         if (!this.tab) {
25553             return;
25554         }
25555         this.title = str;
25556         this.tab.select('a', true).first().dom.innerHTML = str;
25557         
25558     }
25559     
25560     
25561     
25562 });
25563
25564  
25565
25566
25567  /*
25568  * - LGPL
25569  *
25570  * menu
25571  * 
25572  */
25573 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25574
25575 /**
25576  * @class Roo.bootstrap.menu.Menu
25577  * @extends Roo.bootstrap.Component
25578  * Bootstrap Menu class - container for Menu
25579  * @cfg {String} html Text of the menu
25580  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25581  * @cfg {String} icon Font awesome icon
25582  * @cfg {String} pos Menu align to (top | bottom) default bottom
25583  * 
25584  * 
25585  * @constructor
25586  * Create a new Menu
25587  * @param {Object} config The config object
25588  */
25589
25590
25591 Roo.bootstrap.menu.Menu = function(config){
25592     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25593     
25594     this.addEvents({
25595         /**
25596          * @event beforeshow
25597          * Fires before this menu is displayed
25598          * @param {Roo.bootstrap.menu.Menu} this
25599          */
25600         beforeshow : true,
25601         /**
25602          * @event beforehide
25603          * Fires before this menu is hidden
25604          * @param {Roo.bootstrap.menu.Menu} this
25605          */
25606         beforehide : true,
25607         /**
25608          * @event show
25609          * Fires after this menu is displayed
25610          * @param {Roo.bootstrap.menu.Menu} this
25611          */
25612         show : true,
25613         /**
25614          * @event hide
25615          * Fires after this menu is hidden
25616          * @param {Roo.bootstrap.menu.Menu} this
25617          */
25618         hide : true,
25619         /**
25620          * @event click
25621          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25622          * @param {Roo.bootstrap.menu.Menu} this
25623          * @param {Roo.EventObject} e
25624          */
25625         click : true
25626     });
25627     
25628 };
25629
25630 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25631     
25632     submenu : false,
25633     html : '',
25634     weight : 'default',
25635     icon : false,
25636     pos : 'bottom',
25637     
25638     
25639     getChildContainer : function() {
25640         if(this.isSubMenu){
25641             return this.el;
25642         }
25643         
25644         return this.el.select('ul.dropdown-menu', true).first();  
25645     },
25646     
25647     getAutoCreate : function()
25648     {
25649         var text = [
25650             {
25651                 tag : 'span',
25652                 cls : 'roo-menu-text',
25653                 html : this.html
25654             }
25655         ];
25656         
25657         if(this.icon){
25658             text.unshift({
25659                 tag : 'i',
25660                 cls : 'fa ' + this.icon
25661             })
25662         }
25663         
25664         
25665         var cfg = {
25666             tag : 'div',
25667             cls : 'btn-group',
25668             cn : [
25669                 {
25670                     tag : 'button',
25671                     cls : 'dropdown-button btn btn-' + this.weight,
25672                     cn : text
25673                 },
25674                 {
25675                     tag : 'button',
25676                     cls : 'dropdown-toggle btn btn-' + this.weight,
25677                     cn : [
25678                         {
25679                             tag : 'span',
25680                             cls : 'caret'
25681                         }
25682                     ]
25683                 },
25684                 {
25685                     tag : 'ul',
25686                     cls : 'dropdown-menu'
25687                 }
25688             ]
25689             
25690         };
25691         
25692         if(this.pos == 'top'){
25693             cfg.cls += ' dropup';
25694         }
25695         
25696         if(this.isSubMenu){
25697             cfg = {
25698                 tag : 'ul',
25699                 cls : 'dropdown-menu'
25700             }
25701         }
25702         
25703         return cfg;
25704     },
25705     
25706     onRender : function(ct, position)
25707     {
25708         this.isSubMenu = ct.hasClass('dropdown-submenu');
25709         
25710         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25711     },
25712     
25713     initEvents : function() 
25714     {
25715         if(this.isSubMenu){
25716             return;
25717         }
25718         
25719         this.hidden = true;
25720         
25721         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25722         this.triggerEl.on('click', this.onTriggerPress, this);
25723         
25724         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25725         this.buttonEl.on('click', this.onClick, this);
25726         
25727     },
25728     
25729     list : function()
25730     {
25731         if(this.isSubMenu){
25732             return this.el;
25733         }
25734         
25735         return this.el.select('ul.dropdown-menu', true).first();
25736     },
25737     
25738     onClick : function(e)
25739     {
25740         this.fireEvent("click", this, e);
25741     },
25742     
25743     onTriggerPress  : function(e)
25744     {   
25745         if (this.isVisible()) {
25746             this.hide();
25747         } else {
25748             this.show();
25749         }
25750     },
25751     
25752     isVisible : function(){
25753         return !this.hidden;
25754     },
25755     
25756     show : function()
25757     {
25758         this.fireEvent("beforeshow", this);
25759         
25760         this.hidden = false;
25761         this.el.addClass('open');
25762         
25763         Roo.get(document).on("mouseup", this.onMouseUp, this);
25764         
25765         this.fireEvent("show", this);
25766         
25767         
25768     },
25769     
25770     hide : function()
25771     {
25772         this.fireEvent("beforehide", this);
25773         
25774         this.hidden = true;
25775         this.el.removeClass('open');
25776         
25777         Roo.get(document).un("mouseup", this.onMouseUp);
25778         
25779         this.fireEvent("hide", this);
25780     },
25781     
25782     onMouseUp : function()
25783     {
25784         this.hide();
25785     }
25786     
25787 });
25788
25789  
25790  /*
25791  * - LGPL
25792  *
25793  * menu item
25794  * 
25795  */
25796 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25797
25798 /**
25799  * @class Roo.bootstrap.menu.Item
25800  * @extends Roo.bootstrap.Component
25801  * Bootstrap MenuItem class
25802  * @cfg {Boolean} submenu (true | false) default false
25803  * @cfg {String} html text of the item
25804  * @cfg {String} href the link
25805  * @cfg {Boolean} disable (true | false) default false
25806  * @cfg {Boolean} preventDefault (true | false) default true
25807  * @cfg {String} icon Font awesome icon
25808  * @cfg {String} pos Submenu align to (left | right) default right 
25809  * 
25810  * 
25811  * @constructor
25812  * Create a new Item
25813  * @param {Object} config The config object
25814  */
25815
25816
25817 Roo.bootstrap.menu.Item = function(config){
25818     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25819     this.addEvents({
25820         /**
25821          * @event mouseover
25822          * Fires when the mouse is hovering over this menu
25823          * @param {Roo.bootstrap.menu.Item} this
25824          * @param {Roo.EventObject} e
25825          */
25826         mouseover : true,
25827         /**
25828          * @event mouseout
25829          * Fires when the mouse exits this menu
25830          * @param {Roo.bootstrap.menu.Item} this
25831          * @param {Roo.EventObject} e
25832          */
25833         mouseout : true,
25834         // raw events
25835         /**
25836          * @event click
25837          * The raw click event for the entire grid.
25838          * @param {Roo.EventObject} e
25839          */
25840         click : true
25841     });
25842 };
25843
25844 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25845     
25846     submenu : false,
25847     href : '',
25848     html : '',
25849     preventDefault: true,
25850     disable : false,
25851     icon : false,
25852     pos : 'right',
25853     
25854     getAutoCreate : function()
25855     {
25856         var text = [
25857             {
25858                 tag : 'span',
25859                 cls : 'roo-menu-item-text',
25860                 html : this.html
25861             }
25862         ];
25863         
25864         if(this.icon){
25865             text.unshift({
25866                 tag : 'i',
25867                 cls : 'fa ' + this.icon
25868             })
25869         }
25870         
25871         var cfg = {
25872             tag : 'li',
25873             cn : [
25874                 {
25875                     tag : 'a',
25876                     href : this.href || '#',
25877                     cn : text
25878                 }
25879             ]
25880         };
25881         
25882         if(this.disable){
25883             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25884         }
25885         
25886         if(this.submenu){
25887             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25888             
25889             if(this.pos == 'left'){
25890                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25891             }
25892         }
25893         
25894         return cfg;
25895     },
25896     
25897     initEvents : function() 
25898     {
25899         this.el.on('mouseover', this.onMouseOver, this);
25900         this.el.on('mouseout', this.onMouseOut, this);
25901         
25902         this.el.select('a', true).first().on('click', this.onClick, this);
25903         
25904     },
25905     
25906     onClick : function(e)
25907     {
25908         if(this.preventDefault){
25909             e.preventDefault();
25910         }
25911         
25912         this.fireEvent("click", this, e);
25913     },
25914     
25915     onMouseOver : function(e)
25916     {
25917         if(this.submenu && this.pos == 'left'){
25918             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25919         }
25920         
25921         this.fireEvent("mouseover", this, e);
25922     },
25923     
25924     onMouseOut : function(e)
25925     {
25926         this.fireEvent("mouseout", this, e);
25927     }
25928 });
25929
25930  
25931
25932  /*
25933  * - LGPL
25934  *
25935  * menu separator
25936  * 
25937  */
25938 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25939
25940 /**
25941  * @class Roo.bootstrap.menu.Separator
25942  * @extends Roo.bootstrap.Component
25943  * Bootstrap Separator class
25944  * 
25945  * @constructor
25946  * Create a new Separator
25947  * @param {Object} config The config object
25948  */
25949
25950
25951 Roo.bootstrap.menu.Separator = function(config){
25952     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25953 };
25954
25955 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25956     
25957     getAutoCreate : function(){
25958         var cfg = {
25959             tag : 'li',
25960             cls: 'divider'
25961         };
25962         
25963         return cfg;
25964     }
25965    
25966 });
25967
25968  
25969
25970  /*
25971  * - LGPL
25972  *
25973  * Tooltip
25974  * 
25975  */
25976
25977 /**
25978  * @class Roo.bootstrap.Tooltip
25979  * Bootstrap Tooltip class
25980  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25981  * to determine which dom element triggers the tooltip.
25982  * 
25983  * It needs to add support for additional attributes like tooltip-position
25984  * 
25985  * @constructor
25986  * Create a new Toolti
25987  * @param {Object} config The config object
25988  */
25989
25990 Roo.bootstrap.Tooltip = function(config){
25991     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25992     
25993     this.alignment = Roo.bootstrap.Tooltip.alignment;
25994     
25995     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25996         this.alignment = config.alignment;
25997     }
25998     
25999 };
26000
26001 Roo.apply(Roo.bootstrap.Tooltip, {
26002     /**
26003      * @function init initialize tooltip monitoring.
26004      * @static
26005      */
26006     currentEl : false,
26007     currentTip : false,
26008     currentRegion : false,
26009     
26010     //  init : delay?
26011     
26012     init : function()
26013     {
26014         Roo.get(document).on('mouseover', this.enter ,this);
26015         Roo.get(document).on('mouseout', this.leave, this);
26016          
26017         
26018         this.currentTip = new Roo.bootstrap.Tooltip();
26019     },
26020     
26021     enter : function(ev)
26022     {
26023         var dom = ev.getTarget();
26024         
26025         //Roo.log(['enter',dom]);
26026         var el = Roo.fly(dom);
26027         if (this.currentEl) {
26028             //Roo.log(dom);
26029             //Roo.log(this.currentEl);
26030             //Roo.log(this.currentEl.contains(dom));
26031             if (this.currentEl == el) {
26032                 return;
26033             }
26034             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26035                 return;
26036             }
26037
26038         }
26039         
26040         if (this.currentTip.el) {
26041             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26042         }    
26043         //Roo.log(ev);
26044         
26045         if(!el || el.dom == document){
26046             return;
26047         }
26048         
26049         var bindEl = el;
26050         
26051         // you can not look for children, as if el is the body.. then everythign is the child..
26052         if (!el.attr('tooltip')) { //
26053             if (!el.select("[tooltip]").elements.length) {
26054                 return;
26055             }
26056             // is the mouse over this child...?
26057             bindEl = el.select("[tooltip]").first();
26058             var xy = ev.getXY();
26059             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26060                 //Roo.log("not in region.");
26061                 return;
26062             }
26063             //Roo.log("child element over..");
26064             
26065         }
26066         this.currentEl = bindEl;
26067         this.currentTip.bind(bindEl);
26068         this.currentRegion = Roo.lib.Region.getRegion(dom);
26069         this.currentTip.enter();
26070         
26071     },
26072     leave : function(ev)
26073     {
26074         var dom = ev.getTarget();
26075         //Roo.log(['leave',dom]);
26076         if (!this.currentEl) {
26077             return;
26078         }
26079         
26080         
26081         if (dom != this.currentEl.dom) {
26082             return;
26083         }
26084         var xy = ev.getXY();
26085         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26086             return;
26087         }
26088         // only activate leave if mouse cursor is outside... bounding box..
26089         
26090         
26091         
26092         
26093         if (this.currentTip) {
26094             this.currentTip.leave();
26095         }
26096         //Roo.log('clear currentEl');
26097         this.currentEl = false;
26098         
26099         
26100     },
26101     alignment : {
26102         'left' : ['r-l', [-2,0], 'right'],
26103         'right' : ['l-r', [2,0], 'left'],
26104         'bottom' : ['t-b', [0,2], 'top'],
26105         'top' : [ 'b-t', [0,-2], 'bottom']
26106     }
26107     
26108 });
26109
26110
26111 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26112     
26113     
26114     bindEl : false,
26115     
26116     delay : null, // can be { show : 300 , hide: 500}
26117     
26118     timeout : null,
26119     
26120     hoverState : null, //???
26121     
26122     placement : 'bottom', 
26123     
26124     alignment : false,
26125     
26126     getAutoCreate : function(){
26127     
26128         var cfg = {
26129            cls : 'tooltip',
26130            role : 'tooltip',
26131            cn : [
26132                 {
26133                     cls : 'tooltip-arrow'
26134                 },
26135                 {
26136                     cls : 'tooltip-inner'
26137                 }
26138            ]
26139         };
26140         
26141         return cfg;
26142     },
26143     bind : function(el)
26144     {
26145         this.bindEl = el;
26146     },
26147       
26148     
26149     enter : function () {
26150        
26151         if (this.timeout != null) {
26152             clearTimeout(this.timeout);
26153         }
26154         
26155         this.hoverState = 'in';
26156          //Roo.log("enter - show");
26157         if (!this.delay || !this.delay.show) {
26158             this.show();
26159             return;
26160         }
26161         var _t = this;
26162         this.timeout = setTimeout(function () {
26163             if (_t.hoverState == 'in') {
26164                 _t.show();
26165             }
26166         }, this.delay.show);
26167     },
26168     leave : function()
26169     {
26170         clearTimeout(this.timeout);
26171     
26172         this.hoverState = 'out';
26173          if (!this.delay || !this.delay.hide) {
26174             this.hide();
26175             return;
26176         }
26177        
26178         var _t = this;
26179         this.timeout = setTimeout(function () {
26180             //Roo.log("leave - timeout");
26181             
26182             if (_t.hoverState == 'out') {
26183                 _t.hide();
26184                 Roo.bootstrap.Tooltip.currentEl = false;
26185             }
26186         }, delay);
26187     },
26188     
26189     show : function (msg)
26190     {
26191         if (!this.el) {
26192             this.render(document.body);
26193         }
26194         // set content.
26195         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26196         
26197         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26198         
26199         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26200         
26201         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26202         
26203         var placement = typeof this.placement == 'function' ?
26204             this.placement.call(this, this.el, on_el) :
26205             this.placement;
26206             
26207         var autoToken = /\s?auto?\s?/i;
26208         var autoPlace = autoToken.test(placement);
26209         if (autoPlace) {
26210             placement = placement.replace(autoToken, '') || 'top';
26211         }
26212         
26213         //this.el.detach()
26214         //this.el.setXY([0,0]);
26215         this.el.show();
26216         //this.el.dom.style.display='block';
26217         
26218         //this.el.appendTo(on_el);
26219         
26220         var p = this.getPosition();
26221         var box = this.el.getBox();
26222         
26223         if (autoPlace) {
26224             // fixme..
26225         }
26226         
26227         var align = this.alignment[placement];
26228         
26229         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26230         
26231         if(placement == 'top' || placement == 'bottom'){
26232             if(xy[0] < 0){
26233                 placement = 'right';
26234             }
26235             
26236             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26237                 placement = 'left';
26238             }
26239             
26240             var scroll = Roo.select('body', true).first().getScroll();
26241             
26242             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26243                 placement = 'top';
26244             }
26245             
26246             align = this.alignment[placement];
26247         }
26248         
26249         this.el.alignTo(this.bindEl, align[0],align[1]);
26250         //var arrow = this.el.select('.arrow',true).first();
26251         //arrow.set(align[2], 
26252         
26253         this.el.addClass(placement);
26254         
26255         this.el.addClass('in fade');
26256         
26257         this.hoverState = null;
26258         
26259         if (this.el.hasClass('fade')) {
26260             // fade it?
26261         }
26262         
26263     },
26264     hide : function()
26265     {
26266          
26267         if (!this.el) {
26268             return;
26269         }
26270         //this.el.setXY([0,0]);
26271         this.el.removeClass('in');
26272         //this.el.hide();
26273         
26274     }
26275     
26276 });
26277  
26278
26279  /*
26280  * - LGPL
26281  *
26282  * Location Picker
26283  * 
26284  */
26285
26286 /**
26287  * @class Roo.bootstrap.LocationPicker
26288  * @extends Roo.bootstrap.Component
26289  * Bootstrap LocationPicker class
26290  * @cfg {Number} latitude Position when init default 0
26291  * @cfg {Number} longitude Position when init default 0
26292  * @cfg {Number} zoom default 15
26293  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26294  * @cfg {Boolean} mapTypeControl default false
26295  * @cfg {Boolean} disableDoubleClickZoom default false
26296  * @cfg {Boolean} scrollwheel default true
26297  * @cfg {Boolean} streetViewControl default false
26298  * @cfg {Number} radius default 0
26299  * @cfg {String} locationName
26300  * @cfg {Boolean} draggable default true
26301  * @cfg {Boolean} enableAutocomplete default false
26302  * @cfg {Boolean} enableReverseGeocode default true
26303  * @cfg {String} markerTitle
26304  * 
26305  * @constructor
26306  * Create a new LocationPicker
26307  * @param {Object} config The config object
26308  */
26309
26310
26311 Roo.bootstrap.LocationPicker = function(config){
26312     
26313     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26314     
26315     this.addEvents({
26316         /**
26317          * @event initial
26318          * Fires when the picker initialized.
26319          * @param {Roo.bootstrap.LocationPicker} this
26320          * @param {Google Location} location
26321          */
26322         initial : true,
26323         /**
26324          * @event positionchanged
26325          * Fires when the picker position changed.
26326          * @param {Roo.bootstrap.LocationPicker} this
26327          * @param {Google Location} location
26328          */
26329         positionchanged : true,
26330         /**
26331          * @event resize
26332          * Fires when the map resize.
26333          * @param {Roo.bootstrap.LocationPicker} this
26334          */
26335         resize : true,
26336         /**
26337          * @event show
26338          * Fires when the map show.
26339          * @param {Roo.bootstrap.LocationPicker} this
26340          */
26341         show : true,
26342         /**
26343          * @event hide
26344          * Fires when the map hide.
26345          * @param {Roo.bootstrap.LocationPicker} this
26346          */
26347         hide : true,
26348         /**
26349          * @event mapClick
26350          * Fires when click the map.
26351          * @param {Roo.bootstrap.LocationPicker} this
26352          * @param {Map event} e
26353          */
26354         mapClick : true,
26355         /**
26356          * @event mapRightClick
26357          * Fires when right click the map.
26358          * @param {Roo.bootstrap.LocationPicker} this
26359          * @param {Map event} e
26360          */
26361         mapRightClick : true,
26362         /**
26363          * @event markerClick
26364          * Fires when click the marker.
26365          * @param {Roo.bootstrap.LocationPicker} this
26366          * @param {Map event} e
26367          */
26368         markerClick : true,
26369         /**
26370          * @event markerRightClick
26371          * Fires when right click the marker.
26372          * @param {Roo.bootstrap.LocationPicker} this
26373          * @param {Map event} e
26374          */
26375         markerRightClick : true,
26376         /**
26377          * @event OverlayViewDraw
26378          * Fires when OverlayView Draw
26379          * @param {Roo.bootstrap.LocationPicker} this
26380          */
26381         OverlayViewDraw : true,
26382         /**
26383          * @event OverlayViewOnAdd
26384          * Fires when OverlayView Draw
26385          * @param {Roo.bootstrap.LocationPicker} this
26386          */
26387         OverlayViewOnAdd : true,
26388         /**
26389          * @event OverlayViewOnRemove
26390          * Fires when OverlayView Draw
26391          * @param {Roo.bootstrap.LocationPicker} this
26392          */
26393         OverlayViewOnRemove : true,
26394         /**
26395          * @event OverlayViewShow
26396          * Fires when OverlayView Draw
26397          * @param {Roo.bootstrap.LocationPicker} this
26398          * @param {Pixel} cpx
26399          */
26400         OverlayViewShow : true,
26401         /**
26402          * @event OverlayViewHide
26403          * Fires when OverlayView Draw
26404          * @param {Roo.bootstrap.LocationPicker} this
26405          */
26406         OverlayViewHide : true,
26407         /**
26408          * @event loadexception
26409          * Fires when load google lib failed.
26410          * @param {Roo.bootstrap.LocationPicker} this
26411          */
26412         loadexception : true
26413     });
26414         
26415 };
26416
26417 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26418     
26419     gMapContext: false,
26420     
26421     latitude: 0,
26422     longitude: 0,
26423     zoom: 15,
26424     mapTypeId: false,
26425     mapTypeControl: false,
26426     disableDoubleClickZoom: false,
26427     scrollwheel: true,
26428     streetViewControl: false,
26429     radius: 0,
26430     locationName: '',
26431     draggable: true,
26432     enableAutocomplete: false,
26433     enableReverseGeocode: true,
26434     markerTitle: '',
26435     
26436     getAutoCreate: function()
26437     {
26438
26439         var cfg = {
26440             tag: 'div',
26441             cls: 'roo-location-picker'
26442         };
26443         
26444         return cfg
26445     },
26446     
26447     initEvents: function(ct, position)
26448     {       
26449         if(!this.el.getWidth() || this.isApplied()){
26450             return;
26451         }
26452         
26453         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26454         
26455         this.initial();
26456     },
26457     
26458     initial: function()
26459     {
26460         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26461             this.fireEvent('loadexception', this);
26462             return;
26463         }
26464         
26465         if(!this.mapTypeId){
26466             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26467         }
26468         
26469         this.gMapContext = this.GMapContext();
26470         
26471         this.initOverlayView();
26472         
26473         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26474         
26475         var _this = this;
26476                 
26477         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26478             _this.setPosition(_this.gMapContext.marker.position);
26479         });
26480         
26481         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26482             _this.fireEvent('mapClick', this, event);
26483             
26484         });
26485
26486         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26487             _this.fireEvent('mapRightClick', this, event);
26488             
26489         });
26490         
26491         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26492             _this.fireEvent('markerClick', this, event);
26493             
26494         });
26495
26496         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26497             _this.fireEvent('markerRightClick', this, event);
26498             
26499         });
26500         
26501         this.setPosition(this.gMapContext.location);
26502         
26503         this.fireEvent('initial', this, this.gMapContext.location);
26504     },
26505     
26506     initOverlayView: function()
26507     {
26508         var _this = this;
26509         
26510         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26511             
26512             draw: function()
26513             {
26514                 _this.fireEvent('OverlayViewDraw', _this);
26515             },
26516             
26517             onAdd: function()
26518             {
26519                 _this.fireEvent('OverlayViewOnAdd', _this);
26520             },
26521             
26522             onRemove: function()
26523             {
26524                 _this.fireEvent('OverlayViewOnRemove', _this);
26525             },
26526             
26527             show: function(cpx)
26528             {
26529                 _this.fireEvent('OverlayViewShow', _this, cpx);
26530             },
26531             
26532             hide: function()
26533             {
26534                 _this.fireEvent('OverlayViewHide', _this);
26535             }
26536             
26537         });
26538     },
26539     
26540     fromLatLngToContainerPixel: function(event)
26541     {
26542         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26543     },
26544     
26545     isApplied: function() 
26546     {
26547         return this.getGmapContext() == false ? false : true;
26548     },
26549     
26550     getGmapContext: function() 
26551     {
26552         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26553     },
26554     
26555     GMapContext: function() 
26556     {
26557         var position = new google.maps.LatLng(this.latitude, this.longitude);
26558         
26559         var _map = new google.maps.Map(this.el.dom, {
26560             center: position,
26561             zoom: this.zoom,
26562             mapTypeId: this.mapTypeId,
26563             mapTypeControl: this.mapTypeControl,
26564             disableDoubleClickZoom: this.disableDoubleClickZoom,
26565             scrollwheel: this.scrollwheel,
26566             streetViewControl: this.streetViewControl,
26567             locationName: this.locationName,
26568             draggable: this.draggable,
26569             enableAutocomplete: this.enableAutocomplete,
26570             enableReverseGeocode: this.enableReverseGeocode
26571         });
26572         
26573         var _marker = new google.maps.Marker({
26574             position: position,
26575             map: _map,
26576             title: this.markerTitle,
26577             draggable: this.draggable
26578         });
26579         
26580         return {
26581             map: _map,
26582             marker: _marker,
26583             circle: null,
26584             location: position,
26585             radius: this.radius,
26586             locationName: this.locationName,
26587             addressComponents: {
26588                 formatted_address: null,
26589                 addressLine1: null,
26590                 addressLine2: null,
26591                 streetName: null,
26592                 streetNumber: null,
26593                 city: null,
26594                 district: null,
26595                 state: null,
26596                 stateOrProvince: null
26597             },
26598             settings: this,
26599             domContainer: this.el.dom,
26600             geodecoder: new google.maps.Geocoder()
26601         };
26602     },
26603     
26604     drawCircle: function(center, radius, options) 
26605     {
26606         if (this.gMapContext.circle != null) {
26607             this.gMapContext.circle.setMap(null);
26608         }
26609         if (radius > 0) {
26610             radius *= 1;
26611             options = Roo.apply({}, options, {
26612                 strokeColor: "#0000FF",
26613                 strokeOpacity: .35,
26614                 strokeWeight: 2,
26615                 fillColor: "#0000FF",
26616                 fillOpacity: .2
26617             });
26618             
26619             options.map = this.gMapContext.map;
26620             options.radius = radius;
26621             options.center = center;
26622             this.gMapContext.circle = new google.maps.Circle(options);
26623             return this.gMapContext.circle;
26624         }
26625         
26626         return null;
26627     },
26628     
26629     setPosition: function(location) 
26630     {
26631         this.gMapContext.location = location;
26632         this.gMapContext.marker.setPosition(location);
26633         this.gMapContext.map.panTo(location);
26634         this.drawCircle(location, this.gMapContext.radius, {});
26635         
26636         var _this = this;
26637         
26638         if (this.gMapContext.settings.enableReverseGeocode) {
26639             this.gMapContext.geodecoder.geocode({
26640                 latLng: this.gMapContext.location
26641             }, function(results, status) {
26642                 
26643                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26644                     _this.gMapContext.locationName = results[0].formatted_address;
26645                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26646                     
26647                     _this.fireEvent('positionchanged', this, location);
26648                 }
26649             });
26650             
26651             return;
26652         }
26653         
26654         this.fireEvent('positionchanged', this, location);
26655     },
26656     
26657     resize: function()
26658     {
26659         google.maps.event.trigger(this.gMapContext.map, "resize");
26660         
26661         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26662         
26663         this.fireEvent('resize', this);
26664     },
26665     
26666     setPositionByLatLng: function(latitude, longitude)
26667     {
26668         this.setPosition(new google.maps.LatLng(latitude, longitude));
26669     },
26670     
26671     getCurrentPosition: function() 
26672     {
26673         return {
26674             latitude: this.gMapContext.location.lat(),
26675             longitude: this.gMapContext.location.lng()
26676         };
26677     },
26678     
26679     getAddressName: function() 
26680     {
26681         return this.gMapContext.locationName;
26682     },
26683     
26684     getAddressComponents: function() 
26685     {
26686         return this.gMapContext.addressComponents;
26687     },
26688     
26689     address_component_from_google_geocode: function(address_components) 
26690     {
26691         var result = {};
26692         
26693         for (var i = 0; i < address_components.length; i++) {
26694             var component = address_components[i];
26695             if (component.types.indexOf("postal_code") >= 0) {
26696                 result.postalCode = component.short_name;
26697             } else if (component.types.indexOf("street_number") >= 0) {
26698                 result.streetNumber = component.short_name;
26699             } else if (component.types.indexOf("route") >= 0) {
26700                 result.streetName = component.short_name;
26701             } else if (component.types.indexOf("neighborhood") >= 0) {
26702                 result.city = component.short_name;
26703             } else if (component.types.indexOf("locality") >= 0) {
26704                 result.city = component.short_name;
26705             } else if (component.types.indexOf("sublocality") >= 0) {
26706                 result.district = component.short_name;
26707             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26708                 result.stateOrProvince = component.short_name;
26709             } else if (component.types.indexOf("country") >= 0) {
26710                 result.country = component.short_name;
26711             }
26712         }
26713         
26714         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26715         result.addressLine2 = "";
26716         return result;
26717     },
26718     
26719     setZoomLevel: function(zoom)
26720     {
26721         this.gMapContext.map.setZoom(zoom);
26722     },
26723     
26724     show: function()
26725     {
26726         if(!this.el){
26727             return;
26728         }
26729         
26730         this.el.show();
26731         
26732         this.resize();
26733         
26734         this.fireEvent('show', this);
26735     },
26736     
26737     hide: function()
26738     {
26739         if(!this.el){
26740             return;
26741         }
26742         
26743         this.el.hide();
26744         
26745         this.fireEvent('hide', this);
26746     }
26747     
26748 });
26749
26750 Roo.apply(Roo.bootstrap.LocationPicker, {
26751     
26752     OverlayView : function(map, options)
26753     {
26754         options = options || {};
26755         
26756         this.setMap(map);
26757     }
26758     
26759     
26760 });/*
26761  * - LGPL
26762  *
26763  * Alert
26764  * 
26765  */
26766
26767 /**
26768  * @class Roo.bootstrap.Alert
26769  * @extends Roo.bootstrap.Component
26770  * Bootstrap Alert class
26771  * @cfg {String} title The title of alert
26772  * @cfg {String} html The content of alert
26773  * @cfg {String} weight (  success | info | warning | danger )
26774  * @cfg {String} faicon font-awesomeicon
26775  * 
26776  * @constructor
26777  * Create a new alert
26778  * @param {Object} config The config object
26779  */
26780
26781
26782 Roo.bootstrap.Alert = function(config){
26783     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26784     
26785 };
26786
26787 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26788     
26789     title: '',
26790     html: '',
26791     weight: false,
26792     faicon: false,
26793     
26794     getAutoCreate : function()
26795     {
26796         
26797         var cfg = {
26798             tag : 'div',
26799             cls : 'alert',
26800             cn : [
26801                 {
26802                     tag : 'i',
26803                     cls : 'roo-alert-icon'
26804                     
26805                 },
26806                 {
26807                     tag : 'b',
26808                     cls : 'roo-alert-title',
26809                     html : this.title
26810                 },
26811                 {
26812                     tag : 'span',
26813                     cls : 'roo-alert-text',
26814                     html : this.html
26815                 }
26816             ]
26817         };
26818         
26819         if(this.faicon){
26820             cfg.cn[0].cls += ' fa ' + this.faicon;
26821         }
26822         
26823         if(this.weight){
26824             cfg.cls += ' alert-' + this.weight;
26825         }
26826         
26827         return cfg;
26828     },
26829     
26830     initEvents: function() 
26831     {
26832         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26833     },
26834     
26835     setTitle : function(str)
26836     {
26837         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26838     },
26839     
26840     setText : function(str)
26841     {
26842         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26843     },
26844     
26845     setWeight : function(weight)
26846     {
26847         if(this.weight){
26848             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26849         }
26850         
26851         this.weight = weight;
26852         
26853         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26854     },
26855     
26856     setIcon : function(icon)
26857     {
26858         if(this.faicon){
26859             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26860         }
26861         
26862         this.faicon = icon;
26863         
26864         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26865     },
26866     
26867     hide: function() 
26868     {
26869         this.el.hide();   
26870     },
26871     
26872     show: function() 
26873     {  
26874         this.el.show();   
26875     }
26876     
26877 });
26878
26879  
26880 /*
26881 * Licence: LGPL
26882 */
26883
26884 /**
26885  * @class Roo.bootstrap.UploadCropbox
26886  * @extends Roo.bootstrap.Component
26887  * Bootstrap UploadCropbox class
26888  * @cfg {String} emptyText show when image has been loaded
26889  * @cfg {String} rotateNotify show when image too small to rotate
26890  * @cfg {Number} errorTimeout default 3000
26891  * @cfg {Number} minWidth default 300
26892  * @cfg {Number} minHeight default 300
26893  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26894  * @cfg {Boolean} isDocument (true|false) default false
26895  * @cfg {String} url action url
26896  * @cfg {String} paramName default 'imageUpload'
26897  * @cfg {String} method default POST
26898  * @cfg {Boolean} loadMask (true|false) default true
26899  * @cfg {Boolean} loadingText default 'Loading...'
26900  * 
26901  * @constructor
26902  * Create a new UploadCropbox
26903  * @param {Object} config The config object
26904  */
26905
26906 Roo.bootstrap.UploadCropbox = function(config){
26907     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26908     
26909     this.addEvents({
26910         /**
26911          * @event beforeselectfile
26912          * Fire before select file
26913          * @param {Roo.bootstrap.UploadCropbox} this
26914          */
26915         "beforeselectfile" : true,
26916         /**
26917          * @event initial
26918          * Fire after initEvent
26919          * @param {Roo.bootstrap.UploadCropbox} this
26920          */
26921         "initial" : true,
26922         /**
26923          * @event crop
26924          * Fire after initEvent
26925          * @param {Roo.bootstrap.UploadCropbox} this
26926          * @param {String} data
26927          */
26928         "crop" : true,
26929         /**
26930          * @event prepare
26931          * Fire when preparing the file data
26932          * @param {Roo.bootstrap.UploadCropbox} this
26933          * @param {Object} file
26934          */
26935         "prepare" : true,
26936         /**
26937          * @event exception
26938          * Fire when get exception
26939          * @param {Roo.bootstrap.UploadCropbox} this
26940          * @param {XMLHttpRequest} xhr
26941          */
26942         "exception" : true,
26943         /**
26944          * @event beforeloadcanvas
26945          * Fire before load the canvas
26946          * @param {Roo.bootstrap.UploadCropbox} this
26947          * @param {String} src
26948          */
26949         "beforeloadcanvas" : true,
26950         /**
26951          * @event trash
26952          * Fire when trash image
26953          * @param {Roo.bootstrap.UploadCropbox} this
26954          */
26955         "trash" : true,
26956         /**
26957          * @event download
26958          * Fire when download the image
26959          * @param {Roo.bootstrap.UploadCropbox} this
26960          */
26961         "download" : true,
26962         /**
26963          * @event footerbuttonclick
26964          * Fire when footerbuttonclick
26965          * @param {Roo.bootstrap.UploadCropbox} this
26966          * @param {String} type
26967          */
26968         "footerbuttonclick" : true,
26969         /**
26970          * @event resize
26971          * Fire when resize
26972          * @param {Roo.bootstrap.UploadCropbox} this
26973          */
26974         "resize" : true,
26975         /**
26976          * @event rotate
26977          * Fire when rotate the image
26978          * @param {Roo.bootstrap.UploadCropbox} this
26979          * @param {String} pos
26980          */
26981         "rotate" : true,
26982         /**
26983          * @event inspect
26984          * Fire when inspect the file
26985          * @param {Roo.bootstrap.UploadCropbox} this
26986          * @param {Object} file
26987          */
26988         "inspect" : true,
26989         /**
26990          * @event upload
26991          * Fire when xhr upload the file
26992          * @param {Roo.bootstrap.UploadCropbox} this
26993          * @param {Object} data
26994          */
26995         "upload" : true,
26996         /**
26997          * @event arrange
26998          * Fire when arrange the file data
26999          * @param {Roo.bootstrap.UploadCropbox} this
27000          * @param {Object} formData
27001          */
27002         "arrange" : true
27003     });
27004     
27005     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27006 };
27007
27008 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27009     
27010     emptyText : 'Click to upload image',
27011     rotateNotify : 'Image is too small to rotate',
27012     errorTimeout : 3000,
27013     scale : 0,
27014     baseScale : 1,
27015     rotate : 0,
27016     dragable : false,
27017     pinching : false,
27018     mouseX : 0,
27019     mouseY : 0,
27020     cropData : false,
27021     minWidth : 300,
27022     minHeight : 300,
27023     file : false,
27024     exif : {},
27025     baseRotate : 1,
27026     cropType : 'image/jpeg',
27027     buttons : false,
27028     canvasLoaded : false,
27029     isDocument : false,
27030     method : 'POST',
27031     paramName : 'imageUpload',
27032     loadMask : true,
27033     loadingText : 'Loading...',
27034     maskEl : false,
27035     
27036     getAutoCreate : function()
27037     {
27038         var cfg = {
27039             tag : 'div',
27040             cls : 'roo-upload-cropbox',
27041             cn : [
27042                 {
27043                     tag : 'input',
27044                     cls : 'roo-upload-cropbox-selector',
27045                     type : 'file'
27046                 },
27047                 {
27048                     tag : 'div',
27049                     cls : 'roo-upload-cropbox-body',
27050                     style : 'cursor:pointer',
27051                     cn : [
27052                         {
27053                             tag : 'div',
27054                             cls : 'roo-upload-cropbox-preview'
27055                         },
27056                         {
27057                             tag : 'div',
27058                             cls : 'roo-upload-cropbox-thumb'
27059                         },
27060                         {
27061                             tag : 'div',
27062                             cls : 'roo-upload-cropbox-empty-notify',
27063                             html : this.emptyText
27064                         },
27065                         {
27066                             tag : 'div',
27067                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27068                             html : this.rotateNotify
27069                         }
27070                     ]
27071                 },
27072                 {
27073                     tag : 'div',
27074                     cls : 'roo-upload-cropbox-footer',
27075                     cn : {
27076                         tag : 'div',
27077                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27078                         cn : []
27079                     }
27080                 }
27081             ]
27082         };
27083         
27084         return cfg;
27085     },
27086     
27087     onRender : function(ct, position)
27088     {
27089         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27090         
27091         if (this.buttons.length) {
27092             
27093             Roo.each(this.buttons, function(bb) {
27094                 
27095                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27096                 
27097                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27098                 
27099             }, this);
27100         }
27101         
27102         if(this.loadMask){
27103             this.maskEl = this.el;
27104         }
27105     },
27106     
27107     initEvents : function()
27108     {
27109         this.urlAPI = (window.createObjectURL && window) || 
27110                                 (window.URL && URL.revokeObjectURL && URL) || 
27111                                 (window.webkitURL && webkitURL);
27112                         
27113         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27114         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27115         
27116         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27117         this.selectorEl.hide();
27118         
27119         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27120         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27121         
27122         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27123         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27124         this.thumbEl.hide();
27125         
27126         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27127         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27128         
27129         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27130         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27131         this.errorEl.hide();
27132         
27133         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27134         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27135         this.footerEl.hide();
27136         
27137         this.setThumbBoxSize();
27138         
27139         this.bind();
27140         
27141         this.resize();
27142         
27143         this.fireEvent('initial', this);
27144     },
27145
27146     bind : function()
27147     {
27148         var _this = this;
27149         
27150         window.addEventListener("resize", function() { _this.resize(); } );
27151         
27152         this.bodyEl.on('click', this.beforeSelectFile, this);
27153         
27154         if(Roo.isTouch){
27155             this.bodyEl.on('touchstart', this.onTouchStart, this);
27156             this.bodyEl.on('touchmove', this.onTouchMove, this);
27157             this.bodyEl.on('touchend', this.onTouchEnd, this);
27158         }
27159         
27160         if(!Roo.isTouch){
27161             this.bodyEl.on('mousedown', this.onMouseDown, this);
27162             this.bodyEl.on('mousemove', this.onMouseMove, this);
27163             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27164             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27165             Roo.get(document).on('mouseup', this.onMouseUp, this);
27166         }
27167         
27168         this.selectorEl.on('change', this.onFileSelected, this);
27169     },
27170     
27171     reset : function()
27172     {    
27173         this.scale = 0;
27174         this.baseScale = 1;
27175         this.rotate = 0;
27176         this.baseRotate = 1;
27177         this.dragable = false;
27178         this.pinching = false;
27179         this.mouseX = 0;
27180         this.mouseY = 0;
27181         this.cropData = false;
27182         this.notifyEl.dom.innerHTML = this.emptyText;
27183         
27184         this.selectorEl.dom.value = '';
27185         
27186     },
27187     
27188     resize : function()
27189     {
27190         if(this.fireEvent('resize', this) != false){
27191             this.setThumbBoxPosition();
27192             this.setCanvasPosition();
27193         }
27194     },
27195     
27196     onFooterButtonClick : function(e, el, o, type)
27197     {
27198         switch (type) {
27199             case 'rotate-left' :
27200                 this.onRotateLeft(e);
27201                 break;
27202             case 'rotate-right' :
27203                 this.onRotateRight(e);
27204                 break;
27205             case 'picture' :
27206                 this.beforeSelectFile(e);
27207                 break;
27208             case 'trash' :
27209                 this.trash(e);
27210                 break;
27211             case 'crop' :
27212                 this.crop(e);
27213                 break;
27214             case 'download' :
27215                 this.download(e);
27216                 break;
27217             default :
27218                 break;
27219         }
27220         
27221         this.fireEvent('footerbuttonclick', this, type);
27222     },
27223     
27224     beforeSelectFile : function(e)
27225     {
27226         e.preventDefault();
27227         
27228         if(this.fireEvent('beforeselectfile', this) != false){
27229             this.selectorEl.dom.click();
27230         }
27231     },
27232     
27233     onFileSelected : function(e)
27234     {
27235         e.preventDefault();
27236         
27237         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27238             return;
27239         }
27240         
27241         var file = this.selectorEl.dom.files[0];
27242         
27243         if(this.fireEvent('inspect', this, file) != false){
27244             this.prepare(file);
27245         }
27246         
27247     },
27248     
27249     trash : function(e)
27250     {
27251         this.fireEvent('trash', this);
27252     },
27253     
27254     download : function(e)
27255     {
27256         this.fireEvent('download', this);
27257     },
27258     
27259     loadCanvas : function(src)
27260     {   
27261         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27262             
27263             this.reset();
27264             
27265             this.imageEl = document.createElement('img');
27266             
27267             var _this = this;
27268             
27269             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27270             
27271             this.imageEl.src = src;
27272         }
27273     },
27274     
27275     onLoadCanvas : function()
27276     {   
27277         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27278         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27279         
27280         this.bodyEl.un('click', this.beforeSelectFile, this);
27281         
27282         this.notifyEl.hide();
27283         this.thumbEl.show();
27284         this.footerEl.show();
27285         
27286         this.baseRotateLevel();
27287         
27288         if(this.isDocument){
27289             this.setThumbBoxSize();
27290         }
27291         
27292         this.setThumbBoxPosition();
27293         
27294         this.baseScaleLevel();
27295         
27296         this.draw();
27297         
27298         this.resize();
27299         
27300         this.canvasLoaded = true;
27301         
27302         if(this.loadMask){
27303             this.maskEl.unmask();
27304         }
27305         
27306     },
27307     
27308     setCanvasPosition : function()
27309     {   
27310         if(!this.canvasEl){
27311             return;
27312         }
27313         
27314         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27315         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27316         
27317         this.previewEl.setLeft(pw);
27318         this.previewEl.setTop(ph);
27319         
27320     },
27321     
27322     onMouseDown : function(e)
27323     {   
27324         e.stopEvent();
27325         
27326         this.dragable = true;
27327         this.pinching = false;
27328         
27329         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27330             this.dragable = false;
27331             return;
27332         }
27333         
27334         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27335         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27336         
27337     },
27338     
27339     onMouseMove : function(e)
27340     {   
27341         e.stopEvent();
27342         
27343         if(!this.canvasLoaded){
27344             return;
27345         }
27346         
27347         if (!this.dragable){
27348             return;
27349         }
27350         
27351         var minX = Math.ceil(this.thumbEl.getLeft(true));
27352         var minY = Math.ceil(this.thumbEl.getTop(true));
27353         
27354         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27355         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27356         
27357         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27358         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27359         
27360         x = x - this.mouseX;
27361         y = y - this.mouseY;
27362         
27363         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27364         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27365         
27366         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27367         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27368         
27369         this.previewEl.setLeft(bgX);
27370         this.previewEl.setTop(bgY);
27371         
27372         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27373         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27374     },
27375     
27376     onMouseUp : function(e)
27377     {   
27378         e.stopEvent();
27379         
27380         this.dragable = false;
27381     },
27382     
27383     onMouseWheel : function(e)
27384     {   
27385         e.stopEvent();
27386         
27387         this.startScale = this.scale;
27388         
27389         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27390         
27391         if(!this.zoomable()){
27392             this.scale = this.startScale;
27393             return;
27394         }
27395         
27396         this.draw();
27397         
27398         return;
27399     },
27400     
27401     zoomable : function()
27402     {
27403         var minScale = this.thumbEl.getWidth() / this.minWidth;
27404         
27405         if(this.minWidth < this.minHeight){
27406             minScale = this.thumbEl.getHeight() / this.minHeight;
27407         }
27408         
27409         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27410         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27411         
27412         if(
27413                 this.isDocument &&
27414                 (this.rotate == 0 || this.rotate == 180) && 
27415                 (
27416                     width > this.imageEl.OriginWidth || 
27417                     height > this.imageEl.OriginHeight ||
27418                     (width < this.minWidth && height < this.minHeight)
27419                 )
27420         ){
27421             return false;
27422         }
27423         
27424         if(
27425                 this.isDocument &&
27426                 (this.rotate == 90 || this.rotate == 270) && 
27427                 (
27428                     width > this.imageEl.OriginWidth || 
27429                     height > this.imageEl.OriginHeight ||
27430                     (width < this.minHeight && height < this.minWidth)
27431                 )
27432         ){
27433             return false;
27434         }
27435         
27436         if(
27437                 !this.isDocument &&
27438                 (this.rotate == 0 || this.rotate == 180) && 
27439                 (
27440                     width < this.minWidth || 
27441                     width > this.imageEl.OriginWidth || 
27442                     height < this.minHeight || 
27443                     height > this.imageEl.OriginHeight
27444                 )
27445         ){
27446             return false;
27447         }
27448         
27449         if(
27450                 !this.isDocument &&
27451                 (this.rotate == 90 || this.rotate == 270) && 
27452                 (
27453                     width < this.minHeight || 
27454                     width > this.imageEl.OriginWidth || 
27455                     height < this.minWidth || 
27456                     height > this.imageEl.OriginHeight
27457                 )
27458         ){
27459             return false;
27460         }
27461         
27462         return true;
27463         
27464     },
27465     
27466     onRotateLeft : function(e)
27467     {   
27468         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27469             
27470             var minScale = this.thumbEl.getWidth() / this.minWidth;
27471             
27472             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27473             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27474             
27475             this.startScale = this.scale;
27476             
27477             while (this.getScaleLevel() < minScale){
27478             
27479                 this.scale = this.scale + 1;
27480                 
27481                 if(!this.zoomable()){
27482                     break;
27483                 }
27484                 
27485                 if(
27486                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27487                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27488                 ){
27489                     continue;
27490                 }
27491                 
27492                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27493
27494                 this.draw();
27495                 
27496                 return;
27497             }
27498             
27499             this.scale = this.startScale;
27500             
27501             this.onRotateFail();
27502             
27503             return false;
27504         }
27505         
27506         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27507
27508         if(this.isDocument){
27509             this.setThumbBoxSize();
27510             this.setThumbBoxPosition();
27511             this.setCanvasPosition();
27512         }
27513         
27514         this.draw();
27515         
27516         this.fireEvent('rotate', this, 'left');
27517         
27518     },
27519     
27520     onRotateRight : function(e)
27521     {
27522         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27523             
27524             var minScale = this.thumbEl.getWidth() / this.minWidth;
27525         
27526             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27527             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27528             
27529             this.startScale = this.scale;
27530             
27531             while (this.getScaleLevel() < minScale){
27532             
27533                 this.scale = this.scale + 1;
27534                 
27535                 if(!this.zoomable()){
27536                     break;
27537                 }
27538                 
27539                 if(
27540                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27541                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27542                 ){
27543                     continue;
27544                 }
27545                 
27546                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27547
27548                 this.draw();
27549                 
27550                 return;
27551             }
27552             
27553             this.scale = this.startScale;
27554             
27555             this.onRotateFail();
27556             
27557             return false;
27558         }
27559         
27560         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27561
27562         if(this.isDocument){
27563             this.setThumbBoxSize();
27564             this.setThumbBoxPosition();
27565             this.setCanvasPosition();
27566         }
27567         
27568         this.draw();
27569         
27570         this.fireEvent('rotate', this, 'right');
27571     },
27572     
27573     onRotateFail : function()
27574     {
27575         this.errorEl.show(true);
27576         
27577         var _this = this;
27578         
27579         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27580     },
27581     
27582     draw : function()
27583     {
27584         this.previewEl.dom.innerHTML = '';
27585         
27586         var canvasEl = document.createElement("canvas");
27587         
27588         var contextEl = canvasEl.getContext("2d");
27589         
27590         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27591         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27592         var center = this.imageEl.OriginWidth / 2;
27593         
27594         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27595             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27596             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27597             center = this.imageEl.OriginHeight / 2;
27598         }
27599         
27600         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27601         
27602         contextEl.translate(center, center);
27603         contextEl.rotate(this.rotate * Math.PI / 180);
27604
27605         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27606         
27607         this.canvasEl = document.createElement("canvas");
27608         
27609         this.contextEl = this.canvasEl.getContext("2d");
27610         
27611         switch (this.rotate) {
27612             case 0 :
27613                 
27614                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27615                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27616                 
27617                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27618                 
27619                 break;
27620             case 90 : 
27621                 
27622                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27623                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27624                 
27625                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27626                     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);
27627                     break;
27628                 }
27629                 
27630                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27631                 
27632                 break;
27633             case 180 :
27634                 
27635                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27636                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27637                 
27638                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27639                     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);
27640                     break;
27641                 }
27642                 
27643                 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);
27644                 
27645                 break;
27646             case 270 :
27647                 
27648                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27649                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27650         
27651                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27652                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27653                     break;
27654                 }
27655                 
27656                 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);
27657                 
27658                 break;
27659             default : 
27660                 break;
27661         }
27662         
27663         this.previewEl.appendChild(this.canvasEl);
27664         
27665         this.setCanvasPosition();
27666     },
27667     
27668     crop : function()
27669     {
27670         if(!this.canvasLoaded){
27671             return;
27672         }
27673         
27674         var imageCanvas = document.createElement("canvas");
27675         
27676         var imageContext = imageCanvas.getContext("2d");
27677         
27678         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27679         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27680         
27681         var center = imageCanvas.width / 2;
27682         
27683         imageContext.translate(center, center);
27684         
27685         imageContext.rotate(this.rotate * Math.PI / 180);
27686         
27687         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27688         
27689         var canvas = document.createElement("canvas");
27690         
27691         var context = canvas.getContext("2d");
27692                 
27693         canvas.width = this.minWidth;
27694         canvas.height = this.minHeight;
27695
27696         switch (this.rotate) {
27697             case 0 :
27698                 
27699                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27700                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27701                 
27702                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27703                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27704                 
27705                 var targetWidth = this.minWidth - 2 * x;
27706                 var targetHeight = this.minHeight - 2 * y;
27707                 
27708                 var scale = 1;
27709                 
27710                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27711                     scale = targetWidth / width;
27712                 }
27713                 
27714                 if(x > 0 && y == 0){
27715                     scale = targetHeight / height;
27716                 }
27717                 
27718                 if(x > 0 && y > 0){
27719                     scale = targetWidth / width;
27720                     
27721                     if(width < height){
27722                         scale = targetHeight / height;
27723                     }
27724                 }
27725                 
27726                 context.scale(scale, scale);
27727                 
27728                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27729                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27730
27731                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27732                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27733
27734                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27735                 
27736                 break;
27737             case 90 : 
27738                 
27739                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27740                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27741                 
27742                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27743                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27744                 
27745                 var targetWidth = this.minWidth - 2 * x;
27746                 var targetHeight = this.minHeight - 2 * y;
27747                 
27748                 var scale = 1;
27749                 
27750                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27751                     scale = targetWidth / width;
27752                 }
27753                 
27754                 if(x > 0 && y == 0){
27755                     scale = targetHeight / height;
27756                 }
27757                 
27758                 if(x > 0 && y > 0){
27759                     scale = targetWidth / width;
27760                     
27761                     if(width < height){
27762                         scale = targetHeight / height;
27763                     }
27764                 }
27765                 
27766                 context.scale(scale, scale);
27767                 
27768                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27769                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27770
27771                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27772                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27773                 
27774                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27775                 
27776                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27777                 
27778                 break;
27779             case 180 :
27780                 
27781                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27782                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27783                 
27784                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27785                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27786                 
27787                 var targetWidth = this.minWidth - 2 * x;
27788                 var targetHeight = this.minHeight - 2 * y;
27789                 
27790                 var scale = 1;
27791                 
27792                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27793                     scale = targetWidth / width;
27794                 }
27795                 
27796                 if(x > 0 && y == 0){
27797                     scale = targetHeight / height;
27798                 }
27799                 
27800                 if(x > 0 && y > 0){
27801                     scale = targetWidth / width;
27802                     
27803                     if(width < height){
27804                         scale = targetHeight / height;
27805                     }
27806                 }
27807                 
27808                 context.scale(scale, scale);
27809                 
27810                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27811                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27812
27813                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27814                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27815
27816                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27817                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27818                 
27819                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27820                 
27821                 break;
27822             case 270 :
27823                 
27824                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27825                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27826                 
27827                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27828                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27829                 
27830                 var targetWidth = this.minWidth - 2 * x;
27831                 var targetHeight = this.minHeight - 2 * y;
27832                 
27833                 var scale = 1;
27834                 
27835                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27836                     scale = targetWidth / width;
27837                 }
27838                 
27839                 if(x > 0 && y == 0){
27840                     scale = targetHeight / height;
27841                 }
27842                 
27843                 if(x > 0 && y > 0){
27844                     scale = targetWidth / width;
27845                     
27846                     if(width < height){
27847                         scale = targetHeight / height;
27848                     }
27849                 }
27850                 
27851                 context.scale(scale, scale);
27852                 
27853                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27854                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27855
27856                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27857                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27858                 
27859                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27860                 
27861                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27862                 
27863                 break;
27864             default : 
27865                 break;
27866         }
27867         
27868         this.cropData = canvas.toDataURL(this.cropType);
27869         
27870         if(this.fireEvent('crop', this, this.cropData) !== false){
27871             this.process(this.file, this.cropData);
27872         }
27873         
27874         return;
27875         
27876     },
27877     
27878     setThumbBoxSize : function()
27879     {
27880         var width, height;
27881         
27882         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27883             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27884             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27885             
27886             this.minWidth = width;
27887             this.minHeight = height;
27888             
27889             if(this.rotate == 90 || this.rotate == 270){
27890                 this.minWidth = height;
27891                 this.minHeight = width;
27892             }
27893         }
27894         
27895         height = 300;
27896         width = Math.ceil(this.minWidth * height / this.minHeight);
27897         
27898         if(this.minWidth > this.minHeight){
27899             width = 300;
27900             height = Math.ceil(this.minHeight * width / this.minWidth);
27901         }
27902         
27903         this.thumbEl.setStyle({
27904             width : width + 'px',
27905             height : height + 'px'
27906         });
27907
27908         return;
27909             
27910     },
27911     
27912     setThumbBoxPosition : function()
27913     {
27914         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27915         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27916         
27917         this.thumbEl.setLeft(x);
27918         this.thumbEl.setTop(y);
27919         
27920     },
27921     
27922     baseRotateLevel : function()
27923     {
27924         this.baseRotate = 1;
27925         
27926         if(
27927                 typeof(this.exif) != 'undefined' &&
27928                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27929                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27930         ){
27931             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27932         }
27933         
27934         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27935         
27936     },
27937     
27938     baseScaleLevel : function()
27939     {
27940         var width, height;
27941         
27942         if(this.isDocument){
27943             
27944             if(this.baseRotate == 6 || this.baseRotate == 8){
27945             
27946                 height = this.thumbEl.getHeight();
27947                 this.baseScale = height / this.imageEl.OriginWidth;
27948
27949                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27950                     width = this.thumbEl.getWidth();
27951                     this.baseScale = width / this.imageEl.OriginHeight;
27952                 }
27953
27954                 return;
27955             }
27956
27957             height = this.thumbEl.getHeight();
27958             this.baseScale = height / this.imageEl.OriginHeight;
27959
27960             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27961                 width = this.thumbEl.getWidth();
27962                 this.baseScale = width / this.imageEl.OriginWidth;
27963             }
27964
27965             return;
27966         }
27967         
27968         if(this.baseRotate == 6 || this.baseRotate == 8){
27969             
27970             width = this.thumbEl.getHeight();
27971             this.baseScale = width / this.imageEl.OriginHeight;
27972             
27973             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27974                 height = this.thumbEl.getWidth();
27975                 this.baseScale = height / this.imageEl.OriginHeight;
27976             }
27977             
27978             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27979                 height = this.thumbEl.getWidth();
27980                 this.baseScale = height / this.imageEl.OriginHeight;
27981                 
27982                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27983                     width = this.thumbEl.getHeight();
27984                     this.baseScale = width / this.imageEl.OriginWidth;
27985                 }
27986             }
27987             
27988             return;
27989         }
27990         
27991         width = this.thumbEl.getWidth();
27992         this.baseScale = width / this.imageEl.OriginWidth;
27993         
27994         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27995             height = this.thumbEl.getHeight();
27996             this.baseScale = height / this.imageEl.OriginHeight;
27997         }
27998         
27999         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28000             
28001             height = this.thumbEl.getHeight();
28002             this.baseScale = height / this.imageEl.OriginHeight;
28003             
28004             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28005                 width = this.thumbEl.getWidth();
28006                 this.baseScale = width / this.imageEl.OriginWidth;
28007             }
28008             
28009         }
28010         
28011         return;
28012     },
28013     
28014     getScaleLevel : function()
28015     {
28016         return this.baseScale * Math.pow(1.1, this.scale);
28017     },
28018     
28019     onTouchStart : function(e)
28020     {
28021         if(!this.canvasLoaded){
28022             this.beforeSelectFile(e);
28023             return;
28024         }
28025         
28026         var touches = e.browserEvent.touches;
28027         
28028         if(!touches){
28029             return;
28030         }
28031         
28032         if(touches.length == 1){
28033             this.onMouseDown(e);
28034             return;
28035         }
28036         
28037         if(touches.length != 2){
28038             return;
28039         }
28040         
28041         var coords = [];
28042         
28043         for(var i = 0, finger; finger = touches[i]; i++){
28044             coords.push(finger.pageX, finger.pageY);
28045         }
28046         
28047         var x = Math.pow(coords[0] - coords[2], 2);
28048         var y = Math.pow(coords[1] - coords[3], 2);
28049         
28050         this.startDistance = Math.sqrt(x + y);
28051         
28052         this.startScale = this.scale;
28053         
28054         this.pinching = true;
28055         this.dragable = false;
28056         
28057     },
28058     
28059     onTouchMove : function(e)
28060     {
28061         if(!this.pinching && !this.dragable){
28062             return;
28063         }
28064         
28065         var touches = e.browserEvent.touches;
28066         
28067         if(!touches){
28068             return;
28069         }
28070         
28071         if(this.dragable){
28072             this.onMouseMove(e);
28073             return;
28074         }
28075         
28076         var coords = [];
28077         
28078         for(var i = 0, finger; finger = touches[i]; i++){
28079             coords.push(finger.pageX, finger.pageY);
28080         }
28081         
28082         var x = Math.pow(coords[0] - coords[2], 2);
28083         var y = Math.pow(coords[1] - coords[3], 2);
28084         
28085         this.endDistance = Math.sqrt(x + y);
28086         
28087         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28088         
28089         if(!this.zoomable()){
28090             this.scale = this.startScale;
28091             return;
28092         }
28093         
28094         this.draw();
28095         
28096     },
28097     
28098     onTouchEnd : function(e)
28099     {
28100         this.pinching = false;
28101         this.dragable = false;
28102         
28103     },
28104     
28105     process : function(file, crop)
28106     {
28107         if(this.loadMask){
28108             this.maskEl.mask(this.loadingText);
28109         }
28110         
28111         this.xhr = new XMLHttpRequest();
28112         
28113         file.xhr = this.xhr;
28114
28115         this.xhr.open(this.method, this.url, true);
28116         
28117         var headers = {
28118             "Accept": "application/json",
28119             "Cache-Control": "no-cache",
28120             "X-Requested-With": "XMLHttpRequest"
28121         };
28122         
28123         for (var headerName in headers) {
28124             var headerValue = headers[headerName];
28125             if (headerValue) {
28126                 this.xhr.setRequestHeader(headerName, headerValue);
28127             }
28128         }
28129         
28130         var _this = this;
28131         
28132         this.xhr.onload = function()
28133         {
28134             _this.xhrOnLoad(_this.xhr);
28135         }
28136         
28137         this.xhr.onerror = function()
28138         {
28139             _this.xhrOnError(_this.xhr);
28140         }
28141         
28142         var formData = new FormData();
28143
28144         formData.append('returnHTML', 'NO');
28145         
28146         if(crop){
28147             formData.append('crop', crop);
28148         }
28149         
28150         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28151             formData.append(this.paramName, file, file.name);
28152         }
28153         
28154         if(typeof(file.filename) != 'undefined'){
28155             formData.append('filename', file.filename);
28156         }
28157         
28158         if(typeof(file.mimetype) != 'undefined'){
28159             formData.append('mimetype', file.mimetype);
28160         }
28161         
28162         if(this.fireEvent('arrange', this, formData) != false){
28163             this.xhr.send(formData);
28164         };
28165     },
28166     
28167     xhrOnLoad : function(xhr)
28168     {
28169         if(this.loadMask){
28170             this.maskEl.unmask();
28171         }
28172         
28173         if (xhr.readyState !== 4) {
28174             this.fireEvent('exception', this, xhr);
28175             return;
28176         }
28177
28178         var response = Roo.decode(xhr.responseText);
28179         
28180         if(!response.success){
28181             this.fireEvent('exception', this, xhr);
28182             return;
28183         }
28184         
28185         var response = Roo.decode(xhr.responseText);
28186         
28187         this.fireEvent('upload', this, response);
28188         
28189     },
28190     
28191     xhrOnError : function()
28192     {
28193         if(this.loadMask){
28194             this.maskEl.unmask();
28195         }
28196         
28197         Roo.log('xhr on error');
28198         
28199         var response = Roo.decode(xhr.responseText);
28200           
28201         Roo.log(response);
28202         
28203     },
28204     
28205     prepare : function(file)
28206     {   
28207         if(this.loadMask){
28208             this.maskEl.mask(this.loadingText);
28209         }
28210         
28211         this.file = false;
28212         this.exif = {};
28213         
28214         if(typeof(file) === 'string'){
28215             this.loadCanvas(file);
28216             return;
28217         }
28218         
28219         if(!file || !this.urlAPI){
28220             return;
28221         }
28222         
28223         this.file = file;
28224         this.cropType = file.type;
28225         
28226         var _this = this;
28227         
28228         if(this.fireEvent('prepare', this, this.file) != false){
28229             
28230             var reader = new FileReader();
28231             
28232             reader.onload = function (e) {
28233                 if (e.target.error) {
28234                     Roo.log(e.target.error);
28235                     return;
28236                 }
28237                 
28238                 var buffer = e.target.result,
28239                     dataView = new DataView(buffer),
28240                     offset = 2,
28241                     maxOffset = dataView.byteLength - 4,
28242                     markerBytes,
28243                     markerLength;
28244                 
28245                 if (dataView.getUint16(0) === 0xffd8) {
28246                     while (offset < maxOffset) {
28247                         markerBytes = dataView.getUint16(offset);
28248                         
28249                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28250                             markerLength = dataView.getUint16(offset + 2) + 2;
28251                             if (offset + markerLength > dataView.byteLength) {
28252                                 Roo.log('Invalid meta data: Invalid segment size.');
28253                                 break;
28254                             }
28255                             
28256                             if(markerBytes == 0xffe1){
28257                                 _this.parseExifData(
28258                                     dataView,
28259                                     offset,
28260                                     markerLength
28261                                 );
28262                             }
28263                             
28264                             offset += markerLength;
28265                             
28266                             continue;
28267                         }
28268                         
28269                         break;
28270                     }
28271                     
28272                 }
28273                 
28274                 var url = _this.urlAPI.createObjectURL(_this.file);
28275                 
28276                 _this.loadCanvas(url);
28277                 
28278                 return;
28279             }
28280             
28281             reader.readAsArrayBuffer(this.file);
28282             
28283         }
28284         
28285     },
28286     
28287     parseExifData : function(dataView, offset, length)
28288     {
28289         var tiffOffset = offset + 10,
28290             littleEndian,
28291             dirOffset;
28292     
28293         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28294             // No Exif data, might be XMP data instead
28295             return;
28296         }
28297         
28298         // Check for the ASCII code for "Exif" (0x45786966):
28299         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28300             // No Exif data, might be XMP data instead
28301             return;
28302         }
28303         if (tiffOffset + 8 > dataView.byteLength) {
28304             Roo.log('Invalid Exif data: Invalid segment size.');
28305             return;
28306         }
28307         // Check for the two null bytes:
28308         if (dataView.getUint16(offset + 8) !== 0x0000) {
28309             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28310             return;
28311         }
28312         // Check the byte alignment:
28313         switch (dataView.getUint16(tiffOffset)) {
28314         case 0x4949:
28315             littleEndian = true;
28316             break;
28317         case 0x4D4D:
28318             littleEndian = false;
28319             break;
28320         default:
28321             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28322             return;
28323         }
28324         // Check for the TIFF tag marker (0x002A):
28325         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28326             Roo.log('Invalid Exif data: Missing TIFF marker.');
28327             return;
28328         }
28329         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28330         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28331         
28332         this.parseExifTags(
28333             dataView,
28334             tiffOffset,
28335             tiffOffset + dirOffset,
28336             littleEndian
28337         );
28338     },
28339     
28340     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28341     {
28342         var tagsNumber,
28343             dirEndOffset,
28344             i;
28345         if (dirOffset + 6 > dataView.byteLength) {
28346             Roo.log('Invalid Exif data: Invalid directory offset.');
28347             return;
28348         }
28349         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28350         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28351         if (dirEndOffset + 4 > dataView.byteLength) {
28352             Roo.log('Invalid Exif data: Invalid directory size.');
28353             return;
28354         }
28355         for (i = 0; i < tagsNumber; i += 1) {
28356             this.parseExifTag(
28357                 dataView,
28358                 tiffOffset,
28359                 dirOffset + 2 + 12 * i, // tag offset
28360                 littleEndian
28361             );
28362         }
28363         // Return the offset to the next directory:
28364         return dataView.getUint32(dirEndOffset, littleEndian);
28365     },
28366     
28367     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28368     {
28369         var tag = dataView.getUint16(offset, littleEndian);
28370         
28371         this.exif[tag] = this.getExifValue(
28372             dataView,
28373             tiffOffset,
28374             offset,
28375             dataView.getUint16(offset + 2, littleEndian), // tag type
28376             dataView.getUint32(offset + 4, littleEndian), // tag length
28377             littleEndian
28378         );
28379     },
28380     
28381     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28382     {
28383         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28384             tagSize,
28385             dataOffset,
28386             values,
28387             i,
28388             str,
28389             c;
28390     
28391         if (!tagType) {
28392             Roo.log('Invalid Exif data: Invalid tag type.');
28393             return;
28394         }
28395         
28396         tagSize = tagType.size * length;
28397         // Determine if the value is contained in the dataOffset bytes,
28398         // or if the value at the dataOffset is a pointer to the actual data:
28399         dataOffset = tagSize > 4 ?
28400                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28401         if (dataOffset + tagSize > dataView.byteLength) {
28402             Roo.log('Invalid Exif data: Invalid data offset.');
28403             return;
28404         }
28405         if (length === 1) {
28406             return tagType.getValue(dataView, dataOffset, littleEndian);
28407         }
28408         values = [];
28409         for (i = 0; i < length; i += 1) {
28410             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28411         }
28412         
28413         if (tagType.ascii) {
28414             str = '';
28415             // Concatenate the chars:
28416             for (i = 0; i < values.length; i += 1) {
28417                 c = values[i];
28418                 // Ignore the terminating NULL byte(s):
28419                 if (c === '\u0000') {
28420                     break;
28421                 }
28422                 str += c;
28423             }
28424             return str;
28425         }
28426         return values;
28427     }
28428     
28429 });
28430
28431 Roo.apply(Roo.bootstrap.UploadCropbox, {
28432     tags : {
28433         'Orientation': 0x0112
28434     },
28435     
28436     Orientation: {
28437             1: 0, //'top-left',
28438 //            2: 'top-right',
28439             3: 180, //'bottom-right',
28440 //            4: 'bottom-left',
28441 //            5: 'left-top',
28442             6: 90, //'right-top',
28443 //            7: 'right-bottom',
28444             8: 270 //'left-bottom'
28445     },
28446     
28447     exifTagTypes : {
28448         // byte, 8-bit unsigned int:
28449         1: {
28450             getValue: function (dataView, dataOffset) {
28451                 return dataView.getUint8(dataOffset);
28452             },
28453             size: 1
28454         },
28455         // ascii, 8-bit byte:
28456         2: {
28457             getValue: function (dataView, dataOffset) {
28458                 return String.fromCharCode(dataView.getUint8(dataOffset));
28459             },
28460             size: 1,
28461             ascii: true
28462         },
28463         // short, 16 bit int:
28464         3: {
28465             getValue: function (dataView, dataOffset, littleEndian) {
28466                 return dataView.getUint16(dataOffset, littleEndian);
28467             },
28468             size: 2
28469         },
28470         // long, 32 bit int:
28471         4: {
28472             getValue: function (dataView, dataOffset, littleEndian) {
28473                 return dataView.getUint32(dataOffset, littleEndian);
28474             },
28475             size: 4
28476         },
28477         // rational = two long values, first is numerator, second is denominator:
28478         5: {
28479             getValue: function (dataView, dataOffset, littleEndian) {
28480                 return dataView.getUint32(dataOffset, littleEndian) /
28481                     dataView.getUint32(dataOffset + 4, littleEndian);
28482             },
28483             size: 8
28484         },
28485         // slong, 32 bit signed int:
28486         9: {
28487             getValue: function (dataView, dataOffset, littleEndian) {
28488                 return dataView.getInt32(dataOffset, littleEndian);
28489             },
28490             size: 4
28491         },
28492         // srational, two slongs, first is numerator, second is denominator:
28493         10: {
28494             getValue: function (dataView, dataOffset, littleEndian) {
28495                 return dataView.getInt32(dataOffset, littleEndian) /
28496                     dataView.getInt32(dataOffset + 4, littleEndian);
28497             },
28498             size: 8
28499         }
28500     },
28501     
28502     footer : {
28503         STANDARD : [
28504             {
28505                 tag : 'div',
28506                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28507                 action : 'rotate-left',
28508                 cn : [
28509                     {
28510                         tag : 'button',
28511                         cls : 'btn btn-default',
28512                         html : '<i class="fa fa-undo"></i>'
28513                     }
28514                 ]
28515             },
28516             {
28517                 tag : 'div',
28518                 cls : 'btn-group roo-upload-cropbox-picture',
28519                 action : 'picture',
28520                 cn : [
28521                     {
28522                         tag : 'button',
28523                         cls : 'btn btn-default',
28524                         html : '<i class="fa fa-picture-o"></i>'
28525                     }
28526                 ]
28527             },
28528             {
28529                 tag : 'div',
28530                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28531                 action : 'rotate-right',
28532                 cn : [
28533                     {
28534                         tag : 'button',
28535                         cls : 'btn btn-default',
28536                         html : '<i class="fa fa-repeat"></i>'
28537                     }
28538                 ]
28539             }
28540         ],
28541         DOCUMENT : [
28542             {
28543                 tag : 'div',
28544                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28545                 action : 'rotate-left',
28546                 cn : [
28547                     {
28548                         tag : 'button',
28549                         cls : 'btn btn-default',
28550                         html : '<i class="fa fa-undo"></i>'
28551                     }
28552                 ]
28553             },
28554             {
28555                 tag : 'div',
28556                 cls : 'btn-group roo-upload-cropbox-download',
28557                 action : 'download',
28558                 cn : [
28559                     {
28560                         tag : 'button',
28561                         cls : 'btn btn-default',
28562                         html : '<i class="fa fa-download"></i>'
28563                     }
28564                 ]
28565             },
28566             {
28567                 tag : 'div',
28568                 cls : 'btn-group roo-upload-cropbox-crop',
28569                 action : 'crop',
28570                 cn : [
28571                     {
28572                         tag : 'button',
28573                         cls : 'btn btn-default',
28574                         html : '<i class="fa fa-crop"></i>'
28575                     }
28576                 ]
28577             },
28578             {
28579                 tag : 'div',
28580                 cls : 'btn-group roo-upload-cropbox-trash',
28581                 action : 'trash',
28582                 cn : [
28583                     {
28584                         tag : 'button',
28585                         cls : 'btn btn-default',
28586                         html : '<i class="fa fa-trash"></i>'
28587                     }
28588                 ]
28589             },
28590             {
28591                 tag : 'div',
28592                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28593                 action : 'rotate-right',
28594                 cn : [
28595                     {
28596                         tag : 'button',
28597                         cls : 'btn btn-default',
28598                         html : '<i class="fa fa-repeat"></i>'
28599                     }
28600                 ]
28601             }
28602         ],
28603         ROTATOR : [
28604             {
28605                 tag : 'div',
28606                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28607                 action : 'rotate-left',
28608                 cn : [
28609                     {
28610                         tag : 'button',
28611                         cls : 'btn btn-default',
28612                         html : '<i class="fa fa-undo"></i>'
28613                     }
28614                 ]
28615             },
28616             {
28617                 tag : 'div',
28618                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28619                 action : 'rotate-right',
28620                 cn : [
28621                     {
28622                         tag : 'button',
28623                         cls : 'btn btn-default',
28624                         html : '<i class="fa fa-repeat"></i>'
28625                     }
28626                 ]
28627             }
28628         ]
28629     }
28630 });
28631
28632 /*
28633 * Licence: LGPL
28634 */
28635
28636 /**
28637  * @class Roo.bootstrap.DocumentManager
28638  * @extends Roo.bootstrap.Component
28639  * Bootstrap DocumentManager class
28640  * @cfg {String} paramName default 'imageUpload'
28641  * @cfg {String} toolTipName default 'filename'
28642  * @cfg {String} method default POST
28643  * @cfg {String} url action url
28644  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28645  * @cfg {Boolean} multiple multiple upload default true
28646  * @cfg {Number} thumbSize default 300
28647  * @cfg {String} fieldLabel
28648  * @cfg {Number} labelWidth default 4
28649  * @cfg {String} labelAlign (left|top) default left
28650  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28651 * @cfg {Number} labellg set the width of label (1-12)
28652  * @cfg {Number} labelmd set the width of label (1-12)
28653  * @cfg {Number} labelsm set the width of label (1-12)
28654  * @cfg {Number} labelxs set the width of label (1-12)
28655  * 
28656  * @constructor
28657  * Create a new DocumentManager
28658  * @param {Object} config The config object
28659  */
28660
28661 Roo.bootstrap.DocumentManager = function(config){
28662     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28663     
28664     this.files = [];
28665     this.delegates = [];
28666     
28667     this.addEvents({
28668         /**
28669          * @event initial
28670          * Fire when initial the DocumentManager
28671          * @param {Roo.bootstrap.DocumentManager} this
28672          */
28673         "initial" : true,
28674         /**
28675          * @event inspect
28676          * inspect selected file
28677          * @param {Roo.bootstrap.DocumentManager} this
28678          * @param {File} file
28679          */
28680         "inspect" : true,
28681         /**
28682          * @event exception
28683          * Fire when xhr load exception
28684          * @param {Roo.bootstrap.DocumentManager} this
28685          * @param {XMLHttpRequest} xhr
28686          */
28687         "exception" : true,
28688         /**
28689          * @event afterupload
28690          * Fire when xhr load exception
28691          * @param {Roo.bootstrap.DocumentManager} this
28692          * @param {XMLHttpRequest} xhr
28693          */
28694         "afterupload" : true,
28695         /**
28696          * @event prepare
28697          * prepare the form data
28698          * @param {Roo.bootstrap.DocumentManager} this
28699          * @param {Object} formData
28700          */
28701         "prepare" : true,
28702         /**
28703          * @event remove
28704          * Fire when remove the file
28705          * @param {Roo.bootstrap.DocumentManager} this
28706          * @param {Object} file
28707          */
28708         "remove" : true,
28709         /**
28710          * @event refresh
28711          * Fire after refresh the file
28712          * @param {Roo.bootstrap.DocumentManager} this
28713          */
28714         "refresh" : true,
28715         /**
28716          * @event click
28717          * Fire after click the image
28718          * @param {Roo.bootstrap.DocumentManager} this
28719          * @param {Object} file
28720          */
28721         "click" : true,
28722         /**
28723          * @event edit
28724          * Fire when upload a image and editable set to true
28725          * @param {Roo.bootstrap.DocumentManager} this
28726          * @param {Object} file
28727          */
28728         "edit" : true,
28729         /**
28730          * @event beforeselectfile
28731          * Fire before select file
28732          * @param {Roo.bootstrap.DocumentManager} this
28733          */
28734         "beforeselectfile" : true,
28735         /**
28736          * @event process
28737          * Fire before process file
28738          * @param {Roo.bootstrap.DocumentManager} this
28739          * @param {Object} file
28740          */
28741         "process" : true,
28742         /**
28743          * @event previewrendered
28744          * Fire when preview rendered
28745          * @param {Roo.bootstrap.DocumentManager} this
28746          * @param {Object} file
28747          */
28748         "previewrendered" : true,
28749         /**
28750          */
28751         "previewResize" : true
28752         
28753     });
28754 };
28755
28756 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28757     
28758     boxes : 0,
28759     inputName : '',
28760     thumbSize : 300,
28761     multiple : true,
28762     files : false,
28763     method : 'POST',
28764     url : '',
28765     paramName : 'imageUpload',
28766     toolTipName : 'filename',
28767     fieldLabel : '',
28768     labelWidth : 4,
28769     labelAlign : 'left',
28770     editable : true,
28771     delegates : false,
28772     xhr : false, 
28773     
28774     labellg : 0,
28775     labelmd : 0,
28776     labelsm : 0,
28777     labelxs : 0,
28778     
28779     getAutoCreate : function()
28780     {   
28781         var managerWidget = {
28782             tag : 'div',
28783             cls : 'roo-document-manager',
28784             cn : [
28785                 {
28786                     tag : 'input',
28787                     cls : 'roo-document-manager-selector',
28788                     type : 'file'
28789                 },
28790                 {
28791                     tag : 'div',
28792                     cls : 'roo-document-manager-uploader',
28793                     cn : [
28794                         {
28795                             tag : 'div',
28796                             cls : 'roo-document-manager-upload-btn',
28797                             html : '<i class="fa fa-plus"></i>'
28798                         }
28799                     ]
28800                     
28801                 }
28802             ]
28803         };
28804         
28805         var content = [
28806             {
28807                 tag : 'div',
28808                 cls : 'column col-md-12',
28809                 cn : managerWidget
28810             }
28811         ];
28812         
28813         if(this.fieldLabel.length){
28814             
28815             content = [
28816                 {
28817                     tag : 'div',
28818                     cls : 'column col-md-12',
28819                     html : this.fieldLabel
28820                 },
28821                 {
28822                     tag : 'div',
28823                     cls : 'column col-md-12',
28824                     cn : managerWidget
28825                 }
28826             ];
28827
28828             if(this.labelAlign == 'left'){
28829                 content = [
28830                     {
28831                         tag : 'div',
28832                         cls : 'column',
28833                         html : this.fieldLabel
28834                     },
28835                     {
28836                         tag : 'div',
28837                         cls : 'column',
28838                         cn : managerWidget
28839                     }
28840                 ];
28841                 
28842                 if(this.labelWidth > 12){
28843                     content[0].style = "width: " + this.labelWidth + 'px';
28844                 }
28845
28846                 if(this.labelWidth < 13 && this.labelmd == 0){
28847                     this.labelmd = this.labelWidth;
28848                 }
28849
28850                 if(this.labellg > 0){
28851                     content[0].cls += ' col-lg-' + this.labellg;
28852                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28853                 }
28854
28855                 if(this.labelmd > 0){
28856                     content[0].cls += ' col-md-' + this.labelmd;
28857                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28858                 }
28859
28860                 if(this.labelsm > 0){
28861                     content[0].cls += ' col-sm-' + this.labelsm;
28862                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28863                 }
28864
28865                 if(this.labelxs > 0){
28866                     content[0].cls += ' col-xs-' + this.labelxs;
28867                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28868                 }
28869                 
28870             }
28871         }
28872         
28873         var cfg = {
28874             tag : 'div',
28875             cls : 'row clearfix',
28876             cn : content
28877         };
28878         
28879         return cfg;
28880         
28881     },
28882     
28883     initEvents : function()
28884     {
28885         this.managerEl = this.el.select('.roo-document-manager', true).first();
28886         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28887         
28888         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28889         this.selectorEl.hide();
28890         
28891         if(this.multiple){
28892             this.selectorEl.attr('multiple', 'multiple');
28893         }
28894         
28895         this.selectorEl.on('change', this.onFileSelected, this);
28896         
28897         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28898         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28899         
28900         this.uploader.on('click', this.onUploaderClick, this);
28901         
28902         this.renderProgressDialog();
28903         
28904         var _this = this;
28905         
28906         window.addEventListener("resize", function() { _this.refresh(); } );
28907         
28908         this.fireEvent('initial', this);
28909     },
28910     
28911     renderProgressDialog : function()
28912     {
28913         var _this = this;
28914         
28915         this.progressDialog = new Roo.bootstrap.Modal({
28916             cls : 'roo-document-manager-progress-dialog',
28917             allow_close : false,
28918             title : '',
28919             buttons : [
28920                 {
28921                     name  :'cancel',
28922                     weight : 'danger',
28923                     html : 'Cancel'
28924                 }
28925             ], 
28926             listeners : { 
28927                 btnclick : function() {
28928                     _this.uploadCancel();
28929                     this.hide();
28930                 }
28931             }
28932         });
28933          
28934         this.progressDialog.render(Roo.get(document.body));
28935          
28936         this.progress = new Roo.bootstrap.Progress({
28937             cls : 'roo-document-manager-progress',
28938             active : true,
28939             striped : true
28940         });
28941         
28942         this.progress.render(this.progressDialog.getChildContainer());
28943         
28944         this.progressBar = new Roo.bootstrap.ProgressBar({
28945             cls : 'roo-document-manager-progress-bar',
28946             aria_valuenow : 0,
28947             aria_valuemin : 0,
28948             aria_valuemax : 12,
28949             panel : 'success'
28950         });
28951         
28952         this.progressBar.render(this.progress.getChildContainer());
28953     },
28954     
28955     onUploaderClick : function(e)
28956     {
28957         e.preventDefault();
28958      
28959         if(this.fireEvent('beforeselectfile', this) != false){
28960             this.selectorEl.dom.click();
28961         }
28962         
28963     },
28964     
28965     onFileSelected : function(e)
28966     {
28967         e.preventDefault();
28968         
28969         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28970             return;
28971         }
28972         
28973         Roo.each(this.selectorEl.dom.files, function(file){
28974             if(this.fireEvent('inspect', this, file) != false){
28975                 this.files.push(file);
28976             }
28977         }, this);
28978         
28979         this.queue();
28980         
28981     },
28982     
28983     queue : function()
28984     {
28985         this.selectorEl.dom.value = '';
28986         
28987         if(!this.files || !this.files.length){
28988             return;
28989         }
28990         
28991         if(this.boxes > 0 && this.files.length > this.boxes){
28992             this.files = this.files.slice(0, this.boxes);
28993         }
28994         
28995         this.uploader.show();
28996         
28997         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28998             this.uploader.hide();
28999         }
29000         
29001         var _this = this;
29002         
29003         var files = [];
29004         
29005         var docs = [];
29006         
29007         Roo.each(this.files, function(file){
29008             
29009             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29010                 var f = this.renderPreview(file);
29011                 files.push(f);
29012                 return;
29013             }
29014             
29015             if(file.type.indexOf('image') != -1){
29016                 this.delegates.push(
29017                     (function(){
29018                         _this.process(file);
29019                     }).createDelegate(this)
29020                 );
29021         
29022                 return;
29023             }
29024             
29025             docs.push(
29026                 (function(){
29027                     _this.process(file);
29028                 }).createDelegate(this)
29029             );
29030             
29031         }, this);
29032         
29033         this.files = files;
29034         
29035         this.delegates = this.delegates.concat(docs);
29036         
29037         if(!this.delegates.length){
29038             this.refresh();
29039             return;
29040         }
29041         
29042         this.progressBar.aria_valuemax = this.delegates.length;
29043         
29044         this.arrange();
29045         
29046         return;
29047     },
29048     
29049     arrange : function()
29050     {
29051         if(!this.delegates.length){
29052             this.progressDialog.hide();
29053             this.refresh();
29054             return;
29055         }
29056         
29057         var delegate = this.delegates.shift();
29058         
29059         this.progressDialog.show();
29060         
29061         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29062         
29063         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29064         
29065         delegate();
29066     },
29067     
29068     refresh : function()
29069     {
29070         this.uploader.show();
29071         
29072         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29073             this.uploader.hide();
29074         }
29075         
29076         Roo.isTouch ? this.closable(false) : this.closable(true);
29077         
29078         this.fireEvent('refresh', this);
29079     },
29080     
29081     onRemove : function(e, el, o)
29082     {
29083         e.preventDefault();
29084         
29085         this.fireEvent('remove', this, o);
29086         
29087     },
29088     
29089     remove : function(o)
29090     {
29091         var files = [];
29092         
29093         Roo.each(this.files, function(file){
29094             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29095                 files.push(file);
29096                 return;
29097             }
29098
29099             o.target.remove();
29100
29101         }, this);
29102         
29103         this.files = files;
29104         
29105         this.refresh();
29106     },
29107     
29108     clear : function()
29109     {
29110         Roo.each(this.files, function(file){
29111             if(!file.target){
29112                 return;
29113             }
29114             
29115             file.target.remove();
29116
29117         }, this);
29118         
29119         this.files = [];
29120         
29121         this.refresh();
29122     },
29123     
29124     onClick : function(e, el, o)
29125     {
29126         e.preventDefault();
29127         
29128         this.fireEvent('click', this, o);
29129         
29130     },
29131     
29132     closable : function(closable)
29133     {
29134         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29135             
29136             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29137             
29138             if(closable){
29139                 el.show();
29140                 return;
29141             }
29142             
29143             el.hide();
29144             
29145         }, this);
29146     },
29147     
29148     xhrOnLoad : function(xhr)
29149     {
29150         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29151             el.remove();
29152         }, this);
29153         
29154         if (xhr.readyState !== 4) {
29155             this.arrange();
29156             this.fireEvent('exception', this, xhr);
29157             return;
29158         }
29159
29160         var response = Roo.decode(xhr.responseText);
29161         
29162         if(!response.success){
29163             this.arrange();
29164             this.fireEvent('exception', this, xhr);
29165             return;
29166         }
29167         
29168         var file = this.renderPreview(response.data);
29169         
29170         this.files.push(file);
29171         
29172         this.arrange();
29173         
29174         this.fireEvent('afterupload', this, xhr);
29175         
29176     },
29177     
29178     xhrOnError : function(xhr)
29179     {
29180         Roo.log('xhr on error');
29181         
29182         var response = Roo.decode(xhr.responseText);
29183           
29184         Roo.log(response);
29185         
29186         this.arrange();
29187     },
29188     
29189     process : function(file)
29190     {
29191         if(this.fireEvent('process', this, file) !== false){
29192             if(this.editable && file.type.indexOf('image') != -1){
29193                 this.fireEvent('edit', this, file);
29194                 return;
29195             }
29196
29197             this.uploadStart(file, false);
29198
29199             return;
29200         }
29201         
29202     },
29203     
29204     uploadStart : function(file, crop)
29205     {
29206         this.xhr = new XMLHttpRequest();
29207         
29208         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29209             this.arrange();
29210             return;
29211         }
29212         
29213         file.xhr = this.xhr;
29214             
29215         this.managerEl.createChild({
29216             tag : 'div',
29217             cls : 'roo-document-manager-loading',
29218             cn : [
29219                 {
29220                     tag : 'div',
29221                     tooltip : file.name,
29222                     cls : 'roo-document-manager-thumb',
29223                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29224                 }
29225             ]
29226
29227         });
29228
29229         this.xhr.open(this.method, this.url, true);
29230         
29231         var headers = {
29232             "Accept": "application/json",
29233             "Cache-Control": "no-cache",
29234             "X-Requested-With": "XMLHttpRequest"
29235         };
29236         
29237         for (var headerName in headers) {
29238             var headerValue = headers[headerName];
29239             if (headerValue) {
29240                 this.xhr.setRequestHeader(headerName, headerValue);
29241             }
29242         }
29243         
29244         var _this = this;
29245         
29246         this.xhr.onload = function()
29247         {
29248             _this.xhrOnLoad(_this.xhr);
29249         }
29250         
29251         this.xhr.onerror = function()
29252         {
29253             _this.xhrOnError(_this.xhr);
29254         }
29255         
29256         var formData = new FormData();
29257
29258         formData.append('returnHTML', 'NO');
29259         
29260         if(crop){
29261             formData.append('crop', crop);
29262         }
29263         
29264         formData.append(this.paramName, file, file.name);
29265         
29266         var options = {
29267             file : file, 
29268             manually : false
29269         };
29270         
29271         if(this.fireEvent('prepare', this, formData, options) != false){
29272             
29273             if(options.manually){
29274                 return;
29275             }
29276             
29277             this.xhr.send(formData);
29278             return;
29279         };
29280         
29281         this.uploadCancel();
29282     },
29283     
29284     uploadCancel : function()
29285     {
29286         if (this.xhr) {
29287             this.xhr.abort();
29288         }
29289         
29290         this.delegates = [];
29291         
29292         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29293             el.remove();
29294         }, this);
29295         
29296         this.arrange();
29297     },
29298     
29299     renderPreview : function(file)
29300     {
29301         if(typeof(file.target) != 'undefined' && file.target){
29302             return file;
29303         }
29304         
29305         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29306         
29307         var previewEl = this.managerEl.createChild({
29308             tag : 'div',
29309             cls : 'roo-document-manager-preview',
29310             cn : [
29311                 {
29312                     tag : 'div',
29313                     tooltip : file[this.toolTipName],
29314                     cls : 'roo-document-manager-thumb',
29315                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29316                 },
29317                 {
29318                     tag : 'button',
29319                     cls : 'close',
29320                     html : '<i class="fa fa-times-circle"></i>'
29321                 }
29322             ]
29323         });
29324
29325         var close = previewEl.select('button.close', true).first();
29326
29327         close.on('click', this.onRemove, this, file);
29328
29329         file.target = previewEl;
29330
29331         var image = previewEl.select('img', true).first();
29332         
29333         var _this = this;
29334         
29335         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29336         
29337         image.on('click', this.onClick, this, file);
29338         
29339         this.fireEvent('previewrendered', this, file);
29340         
29341         return file;
29342         
29343     },
29344     
29345     onPreviewLoad : function(file, image)
29346     {
29347         if(typeof(file.target) == 'undefined' || !file.target){
29348             return;
29349         }
29350         
29351         var width = image.dom.naturalWidth || image.dom.width;
29352         var height = image.dom.naturalHeight || image.dom.height;
29353         
29354         if(!this.previewResize) {
29355             return;
29356         }
29357         
29358         if(width > height){
29359             file.target.addClass('wide');
29360             return;
29361         }
29362         
29363         file.target.addClass('tall');
29364         return;
29365         
29366     },
29367     
29368     uploadFromSource : function(file, crop)
29369     {
29370         this.xhr = new XMLHttpRequest();
29371         
29372         this.managerEl.createChild({
29373             tag : 'div',
29374             cls : 'roo-document-manager-loading',
29375             cn : [
29376                 {
29377                     tag : 'div',
29378                     tooltip : file.name,
29379                     cls : 'roo-document-manager-thumb',
29380                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29381                 }
29382             ]
29383
29384         });
29385
29386         this.xhr.open(this.method, this.url, true);
29387         
29388         var headers = {
29389             "Accept": "application/json",
29390             "Cache-Control": "no-cache",
29391             "X-Requested-With": "XMLHttpRequest"
29392         };
29393         
29394         for (var headerName in headers) {
29395             var headerValue = headers[headerName];
29396             if (headerValue) {
29397                 this.xhr.setRequestHeader(headerName, headerValue);
29398             }
29399         }
29400         
29401         var _this = this;
29402         
29403         this.xhr.onload = function()
29404         {
29405             _this.xhrOnLoad(_this.xhr);
29406         }
29407         
29408         this.xhr.onerror = function()
29409         {
29410             _this.xhrOnError(_this.xhr);
29411         }
29412         
29413         var formData = new FormData();
29414
29415         formData.append('returnHTML', 'NO');
29416         
29417         formData.append('crop', crop);
29418         
29419         if(typeof(file.filename) != 'undefined'){
29420             formData.append('filename', file.filename);
29421         }
29422         
29423         if(typeof(file.mimetype) != 'undefined'){
29424             formData.append('mimetype', file.mimetype);
29425         }
29426         
29427         Roo.log(formData);
29428         
29429         if(this.fireEvent('prepare', this, formData) != false){
29430             this.xhr.send(formData);
29431         };
29432     }
29433 });
29434
29435 /*
29436 * Licence: LGPL
29437 */
29438
29439 /**
29440  * @class Roo.bootstrap.DocumentViewer
29441  * @extends Roo.bootstrap.Component
29442  * Bootstrap DocumentViewer class
29443  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29444  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29445  * 
29446  * @constructor
29447  * Create a new DocumentViewer
29448  * @param {Object} config The config object
29449  */
29450
29451 Roo.bootstrap.DocumentViewer = function(config){
29452     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29453     
29454     this.addEvents({
29455         /**
29456          * @event initial
29457          * Fire after initEvent
29458          * @param {Roo.bootstrap.DocumentViewer} this
29459          */
29460         "initial" : true,
29461         /**
29462          * @event click
29463          * Fire after click
29464          * @param {Roo.bootstrap.DocumentViewer} this
29465          */
29466         "click" : true,
29467         /**
29468          * @event download
29469          * Fire after download button
29470          * @param {Roo.bootstrap.DocumentViewer} this
29471          */
29472         "download" : true,
29473         /**
29474          * @event trash
29475          * Fire after trash button
29476          * @param {Roo.bootstrap.DocumentViewer} this
29477          */
29478         "trash" : true
29479         
29480     });
29481 };
29482
29483 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29484     
29485     showDownload : true,
29486     
29487     showTrash : true,
29488     
29489     getAutoCreate : function()
29490     {
29491         var cfg = {
29492             tag : 'div',
29493             cls : 'roo-document-viewer',
29494             cn : [
29495                 {
29496                     tag : 'div',
29497                     cls : 'roo-document-viewer-body',
29498                     cn : [
29499                         {
29500                             tag : 'div',
29501                             cls : 'roo-document-viewer-thumb',
29502                             cn : [
29503                                 {
29504                                     tag : 'img',
29505                                     cls : 'roo-document-viewer-image'
29506                                 }
29507                             ]
29508                         }
29509                     ]
29510                 },
29511                 {
29512                     tag : 'div',
29513                     cls : 'roo-document-viewer-footer',
29514                     cn : {
29515                         tag : 'div',
29516                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29517                         cn : [
29518                             {
29519                                 tag : 'div',
29520                                 cls : 'btn-group roo-document-viewer-download',
29521                                 cn : [
29522                                     {
29523                                         tag : 'button',
29524                                         cls : 'btn btn-default',
29525                                         html : '<i class="fa fa-download"></i>'
29526                                     }
29527                                 ]
29528                             },
29529                             {
29530                                 tag : 'div',
29531                                 cls : 'btn-group roo-document-viewer-trash',
29532                                 cn : [
29533                                     {
29534                                         tag : 'button',
29535                                         cls : 'btn btn-default',
29536                                         html : '<i class="fa fa-trash"></i>'
29537                                     }
29538                                 ]
29539                             }
29540                         ]
29541                     }
29542                 }
29543             ]
29544         };
29545         
29546         return cfg;
29547     },
29548     
29549     initEvents : function()
29550     {
29551         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29552         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29553         
29554         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29555         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29556         
29557         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29558         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29559         
29560         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29561         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29562         
29563         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29564         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29565         
29566         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29567         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29568         
29569         this.bodyEl.on('click', this.onClick, this);
29570         this.downloadBtn.on('click', this.onDownload, this);
29571         this.trashBtn.on('click', this.onTrash, this);
29572         
29573         this.downloadBtn.hide();
29574         this.trashBtn.hide();
29575         
29576         if(this.showDownload){
29577             this.downloadBtn.show();
29578         }
29579         
29580         if(this.showTrash){
29581             this.trashBtn.show();
29582         }
29583         
29584         if(!this.showDownload && !this.showTrash) {
29585             this.footerEl.hide();
29586         }
29587         
29588     },
29589     
29590     initial : function()
29591     {
29592         this.fireEvent('initial', this);
29593         
29594     },
29595     
29596     onClick : function(e)
29597     {
29598         e.preventDefault();
29599         
29600         this.fireEvent('click', this);
29601     },
29602     
29603     onDownload : function(e)
29604     {
29605         e.preventDefault();
29606         
29607         this.fireEvent('download', this);
29608     },
29609     
29610     onTrash : function(e)
29611     {
29612         e.preventDefault();
29613         
29614         this.fireEvent('trash', this);
29615     }
29616     
29617 });
29618 /*
29619  * - LGPL
29620  *
29621  * nav progress bar
29622  * 
29623  */
29624
29625 /**
29626  * @class Roo.bootstrap.NavProgressBar
29627  * @extends Roo.bootstrap.Component
29628  * Bootstrap NavProgressBar class
29629  * 
29630  * @constructor
29631  * Create a new nav progress bar
29632  * @param {Object} config The config object
29633  */
29634
29635 Roo.bootstrap.NavProgressBar = function(config){
29636     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29637
29638     this.bullets = this.bullets || [];
29639    
29640 //    Roo.bootstrap.NavProgressBar.register(this);
29641      this.addEvents({
29642         /**
29643              * @event changed
29644              * Fires when the active item changes
29645              * @param {Roo.bootstrap.NavProgressBar} this
29646              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29647              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29648          */
29649         'changed': true
29650      });
29651     
29652 };
29653
29654 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29655     
29656     bullets : [],
29657     barItems : [],
29658     
29659     getAutoCreate : function()
29660     {
29661         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29662         
29663         cfg = {
29664             tag : 'div',
29665             cls : 'roo-navigation-bar-group',
29666             cn : [
29667                 {
29668                     tag : 'div',
29669                     cls : 'roo-navigation-top-bar'
29670                 },
29671                 {
29672                     tag : 'div',
29673                     cls : 'roo-navigation-bullets-bar',
29674                     cn : [
29675                         {
29676                             tag : 'ul',
29677                             cls : 'roo-navigation-bar'
29678                         }
29679                     ]
29680                 },
29681                 
29682                 {
29683                     tag : 'div',
29684                     cls : 'roo-navigation-bottom-bar'
29685                 }
29686             ]
29687             
29688         };
29689         
29690         return cfg;
29691         
29692     },
29693     
29694     initEvents: function() 
29695     {
29696         
29697     },
29698     
29699     onRender : function(ct, position) 
29700     {
29701         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29702         
29703         if(this.bullets.length){
29704             Roo.each(this.bullets, function(b){
29705                this.addItem(b);
29706             }, this);
29707         }
29708         
29709         this.format();
29710         
29711     },
29712     
29713     addItem : function(cfg)
29714     {
29715         var item = new Roo.bootstrap.NavProgressItem(cfg);
29716         
29717         item.parentId = this.id;
29718         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29719         
29720         if(cfg.html){
29721             var top = new Roo.bootstrap.Element({
29722                 tag : 'div',
29723                 cls : 'roo-navigation-bar-text'
29724             });
29725             
29726             var bottom = new Roo.bootstrap.Element({
29727                 tag : 'div',
29728                 cls : 'roo-navigation-bar-text'
29729             });
29730             
29731             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29732             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29733             
29734             var topText = new Roo.bootstrap.Element({
29735                 tag : 'span',
29736                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29737             });
29738             
29739             var bottomText = new Roo.bootstrap.Element({
29740                 tag : 'span',
29741                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29742             });
29743             
29744             topText.onRender(top.el, null);
29745             bottomText.onRender(bottom.el, null);
29746             
29747             item.topEl = top;
29748             item.bottomEl = bottom;
29749         }
29750         
29751         this.barItems.push(item);
29752         
29753         return item;
29754     },
29755     
29756     getActive : function()
29757     {
29758         var active = false;
29759         
29760         Roo.each(this.barItems, function(v){
29761             
29762             if (!v.isActive()) {
29763                 return;
29764             }
29765             
29766             active = v;
29767             return false;
29768             
29769         });
29770         
29771         return active;
29772     },
29773     
29774     setActiveItem : function(item)
29775     {
29776         var prev = false;
29777         
29778         Roo.each(this.barItems, function(v){
29779             if (v.rid == item.rid) {
29780                 return ;
29781             }
29782             
29783             if (v.isActive()) {
29784                 v.setActive(false);
29785                 prev = v;
29786             }
29787         });
29788
29789         item.setActive(true);
29790         
29791         this.fireEvent('changed', this, item, prev);
29792     },
29793     
29794     getBarItem: function(rid)
29795     {
29796         var ret = false;
29797         
29798         Roo.each(this.barItems, function(e) {
29799             if (e.rid != rid) {
29800                 return;
29801             }
29802             
29803             ret =  e;
29804             return false;
29805         });
29806         
29807         return ret;
29808     },
29809     
29810     indexOfItem : function(item)
29811     {
29812         var index = false;
29813         
29814         Roo.each(this.barItems, function(v, i){
29815             
29816             if (v.rid != item.rid) {
29817                 return;
29818             }
29819             
29820             index = i;
29821             return false
29822         });
29823         
29824         return index;
29825     },
29826     
29827     setActiveNext : function()
29828     {
29829         var i = this.indexOfItem(this.getActive());
29830         
29831         if (i > this.barItems.length) {
29832             return;
29833         }
29834         
29835         this.setActiveItem(this.barItems[i+1]);
29836     },
29837     
29838     setActivePrev : function()
29839     {
29840         var i = this.indexOfItem(this.getActive());
29841         
29842         if (i  < 1) {
29843             return;
29844         }
29845         
29846         this.setActiveItem(this.barItems[i-1]);
29847     },
29848     
29849     format : function()
29850     {
29851         if(!this.barItems.length){
29852             return;
29853         }
29854      
29855         var width = 100 / this.barItems.length;
29856         
29857         Roo.each(this.barItems, function(i){
29858             i.el.setStyle('width', width + '%');
29859             i.topEl.el.setStyle('width', width + '%');
29860             i.bottomEl.el.setStyle('width', width + '%');
29861         }, this);
29862         
29863     }
29864     
29865 });
29866 /*
29867  * - LGPL
29868  *
29869  * Nav Progress Item
29870  * 
29871  */
29872
29873 /**
29874  * @class Roo.bootstrap.NavProgressItem
29875  * @extends Roo.bootstrap.Component
29876  * Bootstrap NavProgressItem class
29877  * @cfg {String} rid the reference id
29878  * @cfg {Boolean} active (true|false) Is item active default false
29879  * @cfg {Boolean} disabled (true|false) Is item active default false
29880  * @cfg {String} html
29881  * @cfg {String} position (top|bottom) text position default bottom
29882  * @cfg {String} icon show icon instead of number
29883  * 
29884  * @constructor
29885  * Create a new NavProgressItem
29886  * @param {Object} config The config object
29887  */
29888 Roo.bootstrap.NavProgressItem = function(config){
29889     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29890     this.addEvents({
29891         // raw events
29892         /**
29893          * @event click
29894          * The raw click event for the entire grid.
29895          * @param {Roo.bootstrap.NavProgressItem} this
29896          * @param {Roo.EventObject} e
29897          */
29898         "click" : true
29899     });
29900    
29901 };
29902
29903 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29904     
29905     rid : '',
29906     active : false,
29907     disabled : false,
29908     html : '',
29909     position : 'bottom',
29910     icon : false,
29911     
29912     getAutoCreate : function()
29913     {
29914         var iconCls = 'roo-navigation-bar-item-icon';
29915         
29916         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29917         
29918         var cfg = {
29919             tag: 'li',
29920             cls: 'roo-navigation-bar-item',
29921             cn : [
29922                 {
29923                     tag : 'i',
29924                     cls : iconCls
29925                 }
29926             ]
29927         };
29928         
29929         if(this.active){
29930             cfg.cls += ' active';
29931         }
29932         if(this.disabled){
29933             cfg.cls += ' disabled';
29934         }
29935         
29936         return cfg;
29937     },
29938     
29939     disable : function()
29940     {
29941         this.setDisabled(true);
29942     },
29943     
29944     enable : function()
29945     {
29946         this.setDisabled(false);
29947     },
29948     
29949     initEvents: function() 
29950     {
29951         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29952         
29953         this.iconEl.on('click', this.onClick, this);
29954     },
29955     
29956     onClick : function(e)
29957     {
29958         e.preventDefault();
29959         
29960         if(this.disabled){
29961             return;
29962         }
29963         
29964         if(this.fireEvent('click', this, e) === false){
29965             return;
29966         };
29967         
29968         this.parent().setActiveItem(this);
29969     },
29970     
29971     isActive: function () 
29972     {
29973         return this.active;
29974     },
29975     
29976     setActive : function(state)
29977     {
29978         if(this.active == state){
29979             return;
29980         }
29981         
29982         this.active = state;
29983         
29984         if (state) {
29985             this.el.addClass('active');
29986             return;
29987         }
29988         
29989         this.el.removeClass('active');
29990         
29991         return;
29992     },
29993     
29994     setDisabled : function(state)
29995     {
29996         if(this.disabled == state){
29997             return;
29998         }
29999         
30000         this.disabled = state;
30001         
30002         if (state) {
30003             this.el.addClass('disabled');
30004             return;
30005         }
30006         
30007         this.el.removeClass('disabled');
30008     },
30009     
30010     tooltipEl : function()
30011     {
30012         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30013     }
30014 });
30015  
30016
30017  /*
30018  * - LGPL
30019  *
30020  * FieldLabel
30021  * 
30022  */
30023
30024 /**
30025  * @class Roo.bootstrap.FieldLabel
30026  * @extends Roo.bootstrap.Component
30027  * Bootstrap FieldLabel class
30028  * @cfg {String} html contents of the element
30029  * @cfg {String} tag tag of the element default label
30030  * @cfg {String} cls class of the element
30031  * @cfg {String} target label target 
30032  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30033  * @cfg {String} invalidClass default "text-warning"
30034  * @cfg {String} validClass default "text-success"
30035  * @cfg {String} iconTooltip default "This field is required"
30036  * @cfg {String} indicatorpos (left|right) default left
30037  * 
30038  * @constructor
30039  * Create a new FieldLabel
30040  * @param {Object} config The config object
30041  */
30042
30043 Roo.bootstrap.FieldLabel = function(config){
30044     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30045     
30046     this.addEvents({
30047             /**
30048              * @event invalid
30049              * Fires after the field has been marked as invalid.
30050              * @param {Roo.form.FieldLabel} this
30051              * @param {String} msg The validation message
30052              */
30053             invalid : true,
30054             /**
30055              * @event valid
30056              * Fires after the field has been validated with no errors.
30057              * @param {Roo.form.FieldLabel} this
30058              */
30059             valid : true
30060         });
30061 };
30062
30063 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30064     
30065     tag: 'label',
30066     cls: '',
30067     html: '',
30068     target: '',
30069     allowBlank : true,
30070     invalidClass : 'has-warning',
30071     validClass : 'has-success',
30072     iconTooltip : 'This field is required',
30073     indicatorpos : 'left',
30074     
30075     getAutoCreate : function(){
30076         
30077         var cls = "";
30078         if (!this.allowBlank) {
30079             cls  = "visible";
30080         }
30081         
30082         var cfg = {
30083             tag : this.tag,
30084             cls : 'roo-bootstrap-field-label ' + this.cls,
30085             for : this.target,
30086             cn : [
30087                 {
30088                     tag : 'i',
30089                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30090                     tooltip : this.iconTooltip
30091                 },
30092                 {
30093                     tag : 'span',
30094                     html : this.html
30095                 }
30096             ] 
30097         };
30098         
30099         if(this.indicatorpos == 'right'){
30100             var cfg = {
30101                 tag : this.tag,
30102                 cls : 'roo-bootstrap-field-label ' + this.cls,
30103                 for : this.target,
30104                 cn : [
30105                     {
30106                         tag : 'span',
30107                         html : this.html
30108                     },
30109                     {
30110                         tag : 'i',
30111                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30112                         tooltip : this.iconTooltip
30113                     }
30114                 ] 
30115             };
30116         }
30117         
30118         return cfg;
30119     },
30120     
30121     initEvents: function() 
30122     {
30123         Roo.bootstrap.Element.superclass.initEvents.call(this);
30124         
30125         this.indicator = this.indicatorEl();
30126         
30127         if(this.indicator){
30128             this.indicator.removeClass('visible');
30129             this.indicator.addClass('invisible');
30130         }
30131         
30132         Roo.bootstrap.FieldLabel.register(this);
30133     },
30134     
30135     indicatorEl : function()
30136     {
30137         var indicator = this.el.select('i.roo-required-indicator',true).first();
30138         
30139         if(!indicator){
30140             return false;
30141         }
30142         
30143         return indicator;
30144         
30145     },
30146     
30147     /**
30148      * Mark this field as valid
30149      */
30150     markValid : function()
30151     {
30152         if(this.indicator){
30153             this.indicator.removeClass('visible');
30154             this.indicator.addClass('invisible');
30155         }
30156         
30157         this.el.removeClass(this.invalidClass);
30158         
30159         this.el.addClass(this.validClass);
30160         
30161         this.fireEvent('valid', this);
30162     },
30163     
30164     /**
30165      * Mark this field as invalid
30166      * @param {String} msg The validation message
30167      */
30168     markInvalid : function(msg)
30169     {
30170         if(this.indicator){
30171             this.indicator.removeClass('invisible');
30172             this.indicator.addClass('visible');
30173         }
30174         
30175         this.el.removeClass(this.validClass);
30176         
30177         this.el.addClass(this.invalidClass);
30178         
30179         this.fireEvent('invalid', this, msg);
30180     }
30181     
30182    
30183 });
30184
30185 Roo.apply(Roo.bootstrap.FieldLabel, {
30186     
30187     groups: {},
30188     
30189      /**
30190     * register a FieldLabel Group
30191     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30192     */
30193     register : function(label)
30194     {
30195         if(this.groups.hasOwnProperty(label.target)){
30196             return;
30197         }
30198      
30199         this.groups[label.target] = label;
30200         
30201     },
30202     /**
30203     * fetch a FieldLabel Group based on the target
30204     * @param {string} target
30205     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30206     */
30207     get: function(target) {
30208         if (typeof(this.groups[target]) == 'undefined') {
30209             return false;
30210         }
30211         
30212         return this.groups[target] ;
30213     }
30214 });
30215
30216  
30217
30218  /*
30219  * - LGPL
30220  *
30221  * page DateSplitField.
30222  * 
30223  */
30224
30225
30226 /**
30227  * @class Roo.bootstrap.DateSplitField
30228  * @extends Roo.bootstrap.Component
30229  * Bootstrap DateSplitField class
30230  * @cfg {string} fieldLabel - the label associated
30231  * @cfg {Number} labelWidth set the width of label (0-12)
30232  * @cfg {String} labelAlign (top|left)
30233  * @cfg {Boolean} dayAllowBlank (true|false) default false
30234  * @cfg {Boolean} monthAllowBlank (true|false) default false
30235  * @cfg {Boolean} yearAllowBlank (true|false) default false
30236  * @cfg {string} dayPlaceholder 
30237  * @cfg {string} monthPlaceholder
30238  * @cfg {string} yearPlaceholder
30239  * @cfg {string} dayFormat default 'd'
30240  * @cfg {string} monthFormat default 'm'
30241  * @cfg {string} yearFormat default 'Y'
30242  * @cfg {Number} labellg set the width of label (1-12)
30243  * @cfg {Number} labelmd set the width of label (1-12)
30244  * @cfg {Number} labelsm set the width of label (1-12)
30245  * @cfg {Number} labelxs set the width of label (1-12)
30246
30247  *     
30248  * @constructor
30249  * Create a new DateSplitField
30250  * @param {Object} config The config object
30251  */
30252
30253 Roo.bootstrap.DateSplitField = function(config){
30254     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30255     
30256     this.addEvents({
30257         // raw events
30258          /**
30259          * @event years
30260          * getting the data of years
30261          * @param {Roo.bootstrap.DateSplitField} this
30262          * @param {Object} years
30263          */
30264         "years" : true,
30265         /**
30266          * @event days
30267          * getting the data of days
30268          * @param {Roo.bootstrap.DateSplitField} this
30269          * @param {Object} days
30270          */
30271         "days" : true,
30272         /**
30273          * @event invalid
30274          * Fires after the field has been marked as invalid.
30275          * @param {Roo.form.Field} this
30276          * @param {String} msg The validation message
30277          */
30278         invalid : true,
30279        /**
30280          * @event valid
30281          * Fires after the field has been validated with no errors.
30282          * @param {Roo.form.Field} this
30283          */
30284         valid : true
30285     });
30286 };
30287
30288 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30289     
30290     fieldLabel : '',
30291     labelAlign : 'top',
30292     labelWidth : 3,
30293     dayAllowBlank : false,
30294     monthAllowBlank : false,
30295     yearAllowBlank : false,
30296     dayPlaceholder : '',
30297     monthPlaceholder : '',
30298     yearPlaceholder : '',
30299     dayFormat : 'd',
30300     monthFormat : 'm',
30301     yearFormat : 'Y',
30302     isFormField : true,
30303     labellg : 0,
30304     labelmd : 0,
30305     labelsm : 0,
30306     labelxs : 0,
30307     
30308     getAutoCreate : function()
30309     {
30310         var cfg = {
30311             tag : 'div',
30312             cls : 'row roo-date-split-field-group',
30313             cn : [
30314                 {
30315                     tag : 'input',
30316                     type : 'hidden',
30317                     cls : 'form-hidden-field roo-date-split-field-group-value',
30318                     name : this.name
30319                 }
30320             ]
30321         };
30322         
30323         var labelCls = 'col-md-12';
30324         var contentCls = 'col-md-4';
30325         
30326         if(this.fieldLabel){
30327             
30328             var label = {
30329                 tag : 'div',
30330                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30331                 cn : [
30332                     {
30333                         tag : 'label',
30334                         html : this.fieldLabel
30335                     }
30336                 ]
30337             };
30338             
30339             if(this.labelAlign == 'left'){
30340             
30341                 if(this.labelWidth > 12){
30342                     label.style = "width: " + this.labelWidth + 'px';
30343                 }
30344
30345                 if(this.labelWidth < 13 && this.labelmd == 0){
30346                     this.labelmd = this.labelWidth;
30347                 }
30348
30349                 if(this.labellg > 0){
30350                     labelCls = ' col-lg-' + this.labellg;
30351                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30352                 }
30353
30354                 if(this.labelmd > 0){
30355                     labelCls = ' col-md-' + this.labelmd;
30356                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30357                 }
30358
30359                 if(this.labelsm > 0){
30360                     labelCls = ' col-sm-' + this.labelsm;
30361                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30362                 }
30363
30364                 if(this.labelxs > 0){
30365                     labelCls = ' col-xs-' + this.labelxs;
30366                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30367                 }
30368             }
30369             
30370             label.cls += ' ' + labelCls;
30371             
30372             cfg.cn.push(label);
30373         }
30374         
30375         Roo.each(['day', 'month', 'year'], function(t){
30376             cfg.cn.push({
30377                 tag : 'div',
30378                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30379             });
30380         }, this);
30381         
30382         return cfg;
30383     },
30384     
30385     inputEl: function ()
30386     {
30387         return this.el.select('.roo-date-split-field-group-value', true).first();
30388     },
30389     
30390     onRender : function(ct, position) 
30391     {
30392         var _this = this;
30393         
30394         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30395         
30396         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30397         
30398         this.dayField = new Roo.bootstrap.ComboBox({
30399             allowBlank : this.dayAllowBlank,
30400             alwaysQuery : true,
30401             displayField : 'value',
30402             editable : false,
30403             fieldLabel : '',
30404             forceSelection : true,
30405             mode : 'local',
30406             placeholder : this.dayPlaceholder,
30407             selectOnFocus : true,
30408             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30409             triggerAction : 'all',
30410             typeAhead : true,
30411             valueField : 'value',
30412             store : new Roo.data.SimpleStore({
30413                 data : (function() {    
30414                     var days = [];
30415                     _this.fireEvent('days', _this, days);
30416                     return days;
30417                 })(),
30418                 fields : [ 'value' ]
30419             }),
30420             listeners : {
30421                 select : function (_self, record, index)
30422                 {
30423                     _this.setValue(_this.getValue());
30424                 }
30425             }
30426         });
30427
30428         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30429         
30430         this.monthField = new Roo.bootstrap.MonthField({
30431             after : '<i class=\"fa fa-calendar\"></i>',
30432             allowBlank : this.monthAllowBlank,
30433             placeholder : this.monthPlaceholder,
30434             readOnly : true,
30435             listeners : {
30436                 render : function (_self)
30437                 {
30438                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30439                         e.preventDefault();
30440                         _self.focus();
30441                     });
30442                 },
30443                 select : function (_self, oldvalue, newvalue)
30444                 {
30445                     _this.setValue(_this.getValue());
30446                 }
30447             }
30448         });
30449         
30450         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30451         
30452         this.yearField = new Roo.bootstrap.ComboBox({
30453             allowBlank : this.yearAllowBlank,
30454             alwaysQuery : true,
30455             displayField : 'value',
30456             editable : false,
30457             fieldLabel : '',
30458             forceSelection : true,
30459             mode : 'local',
30460             placeholder : this.yearPlaceholder,
30461             selectOnFocus : true,
30462             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30463             triggerAction : 'all',
30464             typeAhead : true,
30465             valueField : 'value',
30466             store : new Roo.data.SimpleStore({
30467                 data : (function() {
30468                     var years = [];
30469                     _this.fireEvent('years', _this, years);
30470                     return years;
30471                 })(),
30472                 fields : [ 'value' ]
30473             }),
30474             listeners : {
30475                 select : function (_self, record, index)
30476                 {
30477                     _this.setValue(_this.getValue());
30478                 }
30479             }
30480         });
30481
30482         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30483     },
30484     
30485     setValue : function(v, format)
30486     {
30487         this.inputEl.dom.value = v;
30488         
30489         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30490         
30491         var d = Date.parseDate(v, f);
30492         
30493         if(!d){
30494             this.validate();
30495             return;
30496         }
30497         
30498         this.setDay(d.format(this.dayFormat));
30499         this.setMonth(d.format(this.monthFormat));
30500         this.setYear(d.format(this.yearFormat));
30501         
30502         this.validate();
30503         
30504         return;
30505     },
30506     
30507     setDay : function(v)
30508     {
30509         this.dayField.setValue(v);
30510         this.inputEl.dom.value = this.getValue();
30511         this.validate();
30512         return;
30513     },
30514     
30515     setMonth : function(v)
30516     {
30517         this.monthField.setValue(v, true);
30518         this.inputEl.dom.value = this.getValue();
30519         this.validate();
30520         return;
30521     },
30522     
30523     setYear : function(v)
30524     {
30525         this.yearField.setValue(v);
30526         this.inputEl.dom.value = this.getValue();
30527         this.validate();
30528         return;
30529     },
30530     
30531     getDay : function()
30532     {
30533         return this.dayField.getValue();
30534     },
30535     
30536     getMonth : function()
30537     {
30538         return this.monthField.getValue();
30539     },
30540     
30541     getYear : function()
30542     {
30543         return this.yearField.getValue();
30544     },
30545     
30546     getValue : function()
30547     {
30548         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30549         
30550         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30551         
30552         return date;
30553     },
30554     
30555     reset : function()
30556     {
30557         this.setDay('');
30558         this.setMonth('');
30559         this.setYear('');
30560         this.inputEl.dom.value = '';
30561         this.validate();
30562         return;
30563     },
30564     
30565     validate : function()
30566     {
30567         var d = this.dayField.validate();
30568         var m = this.monthField.validate();
30569         var y = this.yearField.validate();
30570         
30571         var valid = true;
30572         
30573         if(
30574                 (!this.dayAllowBlank && !d) ||
30575                 (!this.monthAllowBlank && !m) ||
30576                 (!this.yearAllowBlank && !y)
30577         ){
30578             valid = false;
30579         }
30580         
30581         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30582             return valid;
30583         }
30584         
30585         if(valid){
30586             this.markValid();
30587             return valid;
30588         }
30589         
30590         this.markInvalid();
30591         
30592         return valid;
30593     },
30594     
30595     markValid : function()
30596     {
30597         
30598         var label = this.el.select('label', true).first();
30599         var icon = this.el.select('i.fa-star', true).first();
30600
30601         if(label && icon){
30602             icon.remove();
30603         }
30604         
30605         this.fireEvent('valid', this);
30606     },
30607     
30608      /**
30609      * Mark this field as invalid
30610      * @param {String} msg The validation message
30611      */
30612     markInvalid : function(msg)
30613     {
30614         
30615         var label = this.el.select('label', true).first();
30616         var icon = this.el.select('i.fa-star', true).first();
30617
30618         if(label && !icon){
30619             this.el.select('.roo-date-split-field-label', true).createChild({
30620                 tag : 'i',
30621                 cls : 'text-danger fa fa-lg fa-star',
30622                 tooltip : 'This field is required',
30623                 style : 'margin-right:5px;'
30624             }, label, true);
30625         }
30626         
30627         this.fireEvent('invalid', this, msg);
30628     },
30629     
30630     clearInvalid : function()
30631     {
30632         var label = this.el.select('label', true).first();
30633         var icon = this.el.select('i.fa-star', true).first();
30634
30635         if(label && icon){
30636             icon.remove();
30637         }
30638         
30639         this.fireEvent('valid', this);
30640     },
30641     
30642     getName: function()
30643     {
30644         return this.name;
30645     }
30646     
30647 });
30648
30649  /**
30650  *
30651  * This is based on 
30652  * http://masonry.desandro.com
30653  *
30654  * The idea is to render all the bricks based on vertical width...
30655  *
30656  * The original code extends 'outlayer' - we might need to use that....
30657  * 
30658  */
30659
30660
30661 /**
30662  * @class Roo.bootstrap.LayoutMasonry
30663  * @extends Roo.bootstrap.Component
30664  * Bootstrap Layout Masonry class
30665  * 
30666  * @constructor
30667  * Create a new Element
30668  * @param {Object} config The config object
30669  */
30670
30671 Roo.bootstrap.LayoutMasonry = function(config){
30672     
30673     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30674     
30675     this.bricks = [];
30676     
30677     Roo.bootstrap.LayoutMasonry.register(this);
30678     
30679     this.addEvents({
30680         // raw events
30681         /**
30682          * @event layout
30683          * Fire after layout the items
30684          * @param {Roo.bootstrap.LayoutMasonry} this
30685          * @param {Roo.EventObject} e
30686          */
30687         "layout" : true
30688     });
30689     
30690 };
30691
30692 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30693     
30694     /**
30695      * @cfg {Boolean} isLayoutInstant = no animation?
30696      */   
30697     isLayoutInstant : false, // needed?
30698    
30699     /**
30700      * @cfg {Number} boxWidth  width of the columns
30701      */   
30702     boxWidth : 450,
30703     
30704       /**
30705      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30706      */   
30707     boxHeight : 0,
30708     
30709     /**
30710      * @cfg {Number} padWidth padding below box..
30711      */   
30712     padWidth : 10, 
30713     
30714     /**
30715      * @cfg {Number} gutter gutter width..
30716      */   
30717     gutter : 10,
30718     
30719      /**
30720      * @cfg {Number} maxCols maximum number of columns
30721      */   
30722     
30723     maxCols: 0,
30724     
30725     /**
30726      * @cfg {Boolean} isAutoInitial defalut true
30727      */   
30728     isAutoInitial : true, 
30729     
30730     containerWidth: 0,
30731     
30732     /**
30733      * @cfg {Boolean} isHorizontal defalut false
30734      */   
30735     isHorizontal : false, 
30736
30737     currentSize : null,
30738     
30739     tag: 'div',
30740     
30741     cls: '',
30742     
30743     bricks: null, //CompositeElement
30744     
30745     cols : 1,
30746     
30747     _isLayoutInited : false,
30748     
30749 //    isAlternative : false, // only use for vertical layout...
30750     
30751     /**
30752      * @cfg {Number} alternativePadWidth padding below box..
30753      */   
30754     alternativePadWidth : 50,
30755     
30756     selectedBrick : [],
30757     
30758     getAutoCreate : function(){
30759         
30760         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30761         
30762         var cfg = {
30763             tag: this.tag,
30764             cls: 'blog-masonary-wrapper ' + this.cls,
30765             cn : {
30766                 cls : 'mas-boxes masonary'
30767             }
30768         };
30769         
30770         return cfg;
30771     },
30772     
30773     getChildContainer: function( )
30774     {
30775         if (this.boxesEl) {
30776             return this.boxesEl;
30777         }
30778         
30779         this.boxesEl = this.el.select('.mas-boxes').first();
30780         
30781         return this.boxesEl;
30782     },
30783     
30784     
30785     initEvents : function()
30786     {
30787         var _this = this;
30788         
30789         if(this.isAutoInitial){
30790             Roo.log('hook children rendered');
30791             this.on('childrenrendered', function() {
30792                 Roo.log('children rendered');
30793                 _this.initial();
30794             } ,this);
30795         }
30796     },
30797     
30798     initial : function()
30799     {
30800         this.selectedBrick = [];
30801         
30802         this.currentSize = this.el.getBox(true);
30803         
30804         Roo.EventManager.onWindowResize(this.resize, this); 
30805
30806         if(!this.isAutoInitial){
30807             this.layout();
30808             return;
30809         }
30810         
30811         this.layout();
30812         
30813         return;
30814         //this.layout.defer(500,this);
30815         
30816     },
30817     
30818     resize : function()
30819     {
30820         var cs = this.el.getBox(true);
30821         
30822         if (
30823                 this.currentSize.width == cs.width && 
30824                 this.currentSize.x == cs.x && 
30825                 this.currentSize.height == cs.height && 
30826                 this.currentSize.y == cs.y 
30827         ) {
30828             Roo.log("no change in with or X or Y");
30829             return;
30830         }
30831         
30832         this.currentSize = cs;
30833         
30834         this.layout();
30835         
30836     },
30837     
30838     layout : function()
30839     {   
30840         this._resetLayout();
30841         
30842         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30843         
30844         this.layoutItems( isInstant );
30845       
30846         this._isLayoutInited = true;
30847         
30848         this.fireEvent('layout', this);
30849         
30850     },
30851     
30852     _resetLayout : function()
30853     {
30854         if(this.isHorizontal){
30855             this.horizontalMeasureColumns();
30856             return;
30857         }
30858         
30859         this.verticalMeasureColumns();
30860         
30861     },
30862     
30863     verticalMeasureColumns : function()
30864     {
30865         this.getContainerWidth();
30866         
30867 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30868 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30869 //            return;
30870 //        }
30871         
30872         var boxWidth = this.boxWidth + this.padWidth;
30873         
30874         if(this.containerWidth < this.boxWidth){
30875             boxWidth = this.containerWidth
30876         }
30877         
30878         var containerWidth = this.containerWidth;
30879         
30880         var cols = Math.floor(containerWidth / boxWidth);
30881         
30882         this.cols = Math.max( cols, 1 );
30883         
30884         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30885         
30886         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30887         
30888         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30889         
30890         this.colWidth = boxWidth + avail - this.padWidth;
30891         
30892         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30893         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30894     },
30895     
30896     horizontalMeasureColumns : function()
30897     {
30898         this.getContainerWidth();
30899         
30900         var boxWidth = this.boxWidth;
30901         
30902         if(this.containerWidth < boxWidth){
30903             boxWidth = this.containerWidth;
30904         }
30905         
30906         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30907         
30908         this.el.setHeight(boxWidth);
30909         
30910     },
30911     
30912     getContainerWidth : function()
30913     {
30914         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30915     },
30916     
30917     layoutItems : function( isInstant )
30918     {
30919         Roo.log(this.bricks);
30920         
30921         var items = Roo.apply([], this.bricks);
30922         
30923         if(this.isHorizontal){
30924             this._horizontalLayoutItems( items , isInstant );
30925             return;
30926         }
30927         
30928 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30929 //            this._verticalAlternativeLayoutItems( items , isInstant );
30930 //            return;
30931 //        }
30932         
30933         this._verticalLayoutItems( items , isInstant );
30934         
30935     },
30936     
30937     _verticalLayoutItems : function ( items , isInstant)
30938     {
30939         if ( !items || !items.length ) {
30940             return;
30941         }
30942         
30943         var standard = [
30944             ['xs', 'xs', 'xs', 'tall'],
30945             ['xs', 'xs', 'tall'],
30946             ['xs', 'xs', 'sm'],
30947             ['xs', 'xs', 'xs'],
30948             ['xs', 'tall'],
30949             ['xs', 'sm'],
30950             ['xs', 'xs'],
30951             ['xs'],
30952             
30953             ['sm', 'xs', 'xs'],
30954             ['sm', 'xs'],
30955             ['sm'],
30956             
30957             ['tall', 'xs', 'xs', 'xs'],
30958             ['tall', 'xs', 'xs'],
30959             ['tall', 'xs'],
30960             ['tall']
30961             
30962         ];
30963         
30964         var queue = [];
30965         
30966         var boxes = [];
30967         
30968         var box = [];
30969         
30970         Roo.each(items, function(item, k){
30971             
30972             switch (item.size) {
30973                 // these layouts take up a full box,
30974                 case 'md' :
30975                 case 'md-left' :
30976                 case 'md-right' :
30977                 case 'wide' :
30978                     
30979                     if(box.length){
30980                         boxes.push(box);
30981                         box = [];
30982                     }
30983                     
30984                     boxes.push([item]);
30985                     
30986                     break;
30987                     
30988                 case 'xs' :
30989                 case 'sm' :
30990                 case 'tall' :
30991                     
30992                     box.push(item);
30993                     
30994                     break;
30995                 default :
30996                     break;
30997                     
30998             }
30999             
31000         }, this);
31001         
31002         if(box.length){
31003             boxes.push(box);
31004             box = [];
31005         }
31006         
31007         var filterPattern = function(box, length)
31008         {
31009             if(!box.length){
31010                 return;
31011             }
31012             
31013             var match = false;
31014             
31015             var pattern = box.slice(0, length);
31016             
31017             var format = [];
31018             
31019             Roo.each(pattern, function(i){
31020                 format.push(i.size);
31021             }, this);
31022             
31023             Roo.each(standard, function(s){
31024                 
31025                 if(String(s) != String(format)){
31026                     return;
31027                 }
31028                 
31029                 match = true;
31030                 return false;
31031                 
31032             }, this);
31033             
31034             if(!match && length == 1){
31035                 return;
31036             }
31037             
31038             if(!match){
31039                 filterPattern(box, length - 1);
31040                 return;
31041             }
31042                 
31043             queue.push(pattern);
31044
31045             box = box.slice(length, box.length);
31046
31047             filterPattern(box, 4);
31048
31049             return;
31050             
31051         }
31052         
31053         Roo.each(boxes, function(box, k){
31054             
31055             if(!box.length){
31056                 return;
31057             }
31058             
31059             if(box.length == 1){
31060                 queue.push(box);
31061                 return;
31062             }
31063             
31064             filterPattern(box, 4);
31065             
31066         }, this);
31067         
31068         this._processVerticalLayoutQueue( queue, isInstant );
31069         
31070     },
31071     
31072 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31073 //    {
31074 //        if ( !items || !items.length ) {
31075 //            return;
31076 //        }
31077 //
31078 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31079 //        
31080 //    },
31081     
31082     _horizontalLayoutItems : function ( items , isInstant)
31083     {
31084         if ( !items || !items.length || items.length < 3) {
31085             return;
31086         }
31087         
31088         items.reverse();
31089         
31090         var eItems = items.slice(0, 3);
31091         
31092         items = items.slice(3, items.length);
31093         
31094         var standard = [
31095             ['xs', 'xs', 'xs', 'wide'],
31096             ['xs', 'xs', 'wide'],
31097             ['xs', 'xs', 'sm'],
31098             ['xs', 'xs', 'xs'],
31099             ['xs', 'wide'],
31100             ['xs', 'sm'],
31101             ['xs', 'xs'],
31102             ['xs'],
31103             
31104             ['sm', 'xs', 'xs'],
31105             ['sm', 'xs'],
31106             ['sm'],
31107             
31108             ['wide', 'xs', 'xs', 'xs'],
31109             ['wide', 'xs', 'xs'],
31110             ['wide', 'xs'],
31111             ['wide'],
31112             
31113             ['wide-thin']
31114         ];
31115         
31116         var queue = [];
31117         
31118         var boxes = [];
31119         
31120         var box = [];
31121         
31122         Roo.each(items, function(item, k){
31123             
31124             switch (item.size) {
31125                 case 'md' :
31126                 case 'md-left' :
31127                 case 'md-right' :
31128                 case 'tall' :
31129                     
31130                     if(box.length){
31131                         boxes.push(box);
31132                         box = [];
31133                     }
31134                     
31135                     boxes.push([item]);
31136                     
31137                     break;
31138                     
31139                 case 'xs' :
31140                 case 'sm' :
31141                 case 'wide' :
31142                 case 'wide-thin' :
31143                     
31144                     box.push(item);
31145                     
31146                     break;
31147                 default :
31148                     break;
31149                     
31150             }
31151             
31152         }, this);
31153         
31154         if(box.length){
31155             boxes.push(box);
31156             box = [];
31157         }
31158         
31159         var filterPattern = function(box, length)
31160         {
31161             if(!box.length){
31162                 return;
31163             }
31164             
31165             var match = false;
31166             
31167             var pattern = box.slice(0, length);
31168             
31169             var format = [];
31170             
31171             Roo.each(pattern, function(i){
31172                 format.push(i.size);
31173             }, this);
31174             
31175             Roo.each(standard, function(s){
31176                 
31177                 if(String(s) != String(format)){
31178                     return;
31179                 }
31180                 
31181                 match = true;
31182                 return false;
31183                 
31184             }, this);
31185             
31186             if(!match && length == 1){
31187                 return;
31188             }
31189             
31190             if(!match){
31191                 filterPattern(box, length - 1);
31192                 return;
31193             }
31194                 
31195             queue.push(pattern);
31196
31197             box = box.slice(length, box.length);
31198
31199             filterPattern(box, 4);
31200
31201             return;
31202             
31203         }
31204         
31205         Roo.each(boxes, function(box, k){
31206             
31207             if(!box.length){
31208                 return;
31209             }
31210             
31211             if(box.length == 1){
31212                 queue.push(box);
31213                 return;
31214             }
31215             
31216             filterPattern(box, 4);
31217             
31218         }, this);
31219         
31220         
31221         var prune = [];
31222         
31223         var pos = this.el.getBox(true);
31224         
31225         var minX = pos.x;
31226         
31227         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31228         
31229         var hit_end = false;
31230         
31231         Roo.each(queue, function(box){
31232             
31233             if(hit_end){
31234                 
31235                 Roo.each(box, function(b){
31236                 
31237                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31238                     b.el.hide();
31239
31240                 }, this);
31241
31242                 return;
31243             }
31244             
31245             var mx = 0;
31246             
31247             Roo.each(box, function(b){
31248                 
31249                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31250                 b.el.show();
31251
31252                 mx = Math.max(mx, b.x);
31253                 
31254             }, this);
31255             
31256             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31257             
31258             if(maxX < minX){
31259                 
31260                 Roo.each(box, function(b){
31261                 
31262                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31263                     b.el.hide();
31264                     
31265                 }, this);
31266                 
31267                 hit_end = true;
31268                 
31269                 return;
31270             }
31271             
31272             prune.push(box);
31273             
31274         }, this);
31275         
31276         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31277     },
31278     
31279     /** Sets position of item in DOM
31280     * @param {Element} item
31281     * @param {Number} x - horizontal position
31282     * @param {Number} y - vertical position
31283     * @param {Boolean} isInstant - disables transitions
31284     */
31285     _processVerticalLayoutQueue : function( queue, isInstant )
31286     {
31287         var pos = this.el.getBox(true);
31288         var x = pos.x;
31289         var y = pos.y;
31290         var maxY = [];
31291         
31292         for (var i = 0; i < this.cols; i++){
31293             maxY[i] = pos.y;
31294         }
31295         
31296         Roo.each(queue, function(box, k){
31297             
31298             var col = k % this.cols;
31299             
31300             Roo.each(box, function(b,kk){
31301                 
31302                 b.el.position('absolute');
31303                 
31304                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31305                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31306                 
31307                 if(b.size == 'md-left' || b.size == 'md-right'){
31308                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31309                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31310                 }
31311                 
31312                 b.el.setWidth(width);
31313                 b.el.setHeight(height);
31314                 // iframe?
31315                 b.el.select('iframe',true).setSize(width,height);
31316                 
31317             }, this);
31318             
31319             for (var i = 0; i < this.cols; i++){
31320                 
31321                 if(maxY[i] < maxY[col]){
31322                     col = i;
31323                     continue;
31324                 }
31325                 
31326                 col = Math.min(col, i);
31327                 
31328             }
31329             
31330             x = pos.x + col * (this.colWidth + this.padWidth);
31331             
31332             y = maxY[col];
31333             
31334             var positions = [];
31335             
31336             switch (box.length){
31337                 case 1 :
31338                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31339                     break;
31340                 case 2 :
31341                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31342                     break;
31343                 case 3 :
31344                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31345                     break;
31346                 case 4 :
31347                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31348                     break;
31349                 default :
31350                     break;
31351             }
31352             
31353             Roo.each(box, function(b,kk){
31354                 
31355                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31356                 
31357                 var sz = b.el.getSize();
31358                 
31359                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31360                 
31361             }, this);
31362             
31363         }, this);
31364         
31365         var mY = 0;
31366         
31367         for (var i = 0; i < this.cols; i++){
31368             mY = Math.max(mY, maxY[i]);
31369         }
31370         
31371         this.el.setHeight(mY - pos.y);
31372         
31373     },
31374     
31375 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31376 //    {
31377 //        var pos = this.el.getBox(true);
31378 //        var x = pos.x;
31379 //        var y = pos.y;
31380 //        var maxX = pos.right;
31381 //        
31382 //        var maxHeight = 0;
31383 //        
31384 //        Roo.each(items, function(item, k){
31385 //            
31386 //            var c = k % 2;
31387 //            
31388 //            item.el.position('absolute');
31389 //                
31390 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31391 //
31392 //            item.el.setWidth(width);
31393 //
31394 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31395 //
31396 //            item.el.setHeight(height);
31397 //            
31398 //            if(c == 0){
31399 //                item.el.setXY([x, y], isInstant ? false : true);
31400 //            } else {
31401 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31402 //            }
31403 //            
31404 //            y = y + height + this.alternativePadWidth;
31405 //            
31406 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31407 //            
31408 //        }, this);
31409 //        
31410 //        this.el.setHeight(maxHeight);
31411 //        
31412 //    },
31413     
31414     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31415     {
31416         var pos = this.el.getBox(true);
31417         
31418         var minX = pos.x;
31419         var minY = pos.y;
31420         
31421         var maxX = pos.right;
31422         
31423         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31424         
31425         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31426         
31427         Roo.each(queue, function(box, k){
31428             
31429             Roo.each(box, function(b, kk){
31430                 
31431                 b.el.position('absolute');
31432                 
31433                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31434                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31435                 
31436                 if(b.size == 'md-left' || b.size == 'md-right'){
31437                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31438                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31439                 }
31440                 
31441                 b.el.setWidth(width);
31442                 b.el.setHeight(height);
31443                 
31444             }, this);
31445             
31446             if(!box.length){
31447                 return;
31448             }
31449             
31450             var positions = [];
31451             
31452             switch (box.length){
31453                 case 1 :
31454                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31455                     break;
31456                 case 2 :
31457                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31458                     break;
31459                 case 3 :
31460                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31461                     break;
31462                 case 4 :
31463                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31464                     break;
31465                 default :
31466                     break;
31467             }
31468             
31469             Roo.each(box, function(b,kk){
31470                 
31471                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31472                 
31473                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31474                 
31475             }, this);
31476             
31477         }, this);
31478         
31479     },
31480     
31481     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31482     {
31483         Roo.each(eItems, function(b,k){
31484             
31485             b.size = (k == 0) ? 'sm' : 'xs';
31486             b.x = (k == 0) ? 2 : 1;
31487             b.y = (k == 0) ? 2 : 1;
31488             
31489             b.el.position('absolute');
31490             
31491             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31492                 
31493             b.el.setWidth(width);
31494             
31495             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31496             
31497             b.el.setHeight(height);
31498             
31499         }, this);
31500
31501         var positions = [];
31502         
31503         positions.push({
31504             x : maxX - this.unitWidth * 2 - this.gutter,
31505             y : minY
31506         });
31507         
31508         positions.push({
31509             x : maxX - this.unitWidth,
31510             y : minY + (this.unitWidth + this.gutter) * 2
31511         });
31512         
31513         positions.push({
31514             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31515             y : minY
31516         });
31517         
31518         Roo.each(eItems, function(b,k){
31519             
31520             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31521
31522         }, this);
31523         
31524     },
31525     
31526     getVerticalOneBoxColPositions : function(x, y, box)
31527     {
31528         var pos = [];
31529         
31530         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31531         
31532         if(box[0].size == 'md-left'){
31533             rand = 0;
31534         }
31535         
31536         if(box[0].size == 'md-right'){
31537             rand = 1;
31538         }
31539         
31540         pos.push({
31541             x : x + (this.unitWidth + this.gutter) * rand,
31542             y : y
31543         });
31544         
31545         return pos;
31546     },
31547     
31548     getVerticalTwoBoxColPositions : function(x, y, box)
31549     {
31550         var pos = [];
31551         
31552         if(box[0].size == 'xs'){
31553             
31554             pos.push({
31555                 x : x,
31556                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31557             });
31558
31559             pos.push({
31560                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31561                 y : y
31562             });
31563             
31564             return pos;
31565             
31566         }
31567         
31568         pos.push({
31569             x : x,
31570             y : y
31571         });
31572
31573         pos.push({
31574             x : x + (this.unitWidth + this.gutter) * 2,
31575             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31576         });
31577         
31578         return pos;
31579         
31580     },
31581     
31582     getVerticalThreeBoxColPositions : function(x, y, box)
31583     {
31584         var pos = [];
31585         
31586         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31587             
31588             pos.push({
31589                 x : x,
31590                 y : y
31591             });
31592
31593             pos.push({
31594                 x : x + (this.unitWidth + this.gutter) * 1,
31595                 y : y
31596             });
31597             
31598             pos.push({
31599                 x : x + (this.unitWidth + this.gutter) * 2,
31600                 y : y
31601             });
31602             
31603             return pos;
31604             
31605         }
31606         
31607         if(box[0].size == 'xs' && box[1].size == 'xs'){
31608             
31609             pos.push({
31610                 x : x,
31611                 y : y
31612             });
31613
31614             pos.push({
31615                 x : x,
31616                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31617             });
31618             
31619             pos.push({
31620                 x : x + (this.unitWidth + this.gutter) * 1,
31621                 y : y
31622             });
31623             
31624             return pos;
31625             
31626         }
31627         
31628         pos.push({
31629             x : x,
31630             y : y
31631         });
31632
31633         pos.push({
31634             x : x + (this.unitWidth + this.gutter) * 2,
31635             y : y
31636         });
31637
31638         pos.push({
31639             x : x + (this.unitWidth + this.gutter) * 2,
31640             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31641         });
31642             
31643         return pos;
31644         
31645     },
31646     
31647     getVerticalFourBoxColPositions : function(x, y, box)
31648     {
31649         var pos = [];
31650         
31651         if(box[0].size == 'xs'){
31652             
31653             pos.push({
31654                 x : x,
31655                 y : y
31656             });
31657
31658             pos.push({
31659                 x : x,
31660                 y : y + (this.unitHeight + this.gutter) * 1
31661             });
31662             
31663             pos.push({
31664                 x : x,
31665                 y : y + (this.unitHeight + this.gutter) * 2
31666             });
31667             
31668             pos.push({
31669                 x : x + (this.unitWidth + this.gutter) * 1,
31670                 y : y
31671             });
31672             
31673             return pos;
31674             
31675         }
31676         
31677         pos.push({
31678             x : x,
31679             y : y
31680         });
31681
31682         pos.push({
31683             x : x + (this.unitWidth + this.gutter) * 2,
31684             y : y
31685         });
31686
31687         pos.push({
31688             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31689             y : y + (this.unitHeight + this.gutter) * 1
31690         });
31691
31692         pos.push({
31693             x : x + (this.unitWidth + this.gutter) * 2,
31694             y : y + (this.unitWidth + this.gutter) * 2
31695         });
31696
31697         return pos;
31698         
31699     },
31700     
31701     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31702     {
31703         var pos = [];
31704         
31705         if(box[0].size == 'md-left'){
31706             pos.push({
31707                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31708                 y : minY
31709             });
31710             
31711             return pos;
31712         }
31713         
31714         if(box[0].size == 'md-right'){
31715             pos.push({
31716                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31717                 y : minY + (this.unitWidth + this.gutter) * 1
31718             });
31719             
31720             return pos;
31721         }
31722         
31723         var rand = Math.floor(Math.random() * (4 - box[0].y));
31724         
31725         pos.push({
31726             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31727             y : minY + (this.unitWidth + this.gutter) * rand
31728         });
31729         
31730         return pos;
31731         
31732     },
31733     
31734     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31735     {
31736         var pos = [];
31737         
31738         if(box[0].size == 'xs'){
31739             
31740             pos.push({
31741                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31742                 y : minY
31743             });
31744
31745             pos.push({
31746                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31747                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31748             });
31749             
31750             return pos;
31751             
31752         }
31753         
31754         pos.push({
31755             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31756             y : minY
31757         });
31758
31759         pos.push({
31760             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31761             y : minY + (this.unitWidth + this.gutter) * 2
31762         });
31763         
31764         return pos;
31765         
31766     },
31767     
31768     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31769     {
31770         var pos = [];
31771         
31772         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31773             
31774             pos.push({
31775                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31776                 y : minY
31777             });
31778
31779             pos.push({
31780                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31781                 y : minY + (this.unitWidth + this.gutter) * 1
31782             });
31783             
31784             pos.push({
31785                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31786                 y : minY + (this.unitWidth + this.gutter) * 2
31787             });
31788             
31789             return pos;
31790             
31791         }
31792         
31793         if(box[0].size == 'xs' && box[1].size == 'xs'){
31794             
31795             pos.push({
31796                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31797                 y : minY
31798             });
31799
31800             pos.push({
31801                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31802                 y : minY
31803             });
31804             
31805             pos.push({
31806                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31807                 y : minY + (this.unitWidth + this.gutter) * 1
31808             });
31809             
31810             return pos;
31811             
31812         }
31813         
31814         pos.push({
31815             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31816             y : minY
31817         });
31818
31819         pos.push({
31820             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31821             y : minY + (this.unitWidth + this.gutter) * 2
31822         });
31823
31824         pos.push({
31825             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31826             y : minY + (this.unitWidth + this.gutter) * 2
31827         });
31828             
31829         return pos;
31830         
31831     },
31832     
31833     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31834     {
31835         var pos = [];
31836         
31837         if(box[0].size == 'xs'){
31838             
31839             pos.push({
31840                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31841                 y : minY
31842             });
31843
31844             pos.push({
31845                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31846                 y : minY
31847             });
31848             
31849             pos.push({
31850                 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),
31851                 y : minY
31852             });
31853             
31854             pos.push({
31855                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31856                 y : minY + (this.unitWidth + this.gutter) * 1
31857             });
31858             
31859             return pos;
31860             
31861         }
31862         
31863         pos.push({
31864             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31865             y : minY
31866         });
31867         
31868         pos.push({
31869             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31870             y : minY + (this.unitWidth + this.gutter) * 2
31871         });
31872         
31873         pos.push({
31874             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31875             y : minY + (this.unitWidth + this.gutter) * 2
31876         });
31877         
31878         pos.push({
31879             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),
31880             y : minY + (this.unitWidth + this.gutter) * 2
31881         });
31882
31883         return pos;
31884         
31885     },
31886     
31887     /**
31888     * remove a Masonry Brick
31889     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31890     */
31891     removeBrick : function(brick_id)
31892     {
31893         if (!brick_id) {
31894             return;
31895         }
31896         
31897         for (var i = 0; i<this.bricks.length; i++) {
31898             if (this.bricks[i].id == brick_id) {
31899                 this.bricks.splice(i,1);
31900                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31901                 this.initial();
31902             }
31903         }
31904     },
31905     
31906     /**
31907     * adds a Masonry Brick
31908     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31909     */
31910     addBrick : function(cfg)
31911     {
31912         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31913         //this.register(cn);
31914         cn.parentId = this.id;
31915         cn.onRender(this.el, null);
31916         return cn;
31917     },
31918     
31919     /**
31920     * register a Masonry Brick
31921     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31922     */
31923     
31924     register : function(brick)
31925     {
31926         this.bricks.push(brick);
31927         brick.masonryId = this.id;
31928     },
31929     
31930     /**
31931     * clear all the Masonry Brick
31932     */
31933     clearAll : function()
31934     {
31935         this.bricks = [];
31936         //this.getChildContainer().dom.innerHTML = "";
31937         this.el.dom.innerHTML = '';
31938     },
31939     
31940     getSelected : function()
31941     {
31942         if (!this.selectedBrick) {
31943             return false;
31944         }
31945         
31946         return this.selectedBrick;
31947     }
31948 });
31949
31950 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31951     
31952     groups: {},
31953      /**
31954     * register a Masonry Layout
31955     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31956     */
31957     
31958     register : function(layout)
31959     {
31960         this.groups[layout.id] = layout;
31961     },
31962     /**
31963     * fetch a  Masonry Layout based on the masonry layout ID
31964     * @param {string} the masonry layout to add
31965     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31966     */
31967     
31968     get: function(layout_id) {
31969         if (typeof(this.groups[layout_id]) == 'undefined') {
31970             return false;
31971         }
31972         return this.groups[layout_id] ;
31973     }
31974     
31975     
31976     
31977 });
31978
31979  
31980
31981  /**
31982  *
31983  * This is based on 
31984  * http://masonry.desandro.com
31985  *
31986  * The idea is to render all the bricks based on vertical width...
31987  *
31988  * The original code extends 'outlayer' - we might need to use that....
31989  * 
31990  */
31991
31992
31993 /**
31994  * @class Roo.bootstrap.LayoutMasonryAuto
31995  * @extends Roo.bootstrap.Component
31996  * Bootstrap Layout Masonry class
31997  * 
31998  * @constructor
31999  * Create a new Element
32000  * @param {Object} config The config object
32001  */
32002
32003 Roo.bootstrap.LayoutMasonryAuto = function(config){
32004     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32005 };
32006
32007 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32008     
32009       /**
32010      * @cfg {Boolean} isFitWidth  - resize the width..
32011      */   
32012     isFitWidth : false,  // options..
32013     /**
32014      * @cfg {Boolean} isOriginLeft = left align?
32015      */   
32016     isOriginLeft : true,
32017     /**
32018      * @cfg {Boolean} isOriginTop = top align?
32019      */   
32020     isOriginTop : false,
32021     /**
32022      * @cfg {Boolean} isLayoutInstant = no animation?
32023      */   
32024     isLayoutInstant : false, // needed?
32025     /**
32026      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32027      */   
32028     isResizingContainer : true,
32029     /**
32030      * @cfg {Number} columnWidth  width of the columns 
32031      */   
32032     
32033     columnWidth : 0,
32034     
32035     /**
32036      * @cfg {Number} maxCols maximum number of columns
32037      */   
32038     
32039     maxCols: 0,
32040     /**
32041      * @cfg {Number} padHeight padding below box..
32042      */   
32043     
32044     padHeight : 10, 
32045     
32046     /**
32047      * @cfg {Boolean} isAutoInitial defalut true
32048      */   
32049     
32050     isAutoInitial : true, 
32051     
32052     // private?
32053     gutter : 0,
32054     
32055     containerWidth: 0,
32056     initialColumnWidth : 0,
32057     currentSize : null,
32058     
32059     colYs : null, // array.
32060     maxY : 0,
32061     padWidth: 10,
32062     
32063     
32064     tag: 'div',
32065     cls: '',
32066     bricks: null, //CompositeElement
32067     cols : 0, // array?
32068     // element : null, // wrapped now this.el
32069     _isLayoutInited : null, 
32070     
32071     
32072     getAutoCreate : function(){
32073         
32074         var cfg = {
32075             tag: this.tag,
32076             cls: 'blog-masonary-wrapper ' + this.cls,
32077             cn : {
32078                 cls : 'mas-boxes masonary'
32079             }
32080         };
32081         
32082         return cfg;
32083     },
32084     
32085     getChildContainer: function( )
32086     {
32087         if (this.boxesEl) {
32088             return this.boxesEl;
32089         }
32090         
32091         this.boxesEl = this.el.select('.mas-boxes').first();
32092         
32093         return this.boxesEl;
32094     },
32095     
32096     
32097     initEvents : function()
32098     {
32099         var _this = this;
32100         
32101         if(this.isAutoInitial){
32102             Roo.log('hook children rendered');
32103             this.on('childrenrendered', function() {
32104                 Roo.log('children rendered');
32105                 _this.initial();
32106             } ,this);
32107         }
32108         
32109     },
32110     
32111     initial : function()
32112     {
32113         this.reloadItems();
32114
32115         this.currentSize = this.el.getBox(true);
32116
32117         /// was window resize... - let's see if this works..
32118         Roo.EventManager.onWindowResize(this.resize, this); 
32119
32120         if(!this.isAutoInitial){
32121             this.layout();
32122             return;
32123         }
32124         
32125         this.layout.defer(500,this);
32126     },
32127     
32128     reloadItems: function()
32129     {
32130         this.bricks = this.el.select('.masonry-brick', true);
32131         
32132         this.bricks.each(function(b) {
32133             //Roo.log(b.getSize());
32134             if (!b.attr('originalwidth')) {
32135                 b.attr('originalwidth',  b.getSize().width);
32136             }
32137             
32138         });
32139         
32140         Roo.log(this.bricks.elements.length);
32141     },
32142     
32143     resize : function()
32144     {
32145         Roo.log('resize');
32146         var cs = this.el.getBox(true);
32147         
32148         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32149             Roo.log("no change in with or X");
32150             return;
32151         }
32152         this.currentSize = cs;
32153         this.layout();
32154     },
32155     
32156     layout : function()
32157     {
32158          Roo.log('layout');
32159         this._resetLayout();
32160         //this._manageStamps();
32161       
32162         // don't animate first layout
32163         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32164         this.layoutItems( isInstant );
32165       
32166         // flag for initalized
32167         this._isLayoutInited = true;
32168     },
32169     
32170     layoutItems : function( isInstant )
32171     {
32172         //var items = this._getItemsForLayout( this.items );
32173         // original code supports filtering layout items.. we just ignore it..
32174         
32175         this._layoutItems( this.bricks , isInstant );
32176       
32177         this._postLayout();
32178     },
32179     _layoutItems : function ( items , isInstant)
32180     {
32181        //this.fireEvent( 'layout', this, items );
32182     
32183
32184         if ( !items || !items.elements.length ) {
32185           // no items, emit event with empty array
32186             return;
32187         }
32188
32189         var queue = [];
32190         items.each(function(item) {
32191             Roo.log("layout item");
32192             Roo.log(item);
32193             // get x/y object from method
32194             var position = this._getItemLayoutPosition( item );
32195             // enqueue
32196             position.item = item;
32197             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32198             queue.push( position );
32199         }, this);
32200       
32201         this._processLayoutQueue( queue );
32202     },
32203     /** Sets position of item in DOM
32204     * @param {Element} item
32205     * @param {Number} x - horizontal position
32206     * @param {Number} y - vertical position
32207     * @param {Boolean} isInstant - disables transitions
32208     */
32209     _processLayoutQueue : function( queue )
32210     {
32211         for ( var i=0, len = queue.length; i < len; i++ ) {
32212             var obj = queue[i];
32213             obj.item.position('absolute');
32214             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32215         }
32216     },
32217       
32218     
32219     /**
32220     * Any logic you want to do after each layout,
32221     * i.e. size the container
32222     */
32223     _postLayout : function()
32224     {
32225         this.resizeContainer();
32226     },
32227     
32228     resizeContainer : function()
32229     {
32230         if ( !this.isResizingContainer ) {
32231             return;
32232         }
32233         var size = this._getContainerSize();
32234         if ( size ) {
32235             this.el.setSize(size.width,size.height);
32236             this.boxesEl.setSize(size.width,size.height);
32237         }
32238     },
32239     
32240     
32241     
32242     _resetLayout : function()
32243     {
32244         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32245         this.colWidth = this.el.getWidth();
32246         //this.gutter = this.el.getWidth(); 
32247         
32248         this.measureColumns();
32249
32250         // reset column Y
32251         var i = this.cols;
32252         this.colYs = [];
32253         while (i--) {
32254             this.colYs.push( 0 );
32255         }
32256     
32257         this.maxY = 0;
32258     },
32259
32260     measureColumns : function()
32261     {
32262         this.getContainerWidth();
32263       // if columnWidth is 0, default to outerWidth of first item
32264         if ( !this.columnWidth ) {
32265             var firstItem = this.bricks.first();
32266             Roo.log(firstItem);
32267             this.columnWidth  = this.containerWidth;
32268             if (firstItem && firstItem.attr('originalwidth') ) {
32269                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32270             }
32271             // columnWidth fall back to item of first element
32272             Roo.log("set column width?");
32273                         this.initialColumnWidth = this.columnWidth  ;
32274
32275             // if first elem has no width, default to size of container
32276             
32277         }
32278         
32279         
32280         if (this.initialColumnWidth) {
32281             this.columnWidth = this.initialColumnWidth;
32282         }
32283         
32284         
32285             
32286         // column width is fixed at the top - however if container width get's smaller we should
32287         // reduce it...
32288         
32289         // this bit calcs how man columns..
32290             
32291         var columnWidth = this.columnWidth += this.gutter;
32292       
32293         // calculate columns
32294         var containerWidth = this.containerWidth + this.gutter;
32295         
32296         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32297         // fix rounding errors, typically with gutters
32298         var excess = columnWidth - containerWidth % columnWidth;
32299         
32300         
32301         // if overshoot is less than a pixel, round up, otherwise floor it
32302         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32303         cols = Math[ mathMethod ]( cols );
32304         this.cols = Math.max( cols, 1 );
32305         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32306         
32307          // padding positioning..
32308         var totalColWidth = this.cols * this.columnWidth;
32309         var padavail = this.containerWidth - totalColWidth;
32310         // so for 2 columns - we need 3 'pads'
32311         
32312         var padNeeded = (1+this.cols) * this.padWidth;
32313         
32314         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32315         
32316         this.columnWidth += padExtra
32317         //this.padWidth = Math.floor(padavail /  ( this.cols));
32318         
32319         // adjust colum width so that padding is fixed??
32320         
32321         // we have 3 columns ... total = width * 3
32322         // we have X left over... that should be used by 
32323         
32324         //if (this.expandC) {
32325             
32326         //}
32327         
32328         
32329         
32330     },
32331     
32332     getContainerWidth : function()
32333     {
32334        /* // container is parent if fit width
32335         var container = this.isFitWidth ? this.element.parentNode : this.element;
32336         // check that this.size and size are there
32337         // IE8 triggers resize on body size change, so they might not be
32338         
32339         var size = getSize( container );  //FIXME
32340         this.containerWidth = size && size.innerWidth; //FIXME
32341         */
32342          
32343         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32344         
32345     },
32346     
32347     _getItemLayoutPosition : function( item )  // what is item?
32348     {
32349         // we resize the item to our columnWidth..
32350       
32351         item.setWidth(this.columnWidth);
32352         item.autoBoxAdjust  = false;
32353         
32354         var sz = item.getSize();
32355  
32356         // how many columns does this brick span
32357         var remainder = this.containerWidth % this.columnWidth;
32358         
32359         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32360         // round if off by 1 pixel, otherwise use ceil
32361         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32362         colSpan = Math.min( colSpan, this.cols );
32363         
32364         // normally this should be '1' as we dont' currently allow multi width columns..
32365         
32366         var colGroup = this._getColGroup( colSpan );
32367         // get the minimum Y value from the columns
32368         var minimumY = Math.min.apply( Math, colGroup );
32369         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32370         
32371         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32372          
32373         // position the brick
32374         var position = {
32375             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32376             y: this.currentSize.y + minimumY + this.padHeight
32377         };
32378         
32379         Roo.log(position);
32380         // apply setHeight to necessary columns
32381         var setHeight = minimumY + sz.height + this.padHeight;
32382         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32383         
32384         var setSpan = this.cols + 1 - colGroup.length;
32385         for ( var i = 0; i < setSpan; i++ ) {
32386           this.colYs[ shortColIndex + i ] = setHeight ;
32387         }
32388       
32389         return position;
32390     },
32391     
32392     /**
32393      * @param {Number} colSpan - number of columns the element spans
32394      * @returns {Array} colGroup
32395      */
32396     _getColGroup : function( colSpan )
32397     {
32398         if ( colSpan < 2 ) {
32399           // if brick spans only one column, use all the column Ys
32400           return this.colYs;
32401         }
32402       
32403         var colGroup = [];
32404         // how many different places could this brick fit horizontally
32405         var groupCount = this.cols + 1 - colSpan;
32406         // for each group potential horizontal position
32407         for ( var i = 0; i < groupCount; i++ ) {
32408           // make an array of colY values for that one group
32409           var groupColYs = this.colYs.slice( i, i + colSpan );
32410           // and get the max value of the array
32411           colGroup[i] = Math.max.apply( Math, groupColYs );
32412         }
32413         return colGroup;
32414     },
32415     /*
32416     _manageStamp : function( stamp )
32417     {
32418         var stampSize =  stamp.getSize();
32419         var offset = stamp.getBox();
32420         // get the columns that this stamp affects
32421         var firstX = this.isOriginLeft ? offset.x : offset.right;
32422         var lastX = firstX + stampSize.width;
32423         var firstCol = Math.floor( firstX / this.columnWidth );
32424         firstCol = Math.max( 0, firstCol );
32425         
32426         var lastCol = Math.floor( lastX / this.columnWidth );
32427         // lastCol should not go over if multiple of columnWidth #425
32428         lastCol -= lastX % this.columnWidth ? 0 : 1;
32429         lastCol = Math.min( this.cols - 1, lastCol );
32430         
32431         // set colYs to bottom of the stamp
32432         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32433             stampSize.height;
32434             
32435         for ( var i = firstCol; i <= lastCol; i++ ) {
32436           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32437         }
32438     },
32439     */
32440     
32441     _getContainerSize : function()
32442     {
32443         this.maxY = Math.max.apply( Math, this.colYs );
32444         var size = {
32445             height: this.maxY
32446         };
32447       
32448         if ( this.isFitWidth ) {
32449             size.width = this._getContainerFitWidth();
32450         }
32451       
32452         return size;
32453     },
32454     
32455     _getContainerFitWidth : function()
32456     {
32457         var unusedCols = 0;
32458         // count unused columns
32459         var i = this.cols;
32460         while ( --i ) {
32461           if ( this.colYs[i] !== 0 ) {
32462             break;
32463           }
32464           unusedCols++;
32465         }
32466         // fit container to columns that have been used
32467         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32468     },
32469     
32470     needsResizeLayout : function()
32471     {
32472         var previousWidth = this.containerWidth;
32473         this.getContainerWidth();
32474         return previousWidth !== this.containerWidth;
32475     }
32476  
32477 });
32478
32479  
32480
32481  /*
32482  * - LGPL
32483  *
32484  * element
32485  * 
32486  */
32487
32488 /**
32489  * @class Roo.bootstrap.MasonryBrick
32490  * @extends Roo.bootstrap.Component
32491  * Bootstrap MasonryBrick class
32492  * 
32493  * @constructor
32494  * Create a new MasonryBrick
32495  * @param {Object} config The config object
32496  */
32497
32498 Roo.bootstrap.MasonryBrick = function(config){
32499     
32500     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32501     
32502     Roo.bootstrap.MasonryBrick.register(this);
32503     
32504     this.addEvents({
32505         // raw events
32506         /**
32507          * @event click
32508          * When a MasonryBrick is clcik
32509          * @param {Roo.bootstrap.MasonryBrick} this
32510          * @param {Roo.EventObject} e
32511          */
32512         "click" : true
32513     });
32514 };
32515
32516 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32517     
32518     /**
32519      * @cfg {String} title
32520      */   
32521     title : '',
32522     /**
32523      * @cfg {String} html
32524      */   
32525     html : '',
32526     /**
32527      * @cfg {String} bgimage
32528      */   
32529     bgimage : '',
32530     /**
32531      * @cfg {String} videourl
32532      */   
32533     videourl : '',
32534     /**
32535      * @cfg {String} cls
32536      */   
32537     cls : '',
32538     /**
32539      * @cfg {String} href
32540      */   
32541     href : '',
32542     /**
32543      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32544      */   
32545     size : 'xs',
32546     
32547     /**
32548      * @cfg {String} placetitle (center|bottom)
32549      */   
32550     placetitle : '',
32551     
32552     /**
32553      * @cfg {Boolean} isFitContainer defalut true
32554      */   
32555     isFitContainer : true, 
32556     
32557     /**
32558      * @cfg {Boolean} preventDefault defalut false
32559      */   
32560     preventDefault : false, 
32561     
32562     /**
32563      * @cfg {Boolean} inverse defalut false
32564      */   
32565     maskInverse : false, 
32566     
32567     getAutoCreate : function()
32568     {
32569         if(!this.isFitContainer){
32570             return this.getSplitAutoCreate();
32571         }
32572         
32573         var cls = 'masonry-brick masonry-brick-full';
32574         
32575         if(this.href.length){
32576             cls += ' masonry-brick-link';
32577         }
32578         
32579         if(this.bgimage.length){
32580             cls += ' masonry-brick-image';
32581         }
32582         
32583         if(this.maskInverse){
32584             cls += ' mask-inverse';
32585         }
32586         
32587         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32588             cls += ' enable-mask';
32589         }
32590         
32591         if(this.size){
32592             cls += ' masonry-' + this.size + '-brick';
32593         }
32594         
32595         if(this.placetitle.length){
32596             
32597             switch (this.placetitle) {
32598                 case 'center' :
32599                     cls += ' masonry-center-title';
32600                     break;
32601                 case 'bottom' :
32602                     cls += ' masonry-bottom-title';
32603                     break;
32604                 default:
32605                     break;
32606             }
32607             
32608         } else {
32609             if(!this.html.length && !this.bgimage.length){
32610                 cls += ' masonry-center-title';
32611             }
32612
32613             if(!this.html.length && this.bgimage.length){
32614                 cls += ' masonry-bottom-title';
32615             }
32616         }
32617         
32618         if(this.cls){
32619             cls += ' ' + this.cls;
32620         }
32621         
32622         var cfg = {
32623             tag: (this.href.length) ? 'a' : 'div',
32624             cls: cls,
32625             cn: [
32626                 {
32627                     tag: 'div',
32628                     cls: 'masonry-brick-mask'
32629                 },
32630                 {
32631                     tag: 'div',
32632                     cls: 'masonry-brick-paragraph',
32633                     cn: []
32634                 }
32635             ]
32636         };
32637         
32638         if(this.href.length){
32639             cfg.href = this.href;
32640         }
32641         
32642         var cn = cfg.cn[1].cn;
32643         
32644         if(this.title.length){
32645             cn.push({
32646                 tag: 'h4',
32647                 cls: 'masonry-brick-title',
32648                 html: this.title
32649             });
32650         }
32651         
32652         if(this.html.length){
32653             cn.push({
32654                 tag: 'p',
32655                 cls: 'masonry-brick-text',
32656                 html: this.html
32657             });
32658         }
32659         
32660         if (!this.title.length && !this.html.length) {
32661             cfg.cn[1].cls += ' hide';
32662         }
32663         
32664         if(this.bgimage.length){
32665             cfg.cn.push({
32666                 tag: 'img',
32667                 cls: 'masonry-brick-image-view',
32668                 src: this.bgimage
32669             });
32670         }
32671         
32672         if(this.videourl.length){
32673             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32674             // youtube support only?
32675             cfg.cn.push({
32676                 tag: 'iframe',
32677                 cls: 'masonry-brick-image-view',
32678                 src: vurl,
32679                 frameborder : 0,
32680                 allowfullscreen : true
32681             });
32682         }
32683         
32684         return cfg;
32685         
32686     },
32687     
32688     getSplitAutoCreate : function()
32689     {
32690         var cls = 'masonry-brick masonry-brick-split';
32691         
32692         if(this.href.length){
32693             cls += ' masonry-brick-link';
32694         }
32695         
32696         if(this.bgimage.length){
32697             cls += ' masonry-brick-image';
32698         }
32699         
32700         if(this.size){
32701             cls += ' masonry-' + this.size + '-brick';
32702         }
32703         
32704         switch (this.placetitle) {
32705             case 'center' :
32706                 cls += ' masonry-center-title';
32707                 break;
32708             case 'bottom' :
32709                 cls += ' masonry-bottom-title';
32710                 break;
32711             default:
32712                 if(!this.bgimage.length){
32713                     cls += ' masonry-center-title';
32714                 }
32715
32716                 if(this.bgimage.length){
32717                     cls += ' masonry-bottom-title';
32718                 }
32719                 break;
32720         }
32721         
32722         if(this.cls){
32723             cls += ' ' + this.cls;
32724         }
32725         
32726         var cfg = {
32727             tag: (this.href.length) ? 'a' : 'div',
32728             cls: cls,
32729             cn: [
32730                 {
32731                     tag: 'div',
32732                     cls: 'masonry-brick-split-head',
32733                     cn: [
32734                         {
32735                             tag: 'div',
32736                             cls: 'masonry-brick-paragraph',
32737                             cn: []
32738                         }
32739                     ]
32740                 },
32741                 {
32742                     tag: 'div',
32743                     cls: 'masonry-brick-split-body',
32744                     cn: []
32745                 }
32746             ]
32747         };
32748         
32749         if(this.href.length){
32750             cfg.href = this.href;
32751         }
32752         
32753         if(this.title.length){
32754             cfg.cn[0].cn[0].cn.push({
32755                 tag: 'h4',
32756                 cls: 'masonry-brick-title',
32757                 html: this.title
32758             });
32759         }
32760         
32761         if(this.html.length){
32762             cfg.cn[1].cn.push({
32763                 tag: 'p',
32764                 cls: 'masonry-brick-text',
32765                 html: this.html
32766             });
32767         }
32768
32769         if(this.bgimage.length){
32770             cfg.cn[0].cn.push({
32771                 tag: 'img',
32772                 cls: 'masonry-brick-image-view',
32773                 src: this.bgimage
32774             });
32775         }
32776         
32777         if(this.videourl.length){
32778             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32779             // youtube support only?
32780             cfg.cn[0].cn.cn.push({
32781                 tag: 'iframe',
32782                 cls: 'masonry-brick-image-view',
32783                 src: vurl,
32784                 frameborder : 0,
32785                 allowfullscreen : true
32786             });
32787         }
32788         
32789         return cfg;
32790     },
32791     
32792     initEvents: function() 
32793     {
32794         switch (this.size) {
32795             case 'xs' :
32796                 this.x = 1;
32797                 this.y = 1;
32798                 break;
32799             case 'sm' :
32800                 this.x = 2;
32801                 this.y = 2;
32802                 break;
32803             case 'md' :
32804             case 'md-left' :
32805             case 'md-right' :
32806                 this.x = 3;
32807                 this.y = 3;
32808                 break;
32809             case 'tall' :
32810                 this.x = 2;
32811                 this.y = 3;
32812                 break;
32813             case 'wide' :
32814                 this.x = 3;
32815                 this.y = 2;
32816                 break;
32817             case 'wide-thin' :
32818                 this.x = 3;
32819                 this.y = 1;
32820                 break;
32821                         
32822             default :
32823                 break;
32824         }
32825         
32826         if(Roo.isTouch){
32827             this.el.on('touchstart', this.onTouchStart, this);
32828             this.el.on('touchmove', this.onTouchMove, this);
32829             this.el.on('touchend', this.onTouchEnd, this);
32830             this.el.on('contextmenu', this.onContextMenu, this);
32831         } else {
32832             this.el.on('mouseenter'  ,this.enter, this);
32833             this.el.on('mouseleave', this.leave, this);
32834             this.el.on('click', this.onClick, this);
32835         }
32836         
32837         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32838             this.parent().bricks.push(this);   
32839         }
32840         
32841     },
32842     
32843     onClick: function(e, el)
32844     {
32845         var time = this.endTimer - this.startTimer;
32846         // Roo.log(e.preventDefault());
32847         if(Roo.isTouch){
32848             if(time > 1000){
32849                 e.preventDefault();
32850                 return;
32851             }
32852         }
32853         
32854         if(!this.preventDefault){
32855             return;
32856         }
32857         
32858         e.preventDefault();
32859         
32860         if (this.activeClass != '') {
32861             this.selectBrick();
32862         }
32863         
32864         this.fireEvent('click', this, e);
32865     },
32866     
32867     enter: function(e, el)
32868     {
32869         e.preventDefault();
32870         
32871         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32872             return;
32873         }
32874         
32875         if(this.bgimage.length && this.html.length){
32876             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32877         }
32878     },
32879     
32880     leave: function(e, el)
32881     {
32882         e.preventDefault();
32883         
32884         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32885             return;
32886         }
32887         
32888         if(this.bgimage.length && this.html.length){
32889             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32890         }
32891     },
32892     
32893     onTouchStart: function(e, el)
32894     {
32895 //        e.preventDefault();
32896         
32897         this.touchmoved = false;
32898         
32899         if(!this.isFitContainer){
32900             return;
32901         }
32902         
32903         if(!this.bgimage.length || !this.html.length){
32904             return;
32905         }
32906         
32907         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32908         
32909         this.timer = new Date().getTime();
32910         
32911     },
32912     
32913     onTouchMove: function(e, el)
32914     {
32915         this.touchmoved = true;
32916     },
32917     
32918     onContextMenu : function(e,el)
32919     {
32920         e.preventDefault();
32921         e.stopPropagation();
32922         return false;
32923     },
32924     
32925     onTouchEnd: function(e, el)
32926     {
32927 //        e.preventDefault();
32928         
32929         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32930         
32931             this.leave(e,el);
32932             
32933             return;
32934         }
32935         
32936         if(!this.bgimage.length || !this.html.length){
32937             
32938             if(this.href.length){
32939                 window.location.href = this.href;
32940             }
32941             
32942             return;
32943         }
32944         
32945         if(!this.isFitContainer){
32946             return;
32947         }
32948         
32949         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32950         
32951         window.location.href = this.href;
32952     },
32953     
32954     //selection on single brick only
32955     selectBrick : function() {
32956         
32957         if (!this.parentId) {
32958             return;
32959         }
32960         
32961         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32962         var index = m.selectedBrick.indexOf(this.id);
32963         
32964         if ( index > -1) {
32965             m.selectedBrick.splice(index,1);
32966             this.el.removeClass(this.activeClass);
32967             return;
32968         }
32969         
32970         for(var i = 0; i < m.selectedBrick.length; i++) {
32971             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32972             b.el.removeClass(b.activeClass);
32973         }
32974         
32975         m.selectedBrick = [];
32976         
32977         m.selectedBrick.push(this.id);
32978         this.el.addClass(this.activeClass);
32979         return;
32980     },
32981     
32982     isSelected : function(){
32983         return this.el.hasClass(this.activeClass);
32984         
32985     }
32986 });
32987
32988 Roo.apply(Roo.bootstrap.MasonryBrick, {
32989     
32990     //groups: {},
32991     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32992      /**
32993     * register a Masonry Brick
32994     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32995     */
32996     
32997     register : function(brick)
32998     {
32999         //this.groups[brick.id] = brick;
33000         this.groups.add(brick.id, brick);
33001     },
33002     /**
33003     * fetch a  masonry brick based on the masonry brick ID
33004     * @param {string} the masonry brick to add
33005     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33006     */
33007     
33008     get: function(brick_id) 
33009     {
33010         // if (typeof(this.groups[brick_id]) == 'undefined') {
33011         //     return false;
33012         // }
33013         // return this.groups[brick_id] ;
33014         
33015         if(this.groups.key(brick_id)) {
33016             return this.groups.key(brick_id);
33017         }
33018         
33019         return false;
33020     }
33021     
33022     
33023     
33024 });
33025
33026  /*
33027  * - LGPL
33028  *
33029  * element
33030  * 
33031  */
33032
33033 /**
33034  * @class Roo.bootstrap.Brick
33035  * @extends Roo.bootstrap.Component
33036  * Bootstrap Brick class
33037  * 
33038  * @constructor
33039  * Create a new Brick
33040  * @param {Object} config The config object
33041  */
33042
33043 Roo.bootstrap.Brick = function(config){
33044     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33045     
33046     this.addEvents({
33047         // raw events
33048         /**
33049          * @event click
33050          * When a Brick is click
33051          * @param {Roo.bootstrap.Brick} this
33052          * @param {Roo.EventObject} e
33053          */
33054         "click" : true
33055     });
33056 };
33057
33058 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33059     
33060     /**
33061      * @cfg {String} title
33062      */   
33063     title : '',
33064     /**
33065      * @cfg {String} html
33066      */   
33067     html : '',
33068     /**
33069      * @cfg {String} bgimage
33070      */   
33071     bgimage : '',
33072     /**
33073      * @cfg {String} cls
33074      */   
33075     cls : '',
33076     /**
33077      * @cfg {String} href
33078      */   
33079     href : '',
33080     /**
33081      * @cfg {String} video
33082      */   
33083     video : '',
33084     /**
33085      * @cfg {Boolean} square
33086      */   
33087     square : true,
33088     
33089     getAutoCreate : function()
33090     {
33091         var cls = 'roo-brick';
33092         
33093         if(this.href.length){
33094             cls += ' roo-brick-link';
33095         }
33096         
33097         if(this.bgimage.length){
33098             cls += ' roo-brick-image';
33099         }
33100         
33101         if(!this.html.length && !this.bgimage.length){
33102             cls += ' roo-brick-center-title';
33103         }
33104         
33105         if(!this.html.length && this.bgimage.length){
33106             cls += ' roo-brick-bottom-title';
33107         }
33108         
33109         if(this.cls){
33110             cls += ' ' + this.cls;
33111         }
33112         
33113         var cfg = {
33114             tag: (this.href.length) ? 'a' : 'div',
33115             cls: cls,
33116             cn: [
33117                 {
33118                     tag: 'div',
33119                     cls: 'roo-brick-paragraph',
33120                     cn: []
33121                 }
33122             ]
33123         };
33124         
33125         if(this.href.length){
33126             cfg.href = this.href;
33127         }
33128         
33129         var cn = cfg.cn[0].cn;
33130         
33131         if(this.title.length){
33132             cn.push({
33133                 tag: 'h4',
33134                 cls: 'roo-brick-title',
33135                 html: this.title
33136             });
33137         }
33138         
33139         if(this.html.length){
33140             cn.push({
33141                 tag: 'p',
33142                 cls: 'roo-brick-text',
33143                 html: this.html
33144             });
33145         } else {
33146             cn.cls += ' hide';
33147         }
33148         
33149         if(this.bgimage.length){
33150             cfg.cn.push({
33151                 tag: 'img',
33152                 cls: 'roo-brick-image-view',
33153                 src: this.bgimage
33154             });
33155         }
33156         
33157         return cfg;
33158     },
33159     
33160     initEvents: function() 
33161     {
33162         if(this.title.length || this.html.length){
33163             this.el.on('mouseenter'  ,this.enter, this);
33164             this.el.on('mouseleave', this.leave, this);
33165         }
33166         
33167         Roo.EventManager.onWindowResize(this.resize, this); 
33168         
33169         if(this.bgimage.length){
33170             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33171             this.imageEl.on('load', this.onImageLoad, this);
33172             return;
33173         }
33174         
33175         this.resize();
33176     },
33177     
33178     onImageLoad : function()
33179     {
33180         this.resize();
33181     },
33182     
33183     resize : function()
33184     {
33185         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33186         
33187         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33188         
33189         if(this.bgimage.length){
33190             var image = this.el.select('.roo-brick-image-view', true).first();
33191             
33192             image.setWidth(paragraph.getWidth());
33193             
33194             if(this.square){
33195                 image.setHeight(paragraph.getWidth());
33196             }
33197             
33198             this.el.setHeight(image.getHeight());
33199             paragraph.setHeight(image.getHeight());
33200             
33201         }
33202         
33203     },
33204     
33205     enter: function(e, el)
33206     {
33207         e.preventDefault();
33208         
33209         if(this.bgimage.length){
33210             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33211             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33212         }
33213     },
33214     
33215     leave: function(e, el)
33216     {
33217         e.preventDefault();
33218         
33219         if(this.bgimage.length){
33220             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33221             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33222         }
33223     }
33224     
33225 });
33226
33227  
33228
33229  /*
33230  * - LGPL
33231  *
33232  * Number field 
33233  */
33234
33235 /**
33236  * @class Roo.bootstrap.NumberField
33237  * @extends Roo.bootstrap.Input
33238  * Bootstrap NumberField class
33239  * 
33240  * 
33241  * 
33242  * 
33243  * @constructor
33244  * Create a new NumberField
33245  * @param {Object} config The config object
33246  */
33247
33248 Roo.bootstrap.NumberField = function(config){
33249     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33250 };
33251
33252 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33253     
33254     /**
33255      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33256      */
33257     allowDecimals : true,
33258     /**
33259      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33260      */
33261     decimalSeparator : ".",
33262     /**
33263      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33264      */
33265     decimalPrecision : 2,
33266     /**
33267      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33268      */
33269     allowNegative : true,
33270     
33271     /**
33272      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33273      */
33274     allowZero: true,
33275     /**
33276      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33277      */
33278     minValue : Number.NEGATIVE_INFINITY,
33279     /**
33280      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33281      */
33282     maxValue : Number.MAX_VALUE,
33283     /**
33284      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33285      */
33286     minText : "The minimum value for this field is {0}",
33287     /**
33288      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33289      */
33290     maxText : "The maximum value for this field is {0}",
33291     /**
33292      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33293      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33294      */
33295     nanText : "{0} is not a valid number",
33296     /**
33297      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33298      */
33299     thousandsDelimiter : false,
33300     /**
33301      * @cfg {String} valueAlign alignment of value
33302      */
33303     valueAlign : "left",
33304
33305     getAutoCreate : function()
33306     {
33307         var hiddenInput = {
33308             tag: 'input',
33309             type: 'hidden',
33310             id: Roo.id(),
33311             cls: 'hidden-number-input'
33312         };
33313         
33314         if (this.name) {
33315             hiddenInput.name = this.name;
33316         }
33317         
33318         this.name = '';
33319         
33320         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33321         
33322         this.name = hiddenInput.name;
33323         
33324         if(cfg.cn.length > 0) {
33325             cfg.cn.push(hiddenInput);
33326         }
33327         
33328         return cfg;
33329     },
33330
33331     // private
33332     initEvents : function()
33333     {   
33334         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33335         
33336         var allowed = "0123456789";
33337         
33338         if(this.allowDecimals){
33339             allowed += this.decimalSeparator;
33340         }
33341         
33342         if(this.allowNegative){
33343             allowed += "-";
33344         }
33345         
33346         if(this.thousandsDelimiter) {
33347             allowed += ",";
33348         }
33349         
33350         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33351         
33352         var keyPress = function(e){
33353             
33354             var k = e.getKey();
33355             
33356             var c = e.getCharCode();
33357             
33358             if(
33359                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33360                     allowed.indexOf(String.fromCharCode(c)) === -1
33361             ){
33362                 e.stopEvent();
33363                 return;
33364             }
33365             
33366             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33367                 return;
33368             }
33369             
33370             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33371                 e.stopEvent();
33372             }
33373         };
33374         
33375         this.el.on("keypress", keyPress, this);
33376     },
33377     
33378     validateValue : function(value)
33379     {
33380         
33381         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33382             return false;
33383         }
33384         
33385         var num = this.parseValue(value);
33386         
33387         if(isNaN(num)){
33388             this.markInvalid(String.format(this.nanText, value));
33389             return false;
33390         }
33391         
33392         if(num < this.minValue){
33393             this.markInvalid(String.format(this.minText, this.minValue));
33394             return false;
33395         }
33396         
33397         if(num > this.maxValue){
33398             this.markInvalid(String.format(this.maxText, this.maxValue));
33399             return false;
33400         }
33401         
33402         return true;
33403     },
33404
33405     getValue : function()
33406     {
33407         var v = this.hiddenEl().getValue();
33408         
33409         return this.fixPrecision(this.parseValue(v));
33410     },
33411
33412     parseValue : function(value)
33413     {
33414         if(this.thousandsDelimiter) {
33415             value += "";
33416             r = new RegExp(",", "g");
33417             value = value.replace(r, "");
33418         }
33419         
33420         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33421         return isNaN(value) ? '' : value;
33422     },
33423
33424     fixPrecision : function(value)
33425     {
33426         if(this.thousandsDelimiter) {
33427             value += "";
33428             r = new RegExp(",", "g");
33429             value = value.replace(r, "");
33430         }
33431         
33432         var nan = isNaN(value);
33433         
33434         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33435             return nan ? '' : value;
33436         }
33437         return parseFloat(value).toFixed(this.decimalPrecision);
33438     },
33439
33440     setValue : function(v)
33441     {
33442         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33443         
33444         this.value = v;
33445         
33446         if(this.rendered){
33447             
33448             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33449             
33450             this.inputEl().dom.value = (v == '') ? '' :
33451                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33452             
33453             if(!this.allowZero && v === '0') {
33454                 this.hiddenEl().dom.value = '';
33455                 this.inputEl().dom.value = '';
33456             }
33457             
33458             this.validate();
33459         }
33460     },
33461
33462     decimalPrecisionFcn : function(v)
33463     {
33464         return Math.floor(v);
33465     },
33466
33467     beforeBlur : function()
33468     {
33469         var v = this.parseValue(this.getRawValue());
33470         
33471         if(v || v === 0 || v === ''){
33472             this.setValue(v);
33473         }
33474     },
33475     
33476     hiddenEl : function()
33477     {
33478         return this.el.select('input.hidden-number-input',true).first();
33479     }
33480     
33481 });
33482
33483  
33484
33485 /*
33486 * Licence: LGPL
33487 */
33488
33489 /**
33490  * @class Roo.bootstrap.DocumentSlider
33491  * @extends Roo.bootstrap.Component
33492  * Bootstrap DocumentSlider class
33493  * 
33494  * @constructor
33495  * Create a new DocumentViewer
33496  * @param {Object} config The config object
33497  */
33498
33499 Roo.bootstrap.DocumentSlider = function(config){
33500     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33501     
33502     this.files = [];
33503     
33504     this.addEvents({
33505         /**
33506          * @event initial
33507          * Fire after initEvent
33508          * @param {Roo.bootstrap.DocumentSlider} this
33509          */
33510         "initial" : true,
33511         /**
33512          * @event update
33513          * Fire after update
33514          * @param {Roo.bootstrap.DocumentSlider} this
33515          */
33516         "update" : true,
33517         /**
33518          * @event click
33519          * Fire after click
33520          * @param {Roo.bootstrap.DocumentSlider} this
33521          */
33522         "click" : true
33523     });
33524 };
33525
33526 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33527     
33528     files : false,
33529     
33530     indicator : 0,
33531     
33532     getAutoCreate : function()
33533     {
33534         var cfg = {
33535             tag : 'div',
33536             cls : 'roo-document-slider',
33537             cn : [
33538                 {
33539                     tag : 'div',
33540                     cls : 'roo-document-slider-header',
33541                     cn : [
33542                         {
33543                             tag : 'div',
33544                             cls : 'roo-document-slider-header-title'
33545                         }
33546                     ]
33547                 },
33548                 {
33549                     tag : 'div',
33550                     cls : 'roo-document-slider-body',
33551                     cn : [
33552                         {
33553                             tag : 'div',
33554                             cls : 'roo-document-slider-prev',
33555                             cn : [
33556                                 {
33557                                     tag : 'i',
33558                                     cls : 'fa fa-chevron-left'
33559                                 }
33560                             ]
33561                         },
33562                         {
33563                             tag : 'div',
33564                             cls : 'roo-document-slider-thumb',
33565                             cn : [
33566                                 {
33567                                     tag : 'img',
33568                                     cls : 'roo-document-slider-image'
33569                                 }
33570                             ]
33571                         },
33572                         {
33573                             tag : 'div',
33574                             cls : 'roo-document-slider-next',
33575                             cn : [
33576                                 {
33577                                     tag : 'i',
33578                                     cls : 'fa fa-chevron-right'
33579                                 }
33580                             ]
33581                         }
33582                     ]
33583                 }
33584             ]
33585         };
33586         
33587         return cfg;
33588     },
33589     
33590     initEvents : function()
33591     {
33592         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33593         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33594         
33595         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33596         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33597         
33598         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33599         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33600         
33601         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33602         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33603         
33604         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33605         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33606         
33607         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33608         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33609         
33610         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33611         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33612         
33613         this.thumbEl.on('click', this.onClick, this);
33614         
33615         this.prevIndicator.on('click', this.prev, this);
33616         
33617         this.nextIndicator.on('click', this.next, this);
33618         
33619     },
33620     
33621     initial : function()
33622     {
33623         if(this.files.length){
33624             this.indicator = 1;
33625             this.update()
33626         }
33627         
33628         this.fireEvent('initial', this);
33629     },
33630     
33631     update : function()
33632     {
33633         this.imageEl.attr('src', this.files[this.indicator - 1]);
33634         
33635         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33636         
33637         this.prevIndicator.show();
33638         
33639         if(this.indicator == 1){
33640             this.prevIndicator.hide();
33641         }
33642         
33643         this.nextIndicator.show();
33644         
33645         if(this.indicator == this.files.length){
33646             this.nextIndicator.hide();
33647         }
33648         
33649         this.thumbEl.scrollTo('top');
33650         
33651         this.fireEvent('update', this);
33652     },
33653     
33654     onClick : function(e)
33655     {
33656         e.preventDefault();
33657         
33658         this.fireEvent('click', this);
33659     },
33660     
33661     prev : function(e)
33662     {
33663         e.preventDefault();
33664         
33665         this.indicator = Math.max(1, this.indicator - 1);
33666         
33667         this.update();
33668     },
33669     
33670     next : function(e)
33671     {
33672         e.preventDefault();
33673         
33674         this.indicator = Math.min(this.files.length, this.indicator + 1);
33675         
33676         this.update();
33677     }
33678 });
33679 /*
33680  * - LGPL
33681  *
33682  * RadioSet
33683  *
33684  *
33685  */
33686
33687 /**
33688  * @class Roo.bootstrap.RadioSet
33689  * @extends Roo.bootstrap.Input
33690  * Bootstrap RadioSet class
33691  * @cfg {String} indicatorpos (left|right) default left
33692  * @cfg {Boolean} inline (true|false) inline the element (default true)
33693  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33694  * @constructor
33695  * Create a new RadioSet
33696  * @param {Object} config The config object
33697  */
33698
33699 Roo.bootstrap.RadioSet = function(config){
33700     
33701     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33702     
33703     this.radioes = [];
33704     
33705     Roo.bootstrap.RadioSet.register(this);
33706     
33707     this.addEvents({
33708         /**
33709         * @event check
33710         * Fires when the element is checked or unchecked.
33711         * @param {Roo.bootstrap.RadioSet} this This radio
33712         * @param {Roo.bootstrap.Radio} item The checked item
33713         */
33714        check : true,
33715        /**
33716         * @event click
33717         * Fires when the element is click.
33718         * @param {Roo.bootstrap.RadioSet} this This radio set
33719         * @param {Roo.bootstrap.Radio} item The checked item
33720         * @param {Roo.EventObject} e The event object
33721         */
33722        click : true
33723     });
33724     
33725 };
33726
33727 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33728
33729     radioes : false,
33730     
33731     inline : true,
33732     
33733     weight : '',
33734     
33735     indicatorpos : 'left',
33736     
33737     getAutoCreate : function()
33738     {
33739         var label = {
33740             tag : 'label',
33741             cls : 'roo-radio-set-label',
33742             cn : [
33743                 {
33744                     tag : 'span',
33745                     html : this.fieldLabel
33746                 }
33747             ]
33748         };
33749         
33750         if(this.indicatorpos == 'left'){
33751             label.cn.unshift({
33752                 tag : 'i',
33753                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33754                 tooltip : 'This field is required'
33755             });
33756         } else {
33757             label.cn.push({
33758                 tag : 'i',
33759                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33760                 tooltip : 'This field is required'
33761             });
33762         }
33763         
33764         var items = {
33765             tag : 'div',
33766             cls : 'roo-radio-set-items'
33767         };
33768         
33769         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33770         
33771         if (align === 'left' && this.fieldLabel.length) {
33772             
33773             items = {
33774                 cls : "roo-radio-set-right", 
33775                 cn: [
33776                     items
33777                 ]
33778             };
33779             
33780             if(this.labelWidth > 12){
33781                 label.style = "width: " + this.labelWidth + 'px';
33782             }
33783             
33784             if(this.labelWidth < 13 && this.labelmd == 0){
33785                 this.labelmd = this.labelWidth;
33786             }
33787             
33788             if(this.labellg > 0){
33789                 label.cls += ' col-lg-' + this.labellg;
33790                 items.cls += ' col-lg-' + (12 - this.labellg);
33791             }
33792             
33793             if(this.labelmd > 0){
33794                 label.cls += ' col-md-' + this.labelmd;
33795                 items.cls += ' col-md-' + (12 - this.labelmd);
33796             }
33797             
33798             if(this.labelsm > 0){
33799                 label.cls += ' col-sm-' + this.labelsm;
33800                 items.cls += ' col-sm-' + (12 - this.labelsm);
33801             }
33802             
33803             if(this.labelxs > 0){
33804                 label.cls += ' col-xs-' + this.labelxs;
33805                 items.cls += ' col-xs-' + (12 - this.labelxs);
33806             }
33807         }
33808         
33809         var cfg = {
33810             tag : 'div',
33811             cls : 'roo-radio-set',
33812             cn : [
33813                 {
33814                     tag : 'input',
33815                     cls : 'roo-radio-set-input',
33816                     type : 'hidden',
33817                     name : this.name,
33818                     value : this.value ? this.value :  ''
33819                 },
33820                 label,
33821                 items
33822             ]
33823         };
33824         
33825         if(this.weight.length){
33826             cfg.cls += ' roo-radio-' + this.weight;
33827         }
33828         
33829         if(this.inline) {
33830             cfg.cls += ' roo-radio-set-inline';
33831         }
33832         
33833         var settings=this;
33834         ['xs','sm','md','lg'].map(function(size){
33835             if (settings[size]) {
33836                 cfg.cls += ' col-' + size + '-' + settings[size];
33837             }
33838         });
33839         
33840         return cfg;
33841         
33842     },
33843
33844     initEvents : function()
33845     {
33846         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33847         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33848         
33849         if(!this.fieldLabel.length){
33850             this.labelEl.hide();
33851         }
33852         
33853         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33854         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33855         
33856         this.indicator = this.indicatorEl();
33857         
33858         if(this.indicator){
33859             this.indicator.addClass('invisible');
33860         }
33861         
33862         this.originalValue = this.getValue();
33863         
33864     },
33865     
33866     inputEl: function ()
33867     {
33868         return this.el.select('.roo-radio-set-input', true).first();
33869     },
33870     
33871     getChildContainer : function()
33872     {
33873         return this.itemsEl;
33874     },
33875     
33876     register : function(item)
33877     {
33878         this.radioes.push(item);
33879         
33880     },
33881     
33882     validate : function()
33883     {   
33884         if(this.getVisibilityEl().hasClass('hidden')){
33885             return true;
33886         }
33887         
33888         var valid = false;
33889         
33890         Roo.each(this.radioes, function(i){
33891             if(!i.checked){
33892                 return;
33893             }
33894             
33895             valid = true;
33896             return false;
33897         });
33898         
33899         if(this.allowBlank) {
33900             return true;
33901         }
33902         
33903         if(this.disabled || valid){
33904             this.markValid();
33905             return true;
33906         }
33907         
33908         this.markInvalid();
33909         return false;
33910         
33911     },
33912     
33913     markValid : function()
33914     {
33915         if(this.labelEl.isVisible(true)){
33916             this.indicatorEl().removeClass('visible');
33917             this.indicatorEl().addClass('invisible');
33918         }
33919         
33920         this.el.removeClass([this.invalidClass, this.validClass]);
33921         this.el.addClass(this.validClass);
33922         
33923         this.fireEvent('valid', this);
33924     },
33925     
33926     markInvalid : function(msg)
33927     {
33928         if(this.allowBlank || this.disabled){
33929             return;
33930         }
33931         
33932         if(this.labelEl.isVisible(true)){
33933             this.indicatorEl().removeClass('invisible');
33934             this.indicatorEl().addClass('visible');
33935         }
33936         
33937         this.el.removeClass([this.invalidClass, this.validClass]);
33938         this.el.addClass(this.invalidClass);
33939         
33940         this.fireEvent('invalid', this, msg);
33941         
33942     },
33943     
33944     setValue : function(v, suppressEvent)
33945     {   
33946         if(this.value === v){
33947             return;
33948         }
33949         
33950         this.value = v;
33951         
33952         if(this.rendered){
33953             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33954         }
33955         
33956         Roo.each(this.radioes, function(i){
33957             i.checked = false;
33958             i.el.removeClass('checked');
33959         });
33960         
33961         Roo.each(this.radioes, function(i){
33962             
33963             if(i.value === v || i.value.toString() === v.toString()){
33964                 i.checked = true;
33965                 i.el.addClass('checked');
33966                 
33967                 if(suppressEvent !== true){
33968                     this.fireEvent('check', this, i);
33969                 }
33970                 
33971                 return false;
33972             }
33973             
33974         }, this);
33975         
33976         this.validate();
33977     },
33978     
33979     clearInvalid : function(){
33980         
33981         if(!this.el || this.preventMark){
33982             return;
33983         }
33984         
33985         this.el.removeClass([this.invalidClass]);
33986         
33987         this.fireEvent('valid', this);
33988     }
33989     
33990 });
33991
33992 Roo.apply(Roo.bootstrap.RadioSet, {
33993     
33994     groups: {},
33995     
33996     register : function(set)
33997     {
33998         this.groups[set.name] = set;
33999     },
34000     
34001     get: function(name) 
34002     {
34003         if (typeof(this.groups[name]) == 'undefined') {
34004             return false;
34005         }
34006         
34007         return this.groups[name] ;
34008     }
34009     
34010 });
34011 /*
34012  * Based on:
34013  * Ext JS Library 1.1.1
34014  * Copyright(c) 2006-2007, Ext JS, LLC.
34015  *
34016  * Originally Released Under LGPL - original licence link has changed is not relivant.
34017  *
34018  * Fork - LGPL
34019  * <script type="text/javascript">
34020  */
34021
34022
34023 /**
34024  * @class Roo.bootstrap.SplitBar
34025  * @extends Roo.util.Observable
34026  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34027  * <br><br>
34028  * Usage:
34029  * <pre><code>
34030 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34031                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34032 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34033 split.minSize = 100;
34034 split.maxSize = 600;
34035 split.animate = true;
34036 split.on('moved', splitterMoved);
34037 </code></pre>
34038  * @constructor
34039  * Create a new SplitBar
34040  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34041  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34042  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34043  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34044                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34045                         position of the SplitBar).
34046  */
34047 Roo.bootstrap.SplitBar = function(cfg){
34048     
34049     /** @private */
34050     
34051     //{
34052     //  dragElement : elm
34053     //  resizingElement: el,
34054         // optional..
34055     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34056     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34057         // existingProxy ???
34058     //}
34059     
34060     this.el = Roo.get(cfg.dragElement, true);
34061     this.el.dom.unselectable = "on";
34062     /** @private */
34063     this.resizingEl = Roo.get(cfg.resizingElement, true);
34064
34065     /**
34066      * @private
34067      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34068      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34069      * @type Number
34070      */
34071     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34072     
34073     /**
34074      * The minimum size of the resizing element. (Defaults to 0)
34075      * @type Number
34076      */
34077     this.minSize = 0;
34078     
34079     /**
34080      * The maximum size of the resizing element. (Defaults to 2000)
34081      * @type Number
34082      */
34083     this.maxSize = 2000;
34084     
34085     /**
34086      * Whether to animate the transition to the new size
34087      * @type Boolean
34088      */
34089     this.animate = false;
34090     
34091     /**
34092      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34093      * @type Boolean
34094      */
34095     this.useShim = false;
34096     
34097     /** @private */
34098     this.shim = null;
34099     
34100     if(!cfg.existingProxy){
34101         /** @private */
34102         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34103     }else{
34104         this.proxy = Roo.get(cfg.existingProxy).dom;
34105     }
34106     /** @private */
34107     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34108     
34109     /** @private */
34110     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34111     
34112     /** @private */
34113     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34114     
34115     /** @private */
34116     this.dragSpecs = {};
34117     
34118     /**
34119      * @private The adapter to use to positon and resize elements
34120      */
34121     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34122     this.adapter.init(this);
34123     
34124     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34125         /** @private */
34126         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34127         this.el.addClass("roo-splitbar-h");
34128     }else{
34129         /** @private */
34130         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34131         this.el.addClass("roo-splitbar-v");
34132     }
34133     
34134     this.addEvents({
34135         /**
34136          * @event resize
34137          * Fires when the splitter is moved (alias for {@link #event-moved})
34138          * @param {Roo.bootstrap.SplitBar} this
34139          * @param {Number} newSize the new width or height
34140          */
34141         "resize" : true,
34142         /**
34143          * @event moved
34144          * Fires when the splitter is moved
34145          * @param {Roo.bootstrap.SplitBar} this
34146          * @param {Number} newSize the new width or height
34147          */
34148         "moved" : true,
34149         /**
34150          * @event beforeresize
34151          * Fires before the splitter is dragged
34152          * @param {Roo.bootstrap.SplitBar} this
34153          */
34154         "beforeresize" : true,
34155
34156         "beforeapply" : true
34157     });
34158
34159     Roo.util.Observable.call(this);
34160 };
34161
34162 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34163     onStartProxyDrag : function(x, y){
34164         this.fireEvent("beforeresize", this);
34165         if(!this.overlay){
34166             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34167             o.unselectable();
34168             o.enableDisplayMode("block");
34169             // all splitbars share the same overlay
34170             Roo.bootstrap.SplitBar.prototype.overlay = o;
34171         }
34172         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34173         this.overlay.show();
34174         Roo.get(this.proxy).setDisplayed("block");
34175         var size = this.adapter.getElementSize(this);
34176         this.activeMinSize = this.getMinimumSize();;
34177         this.activeMaxSize = this.getMaximumSize();;
34178         var c1 = size - this.activeMinSize;
34179         var c2 = Math.max(this.activeMaxSize - size, 0);
34180         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34181             this.dd.resetConstraints();
34182             this.dd.setXConstraint(
34183                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34184                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34185             );
34186             this.dd.setYConstraint(0, 0);
34187         }else{
34188             this.dd.resetConstraints();
34189             this.dd.setXConstraint(0, 0);
34190             this.dd.setYConstraint(
34191                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34192                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34193             );
34194          }
34195         this.dragSpecs.startSize = size;
34196         this.dragSpecs.startPoint = [x, y];
34197         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34198     },
34199     
34200     /** 
34201      * @private Called after the drag operation by the DDProxy
34202      */
34203     onEndProxyDrag : function(e){
34204         Roo.get(this.proxy).setDisplayed(false);
34205         var endPoint = Roo.lib.Event.getXY(e);
34206         if(this.overlay){
34207             this.overlay.hide();
34208         }
34209         var newSize;
34210         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34211             newSize = this.dragSpecs.startSize + 
34212                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34213                     endPoint[0] - this.dragSpecs.startPoint[0] :
34214                     this.dragSpecs.startPoint[0] - endPoint[0]
34215                 );
34216         }else{
34217             newSize = this.dragSpecs.startSize + 
34218                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34219                     endPoint[1] - this.dragSpecs.startPoint[1] :
34220                     this.dragSpecs.startPoint[1] - endPoint[1]
34221                 );
34222         }
34223         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34224         if(newSize != this.dragSpecs.startSize){
34225             if(this.fireEvent('beforeapply', this, newSize) !== false){
34226                 this.adapter.setElementSize(this, newSize);
34227                 this.fireEvent("moved", this, newSize);
34228                 this.fireEvent("resize", this, newSize);
34229             }
34230         }
34231     },
34232     
34233     /**
34234      * Get the adapter this SplitBar uses
34235      * @return The adapter object
34236      */
34237     getAdapter : function(){
34238         return this.adapter;
34239     },
34240     
34241     /**
34242      * Set the adapter this SplitBar uses
34243      * @param {Object} adapter A SplitBar adapter object
34244      */
34245     setAdapter : function(adapter){
34246         this.adapter = adapter;
34247         this.adapter.init(this);
34248     },
34249     
34250     /**
34251      * Gets the minimum size for the resizing element
34252      * @return {Number} The minimum size
34253      */
34254     getMinimumSize : function(){
34255         return this.minSize;
34256     },
34257     
34258     /**
34259      * Sets the minimum size for the resizing element
34260      * @param {Number} minSize The minimum size
34261      */
34262     setMinimumSize : function(minSize){
34263         this.minSize = minSize;
34264     },
34265     
34266     /**
34267      * Gets the maximum size for the resizing element
34268      * @return {Number} The maximum size
34269      */
34270     getMaximumSize : function(){
34271         return this.maxSize;
34272     },
34273     
34274     /**
34275      * Sets the maximum size for the resizing element
34276      * @param {Number} maxSize The maximum size
34277      */
34278     setMaximumSize : function(maxSize){
34279         this.maxSize = maxSize;
34280     },
34281     
34282     /**
34283      * Sets the initialize size for the resizing element
34284      * @param {Number} size The initial size
34285      */
34286     setCurrentSize : function(size){
34287         var oldAnimate = this.animate;
34288         this.animate = false;
34289         this.adapter.setElementSize(this, size);
34290         this.animate = oldAnimate;
34291     },
34292     
34293     /**
34294      * Destroy this splitbar. 
34295      * @param {Boolean} removeEl True to remove the element
34296      */
34297     destroy : function(removeEl){
34298         if(this.shim){
34299             this.shim.remove();
34300         }
34301         this.dd.unreg();
34302         this.proxy.parentNode.removeChild(this.proxy);
34303         if(removeEl){
34304             this.el.remove();
34305         }
34306     }
34307 });
34308
34309 /**
34310  * @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.
34311  */
34312 Roo.bootstrap.SplitBar.createProxy = function(dir){
34313     var proxy = new Roo.Element(document.createElement("div"));
34314     proxy.unselectable();
34315     var cls = 'roo-splitbar-proxy';
34316     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34317     document.body.appendChild(proxy.dom);
34318     return proxy.dom;
34319 };
34320
34321 /** 
34322  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34323  * Default Adapter. It assumes the splitter and resizing element are not positioned
34324  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34325  */
34326 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34327 };
34328
34329 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34330     // do nothing for now
34331     init : function(s){
34332     
34333     },
34334     /**
34335      * Called before drag operations to get the current size of the resizing element. 
34336      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34337      */
34338      getElementSize : function(s){
34339         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34340             return s.resizingEl.getWidth();
34341         }else{
34342             return s.resizingEl.getHeight();
34343         }
34344     },
34345     
34346     /**
34347      * Called after drag operations to set the size of the resizing element.
34348      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34349      * @param {Number} newSize The new size to set
34350      * @param {Function} onComplete A function to be invoked when resizing is complete
34351      */
34352     setElementSize : function(s, newSize, onComplete){
34353         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34354             if(!s.animate){
34355                 s.resizingEl.setWidth(newSize);
34356                 if(onComplete){
34357                     onComplete(s, newSize);
34358                 }
34359             }else{
34360                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34361             }
34362         }else{
34363             
34364             if(!s.animate){
34365                 s.resizingEl.setHeight(newSize);
34366                 if(onComplete){
34367                     onComplete(s, newSize);
34368                 }
34369             }else{
34370                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34371             }
34372         }
34373     }
34374 };
34375
34376 /** 
34377  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34378  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34379  * Adapter that  moves the splitter element to align with the resized sizing element. 
34380  * Used with an absolute positioned SplitBar.
34381  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34382  * document.body, make sure you assign an id to the body element.
34383  */
34384 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34385     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34386     this.container = Roo.get(container);
34387 };
34388
34389 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34390     init : function(s){
34391         this.basic.init(s);
34392     },
34393     
34394     getElementSize : function(s){
34395         return this.basic.getElementSize(s);
34396     },
34397     
34398     setElementSize : function(s, newSize, onComplete){
34399         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34400     },
34401     
34402     moveSplitter : function(s){
34403         var yes = Roo.bootstrap.SplitBar;
34404         switch(s.placement){
34405             case yes.LEFT:
34406                 s.el.setX(s.resizingEl.getRight());
34407                 break;
34408             case yes.RIGHT:
34409                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34410                 break;
34411             case yes.TOP:
34412                 s.el.setY(s.resizingEl.getBottom());
34413                 break;
34414             case yes.BOTTOM:
34415                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34416                 break;
34417         }
34418     }
34419 };
34420
34421 /**
34422  * Orientation constant - Create a vertical SplitBar
34423  * @static
34424  * @type Number
34425  */
34426 Roo.bootstrap.SplitBar.VERTICAL = 1;
34427
34428 /**
34429  * Orientation constant - Create a horizontal SplitBar
34430  * @static
34431  * @type Number
34432  */
34433 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34434
34435 /**
34436  * Placement constant - The resizing element is to the left of the splitter element
34437  * @static
34438  * @type Number
34439  */
34440 Roo.bootstrap.SplitBar.LEFT = 1;
34441
34442 /**
34443  * Placement constant - The resizing element is to the right of the splitter element
34444  * @static
34445  * @type Number
34446  */
34447 Roo.bootstrap.SplitBar.RIGHT = 2;
34448
34449 /**
34450  * Placement constant - The resizing element is positioned above the splitter element
34451  * @static
34452  * @type Number
34453  */
34454 Roo.bootstrap.SplitBar.TOP = 3;
34455
34456 /**
34457  * Placement constant - The resizing element is positioned under splitter element
34458  * @static
34459  * @type Number
34460  */
34461 Roo.bootstrap.SplitBar.BOTTOM = 4;
34462 Roo.namespace("Roo.bootstrap.layout");/*
34463  * Based on:
34464  * Ext JS Library 1.1.1
34465  * Copyright(c) 2006-2007, Ext JS, LLC.
34466  *
34467  * Originally Released Under LGPL - original licence link has changed is not relivant.
34468  *
34469  * Fork - LGPL
34470  * <script type="text/javascript">
34471  */
34472
34473 /**
34474  * @class Roo.bootstrap.layout.Manager
34475  * @extends Roo.bootstrap.Component
34476  * Base class for layout managers.
34477  */
34478 Roo.bootstrap.layout.Manager = function(config)
34479 {
34480     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34481
34482
34483
34484
34485
34486     /** false to disable window resize monitoring @type Boolean */
34487     this.monitorWindowResize = true;
34488     this.regions = {};
34489     this.addEvents({
34490         /**
34491          * @event layout
34492          * Fires when a layout is performed.
34493          * @param {Roo.LayoutManager} this
34494          */
34495         "layout" : true,
34496         /**
34497          * @event regionresized
34498          * Fires when the user resizes a region.
34499          * @param {Roo.LayoutRegion} region The resized region
34500          * @param {Number} newSize The new size (width for east/west, height for north/south)
34501          */
34502         "regionresized" : true,
34503         /**
34504          * @event regioncollapsed
34505          * Fires when a region is collapsed.
34506          * @param {Roo.LayoutRegion} region The collapsed region
34507          */
34508         "regioncollapsed" : true,
34509         /**
34510          * @event regionexpanded
34511          * Fires when a region is expanded.
34512          * @param {Roo.LayoutRegion} region The expanded region
34513          */
34514         "regionexpanded" : true
34515     });
34516     this.updating = false;
34517
34518     if (config.el) {
34519         this.el = Roo.get(config.el);
34520         this.initEvents();
34521     }
34522
34523 };
34524
34525 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34526
34527
34528     regions : null,
34529
34530     monitorWindowResize : true,
34531
34532
34533     updating : false,
34534
34535
34536     onRender : function(ct, position)
34537     {
34538         if(!this.el){
34539             this.el = Roo.get(ct);
34540             this.initEvents();
34541         }
34542         //this.fireEvent('render',this);
34543     },
34544
34545
34546     initEvents: function()
34547     {
34548
34549
34550         // ie scrollbar fix
34551         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34552             document.body.scroll = "no";
34553         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34554             this.el.position('relative');
34555         }
34556         this.id = this.el.id;
34557         this.el.addClass("roo-layout-container");
34558         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34559         if(this.el.dom != document.body ) {
34560             this.el.on('resize', this.layout,this);
34561             this.el.on('show', this.layout,this);
34562         }
34563
34564     },
34565
34566     /**
34567      * Returns true if this layout is currently being updated
34568      * @return {Boolean}
34569      */
34570     isUpdating : function(){
34571         return this.updating;
34572     },
34573
34574     /**
34575      * Suspend the LayoutManager from doing auto-layouts while
34576      * making multiple add or remove calls
34577      */
34578     beginUpdate : function(){
34579         this.updating = true;
34580     },
34581
34582     /**
34583      * Restore auto-layouts and optionally disable the manager from performing a layout
34584      * @param {Boolean} noLayout true to disable a layout update
34585      */
34586     endUpdate : function(noLayout){
34587         this.updating = false;
34588         if(!noLayout){
34589             this.layout();
34590         }
34591     },
34592
34593     layout: function(){
34594         // abstract...
34595     },
34596
34597     onRegionResized : function(region, newSize){
34598         this.fireEvent("regionresized", region, newSize);
34599         this.layout();
34600     },
34601
34602     onRegionCollapsed : function(region){
34603         this.fireEvent("regioncollapsed", region);
34604     },
34605
34606     onRegionExpanded : function(region){
34607         this.fireEvent("regionexpanded", region);
34608     },
34609
34610     /**
34611      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34612      * performs box-model adjustments.
34613      * @return {Object} The size as an object {width: (the width), height: (the height)}
34614      */
34615     getViewSize : function()
34616     {
34617         var size;
34618         if(this.el.dom != document.body){
34619             size = this.el.getSize();
34620         }else{
34621             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34622         }
34623         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34624         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34625         return size;
34626     },
34627
34628     /**
34629      * Returns the Element this layout is bound to.
34630      * @return {Roo.Element}
34631      */
34632     getEl : function(){
34633         return this.el;
34634     },
34635
34636     /**
34637      * Returns the specified region.
34638      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34639      * @return {Roo.LayoutRegion}
34640      */
34641     getRegion : function(target){
34642         return this.regions[target.toLowerCase()];
34643     },
34644
34645     onWindowResize : function(){
34646         if(this.monitorWindowResize){
34647             this.layout();
34648         }
34649     }
34650 });
34651 /*
34652  * Based on:
34653  * Ext JS Library 1.1.1
34654  * Copyright(c) 2006-2007, Ext JS, LLC.
34655  *
34656  * Originally Released Under LGPL - original licence link has changed is not relivant.
34657  *
34658  * Fork - LGPL
34659  * <script type="text/javascript">
34660  */
34661 /**
34662  * @class Roo.bootstrap.layout.Border
34663  * @extends Roo.bootstrap.layout.Manager
34664  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34665  * please see: examples/bootstrap/nested.html<br><br>
34666  
34667 <b>The container the layout is rendered into can be either the body element or any other element.
34668 If it is not the body element, the container needs to either be an absolute positioned element,
34669 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34670 the container size if it is not the body element.</b>
34671
34672 * @constructor
34673 * Create a new Border
34674 * @param {Object} config Configuration options
34675  */
34676 Roo.bootstrap.layout.Border = function(config){
34677     config = config || {};
34678     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34679     
34680     
34681     
34682     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34683         if(config[region]){
34684             config[region].region = region;
34685             this.addRegion(config[region]);
34686         }
34687     },this);
34688     
34689 };
34690
34691 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34692
34693 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34694     /**
34695      * Creates and adds a new region if it doesn't already exist.
34696      * @param {String} target The target region key (north, south, east, west or center).
34697      * @param {Object} config The regions config object
34698      * @return {BorderLayoutRegion} The new region
34699      */
34700     addRegion : function(config)
34701     {
34702         if(!this.regions[config.region]){
34703             var r = this.factory(config);
34704             this.bindRegion(r);
34705         }
34706         return this.regions[config.region];
34707     },
34708
34709     // private (kinda)
34710     bindRegion : function(r){
34711         this.regions[r.config.region] = r;
34712         
34713         r.on("visibilitychange",    this.layout, this);
34714         r.on("paneladded",          this.layout, this);
34715         r.on("panelremoved",        this.layout, this);
34716         r.on("invalidated",         this.layout, this);
34717         r.on("resized",             this.onRegionResized, this);
34718         r.on("collapsed",           this.onRegionCollapsed, this);
34719         r.on("expanded",            this.onRegionExpanded, this);
34720     },
34721
34722     /**
34723      * Performs a layout update.
34724      */
34725     layout : function()
34726     {
34727         if(this.updating) {
34728             return;
34729         }
34730         
34731         // render all the rebions if they have not been done alreayd?
34732         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34733             if(this.regions[region] && !this.regions[region].bodyEl){
34734                 this.regions[region].onRender(this.el)
34735             }
34736         },this);
34737         
34738         var size = this.getViewSize();
34739         var w = size.width;
34740         var h = size.height;
34741         var centerW = w;
34742         var centerH = h;
34743         var centerY = 0;
34744         var centerX = 0;
34745         //var x = 0, y = 0;
34746
34747         var rs = this.regions;
34748         var north = rs["north"];
34749         var south = rs["south"]; 
34750         var west = rs["west"];
34751         var east = rs["east"];
34752         var center = rs["center"];
34753         //if(this.hideOnLayout){ // not supported anymore
34754             //c.el.setStyle("display", "none");
34755         //}
34756         if(north && north.isVisible()){
34757             var b = north.getBox();
34758             var m = north.getMargins();
34759             b.width = w - (m.left+m.right);
34760             b.x = m.left;
34761             b.y = m.top;
34762             centerY = b.height + b.y + m.bottom;
34763             centerH -= centerY;
34764             north.updateBox(this.safeBox(b));
34765         }
34766         if(south && south.isVisible()){
34767             var b = south.getBox();
34768             var m = south.getMargins();
34769             b.width = w - (m.left+m.right);
34770             b.x = m.left;
34771             var totalHeight = (b.height + m.top + m.bottom);
34772             b.y = h - totalHeight + m.top;
34773             centerH -= totalHeight;
34774             south.updateBox(this.safeBox(b));
34775         }
34776         if(west && west.isVisible()){
34777             var b = west.getBox();
34778             var m = west.getMargins();
34779             b.height = centerH - (m.top+m.bottom);
34780             b.x = m.left;
34781             b.y = centerY + m.top;
34782             var totalWidth = (b.width + m.left + m.right);
34783             centerX += totalWidth;
34784             centerW -= totalWidth;
34785             west.updateBox(this.safeBox(b));
34786         }
34787         if(east && east.isVisible()){
34788             var b = east.getBox();
34789             var m = east.getMargins();
34790             b.height = centerH - (m.top+m.bottom);
34791             var totalWidth = (b.width + m.left + m.right);
34792             b.x = w - totalWidth + m.left;
34793             b.y = centerY + m.top;
34794             centerW -= totalWidth;
34795             east.updateBox(this.safeBox(b));
34796         }
34797         if(center){
34798             var m = center.getMargins();
34799             var centerBox = {
34800                 x: centerX + m.left,
34801                 y: centerY + m.top,
34802                 width: centerW - (m.left+m.right),
34803                 height: centerH - (m.top+m.bottom)
34804             };
34805             //if(this.hideOnLayout){
34806                 //center.el.setStyle("display", "block");
34807             //}
34808             center.updateBox(this.safeBox(centerBox));
34809         }
34810         this.el.repaint();
34811         this.fireEvent("layout", this);
34812     },
34813
34814     // private
34815     safeBox : function(box){
34816         box.width = Math.max(0, box.width);
34817         box.height = Math.max(0, box.height);
34818         return box;
34819     },
34820
34821     /**
34822      * Adds a ContentPanel (or subclass) to this layout.
34823      * @param {String} target The target region key (north, south, east, west or center).
34824      * @param {Roo.ContentPanel} panel The panel to add
34825      * @return {Roo.ContentPanel} The added panel
34826      */
34827     add : function(target, panel){
34828          
34829         target = target.toLowerCase();
34830         return this.regions[target].add(panel);
34831     },
34832
34833     /**
34834      * Remove a ContentPanel (or subclass) to this layout.
34835      * @param {String} target The target region key (north, south, east, west or center).
34836      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34837      * @return {Roo.ContentPanel} The removed panel
34838      */
34839     remove : function(target, panel){
34840         target = target.toLowerCase();
34841         return this.regions[target].remove(panel);
34842     },
34843
34844     /**
34845      * Searches all regions for a panel with the specified id
34846      * @param {String} panelId
34847      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34848      */
34849     findPanel : function(panelId){
34850         var rs = this.regions;
34851         for(var target in rs){
34852             if(typeof rs[target] != "function"){
34853                 var p = rs[target].getPanel(panelId);
34854                 if(p){
34855                     return p;
34856                 }
34857             }
34858         }
34859         return null;
34860     },
34861
34862     /**
34863      * Searches all regions for a panel with the specified id and activates (shows) it.
34864      * @param {String/ContentPanel} panelId The panels id or the panel itself
34865      * @return {Roo.ContentPanel} The shown panel or null
34866      */
34867     showPanel : function(panelId) {
34868       var rs = this.regions;
34869       for(var target in rs){
34870          var r = rs[target];
34871          if(typeof r != "function"){
34872             if(r.hasPanel(panelId)){
34873                return r.showPanel(panelId);
34874             }
34875          }
34876       }
34877       return null;
34878    },
34879
34880    /**
34881      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34882      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34883      */
34884    /*
34885     restoreState : function(provider){
34886         if(!provider){
34887             provider = Roo.state.Manager;
34888         }
34889         var sm = new Roo.LayoutStateManager();
34890         sm.init(this, provider);
34891     },
34892 */
34893  
34894  
34895     /**
34896      * Adds a xtype elements to the layout.
34897      * <pre><code>
34898
34899 layout.addxtype({
34900        xtype : 'ContentPanel',
34901        region: 'west',
34902        items: [ .... ]
34903    }
34904 );
34905
34906 layout.addxtype({
34907         xtype : 'NestedLayoutPanel',
34908         region: 'west',
34909         layout: {
34910            center: { },
34911            west: { }   
34912         },
34913         items : [ ... list of content panels or nested layout panels.. ]
34914    }
34915 );
34916 </code></pre>
34917      * @param {Object} cfg Xtype definition of item to add.
34918      */
34919     addxtype : function(cfg)
34920     {
34921         // basically accepts a pannel...
34922         // can accept a layout region..!?!?
34923         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34924         
34925         
34926         // theory?  children can only be panels??
34927         
34928         //if (!cfg.xtype.match(/Panel$/)) {
34929         //    return false;
34930         //}
34931         var ret = false;
34932         
34933         if (typeof(cfg.region) == 'undefined') {
34934             Roo.log("Failed to add Panel, region was not set");
34935             Roo.log(cfg);
34936             return false;
34937         }
34938         var region = cfg.region;
34939         delete cfg.region;
34940         
34941           
34942         var xitems = [];
34943         if (cfg.items) {
34944             xitems = cfg.items;
34945             delete cfg.items;
34946         }
34947         var nb = false;
34948         
34949         switch(cfg.xtype) 
34950         {
34951             case 'Content':  // ContentPanel (el, cfg)
34952             case 'Scroll':  // ContentPanel (el, cfg)
34953             case 'View': 
34954                 cfg.autoCreate = true;
34955                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34956                 //} else {
34957                 //    var el = this.el.createChild();
34958                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34959                 //}
34960                 
34961                 this.add(region, ret);
34962                 break;
34963             
34964             /*
34965             case 'TreePanel': // our new panel!
34966                 cfg.el = this.el.createChild();
34967                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34968                 this.add(region, ret);
34969                 break;
34970             */
34971             
34972             case 'Nest': 
34973                 // create a new Layout (which is  a Border Layout...
34974                 
34975                 var clayout = cfg.layout;
34976                 clayout.el  = this.el.createChild();
34977                 clayout.items   = clayout.items  || [];
34978                 
34979                 delete cfg.layout;
34980                 
34981                 // replace this exitems with the clayout ones..
34982                 xitems = clayout.items;
34983                  
34984                 // force background off if it's in center...
34985                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34986                     cfg.background = false;
34987                 }
34988                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34989                 
34990                 
34991                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34992                 //console.log('adding nested layout panel '  + cfg.toSource());
34993                 this.add(region, ret);
34994                 nb = {}; /// find first...
34995                 break;
34996             
34997             case 'Grid':
34998                 
34999                 // needs grid and region
35000                 
35001                 //var el = this.getRegion(region).el.createChild();
35002                 /*
35003                  *var el = this.el.createChild();
35004                 // create the grid first...
35005                 cfg.grid.container = el;
35006                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35007                 */
35008                 
35009                 if (region == 'center' && this.active ) {
35010                     cfg.background = false;
35011                 }
35012                 
35013                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35014                 
35015                 this.add(region, ret);
35016                 /*
35017                 if (cfg.background) {
35018                     // render grid on panel activation (if panel background)
35019                     ret.on('activate', function(gp) {
35020                         if (!gp.grid.rendered) {
35021                     //        gp.grid.render(el);
35022                         }
35023                     });
35024                 } else {
35025                   //  cfg.grid.render(el);
35026                 }
35027                 */
35028                 break;
35029            
35030            
35031             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35032                 // it was the old xcomponent building that caused this before.
35033                 // espeically if border is the top element in the tree.
35034                 ret = this;
35035                 break; 
35036                 
35037                     
35038                 
35039                 
35040                 
35041             default:
35042                 /*
35043                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35044                     
35045                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35046                     this.add(region, ret);
35047                 } else {
35048                 */
35049                     Roo.log(cfg);
35050                     throw "Can not add '" + cfg.xtype + "' to Border";
35051                     return null;
35052              
35053                                 
35054              
35055         }
35056         this.beginUpdate();
35057         // add children..
35058         var region = '';
35059         var abn = {};
35060         Roo.each(xitems, function(i)  {
35061             region = nb && i.region ? i.region : false;
35062             
35063             var add = ret.addxtype(i);
35064            
35065             if (region) {
35066                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35067                 if (!i.background) {
35068                     abn[region] = nb[region] ;
35069                 }
35070             }
35071             
35072         });
35073         this.endUpdate();
35074
35075         // make the last non-background panel active..
35076         //if (nb) { Roo.log(abn); }
35077         if (nb) {
35078             
35079             for(var r in abn) {
35080                 region = this.getRegion(r);
35081                 if (region) {
35082                     // tried using nb[r], but it does not work..
35083                      
35084                     region.showPanel(abn[r]);
35085                    
35086                 }
35087             }
35088         }
35089         return ret;
35090         
35091     },
35092     
35093     
35094 // private
35095     factory : function(cfg)
35096     {
35097         
35098         var validRegions = Roo.bootstrap.layout.Border.regions;
35099
35100         var target = cfg.region;
35101         cfg.mgr = this;
35102         
35103         var r = Roo.bootstrap.layout;
35104         Roo.log(target);
35105         switch(target){
35106             case "north":
35107                 return new r.North(cfg);
35108             case "south":
35109                 return new r.South(cfg);
35110             case "east":
35111                 return new r.East(cfg);
35112             case "west":
35113                 return new r.West(cfg);
35114             case "center":
35115                 return new r.Center(cfg);
35116         }
35117         throw 'Layout region "'+target+'" not supported.';
35118     }
35119     
35120     
35121 });
35122  /*
35123  * Based on:
35124  * Ext JS Library 1.1.1
35125  * Copyright(c) 2006-2007, Ext JS, LLC.
35126  *
35127  * Originally Released Under LGPL - original licence link has changed is not relivant.
35128  *
35129  * Fork - LGPL
35130  * <script type="text/javascript">
35131  */
35132  
35133 /**
35134  * @class Roo.bootstrap.layout.Basic
35135  * @extends Roo.util.Observable
35136  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35137  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35138  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35139  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35140  * @cfg {string}   region  the region that it inhabits..
35141  * @cfg {bool}   skipConfig skip config?
35142  * 
35143
35144  */
35145 Roo.bootstrap.layout.Basic = function(config){
35146     
35147     this.mgr = config.mgr;
35148     
35149     this.position = config.region;
35150     
35151     var skipConfig = config.skipConfig;
35152     
35153     this.events = {
35154         /**
35155          * @scope Roo.BasicLayoutRegion
35156          */
35157         
35158         /**
35159          * @event beforeremove
35160          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35161          * @param {Roo.LayoutRegion} this
35162          * @param {Roo.ContentPanel} panel The panel
35163          * @param {Object} e The cancel event object
35164          */
35165         "beforeremove" : true,
35166         /**
35167          * @event invalidated
35168          * Fires when the layout for this region is changed.
35169          * @param {Roo.LayoutRegion} this
35170          */
35171         "invalidated" : true,
35172         /**
35173          * @event visibilitychange
35174          * Fires when this region is shown or hidden 
35175          * @param {Roo.LayoutRegion} this
35176          * @param {Boolean} visibility true or false
35177          */
35178         "visibilitychange" : true,
35179         /**
35180          * @event paneladded
35181          * Fires when a panel is added. 
35182          * @param {Roo.LayoutRegion} this
35183          * @param {Roo.ContentPanel} panel The panel
35184          */
35185         "paneladded" : true,
35186         /**
35187          * @event panelremoved
35188          * Fires when a panel is removed. 
35189          * @param {Roo.LayoutRegion} this
35190          * @param {Roo.ContentPanel} panel The panel
35191          */
35192         "panelremoved" : true,
35193         /**
35194          * @event beforecollapse
35195          * Fires when this region before collapse.
35196          * @param {Roo.LayoutRegion} this
35197          */
35198         "beforecollapse" : true,
35199         /**
35200          * @event collapsed
35201          * Fires when this region is collapsed.
35202          * @param {Roo.LayoutRegion} this
35203          */
35204         "collapsed" : true,
35205         /**
35206          * @event expanded
35207          * Fires when this region is expanded.
35208          * @param {Roo.LayoutRegion} this
35209          */
35210         "expanded" : true,
35211         /**
35212          * @event slideshow
35213          * Fires when this region is slid into view.
35214          * @param {Roo.LayoutRegion} this
35215          */
35216         "slideshow" : true,
35217         /**
35218          * @event slidehide
35219          * Fires when this region slides out of view. 
35220          * @param {Roo.LayoutRegion} this
35221          */
35222         "slidehide" : true,
35223         /**
35224          * @event panelactivated
35225          * Fires when a panel is activated. 
35226          * @param {Roo.LayoutRegion} this
35227          * @param {Roo.ContentPanel} panel The activated panel
35228          */
35229         "panelactivated" : true,
35230         /**
35231          * @event resized
35232          * Fires when the user resizes this region. 
35233          * @param {Roo.LayoutRegion} this
35234          * @param {Number} newSize The new size (width for east/west, height for north/south)
35235          */
35236         "resized" : true
35237     };
35238     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35239     this.panels = new Roo.util.MixedCollection();
35240     this.panels.getKey = this.getPanelId.createDelegate(this);
35241     this.box = null;
35242     this.activePanel = null;
35243     // ensure listeners are added...
35244     
35245     if (config.listeners || config.events) {
35246         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35247             listeners : config.listeners || {},
35248             events : config.events || {}
35249         });
35250     }
35251     
35252     if(skipConfig !== true){
35253         this.applyConfig(config);
35254     }
35255 };
35256
35257 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35258 {
35259     getPanelId : function(p){
35260         return p.getId();
35261     },
35262     
35263     applyConfig : function(config){
35264         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35265         this.config = config;
35266         
35267     },
35268     
35269     /**
35270      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35271      * the width, for horizontal (north, south) the height.
35272      * @param {Number} newSize The new width or height
35273      */
35274     resizeTo : function(newSize){
35275         var el = this.el ? this.el :
35276                  (this.activePanel ? this.activePanel.getEl() : null);
35277         if(el){
35278             switch(this.position){
35279                 case "east":
35280                 case "west":
35281                     el.setWidth(newSize);
35282                     this.fireEvent("resized", this, newSize);
35283                 break;
35284                 case "north":
35285                 case "south":
35286                     el.setHeight(newSize);
35287                     this.fireEvent("resized", this, newSize);
35288                 break;                
35289             }
35290         }
35291     },
35292     
35293     getBox : function(){
35294         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35295     },
35296     
35297     getMargins : function(){
35298         return this.margins;
35299     },
35300     
35301     updateBox : function(box){
35302         this.box = box;
35303         var el = this.activePanel.getEl();
35304         el.dom.style.left = box.x + "px";
35305         el.dom.style.top = box.y + "px";
35306         this.activePanel.setSize(box.width, box.height);
35307     },
35308     
35309     /**
35310      * Returns the container element for this region.
35311      * @return {Roo.Element}
35312      */
35313     getEl : function(){
35314         return this.activePanel;
35315     },
35316     
35317     /**
35318      * Returns true if this region is currently visible.
35319      * @return {Boolean}
35320      */
35321     isVisible : function(){
35322         return this.activePanel ? true : false;
35323     },
35324     
35325     setActivePanel : function(panel){
35326         panel = this.getPanel(panel);
35327         if(this.activePanel && this.activePanel != panel){
35328             this.activePanel.setActiveState(false);
35329             this.activePanel.getEl().setLeftTop(-10000,-10000);
35330         }
35331         this.activePanel = panel;
35332         panel.setActiveState(true);
35333         if(this.box){
35334             panel.setSize(this.box.width, this.box.height);
35335         }
35336         this.fireEvent("panelactivated", this, panel);
35337         this.fireEvent("invalidated");
35338     },
35339     
35340     /**
35341      * Show the specified panel.
35342      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35343      * @return {Roo.ContentPanel} The shown panel or null
35344      */
35345     showPanel : function(panel){
35346         panel = this.getPanel(panel);
35347         if(panel){
35348             this.setActivePanel(panel);
35349         }
35350         return panel;
35351     },
35352     
35353     /**
35354      * Get the active panel for this region.
35355      * @return {Roo.ContentPanel} The active panel or null
35356      */
35357     getActivePanel : function(){
35358         return this.activePanel;
35359     },
35360     
35361     /**
35362      * Add the passed ContentPanel(s)
35363      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35364      * @return {Roo.ContentPanel} The panel added (if only one was added)
35365      */
35366     add : function(panel){
35367         if(arguments.length > 1){
35368             for(var i = 0, len = arguments.length; i < len; i++) {
35369                 this.add(arguments[i]);
35370             }
35371             return null;
35372         }
35373         if(this.hasPanel(panel)){
35374             this.showPanel(panel);
35375             return panel;
35376         }
35377         var el = panel.getEl();
35378         if(el.dom.parentNode != this.mgr.el.dom){
35379             this.mgr.el.dom.appendChild(el.dom);
35380         }
35381         if(panel.setRegion){
35382             panel.setRegion(this);
35383         }
35384         this.panels.add(panel);
35385         el.setStyle("position", "absolute");
35386         if(!panel.background){
35387             this.setActivePanel(panel);
35388             if(this.config.initialSize && this.panels.getCount()==1){
35389                 this.resizeTo(this.config.initialSize);
35390             }
35391         }
35392         this.fireEvent("paneladded", this, panel);
35393         return panel;
35394     },
35395     
35396     /**
35397      * Returns true if the panel is in this region.
35398      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35399      * @return {Boolean}
35400      */
35401     hasPanel : function(panel){
35402         if(typeof panel == "object"){ // must be panel obj
35403             panel = panel.getId();
35404         }
35405         return this.getPanel(panel) ? true : false;
35406     },
35407     
35408     /**
35409      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35410      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35411      * @param {Boolean} preservePanel Overrides the config preservePanel option
35412      * @return {Roo.ContentPanel} The panel that was removed
35413      */
35414     remove : function(panel, preservePanel){
35415         panel = this.getPanel(panel);
35416         if(!panel){
35417             return null;
35418         }
35419         var e = {};
35420         this.fireEvent("beforeremove", this, panel, e);
35421         if(e.cancel === true){
35422             return null;
35423         }
35424         var panelId = panel.getId();
35425         this.panels.removeKey(panelId);
35426         return panel;
35427     },
35428     
35429     /**
35430      * Returns the panel specified or null if it's not in this region.
35431      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35432      * @return {Roo.ContentPanel}
35433      */
35434     getPanel : function(id){
35435         if(typeof id == "object"){ // must be panel obj
35436             return id;
35437         }
35438         return this.panels.get(id);
35439     },
35440     
35441     /**
35442      * Returns this regions position (north/south/east/west/center).
35443      * @return {String} 
35444      */
35445     getPosition: function(){
35446         return this.position;    
35447     }
35448 });/*
35449  * Based on:
35450  * Ext JS Library 1.1.1
35451  * Copyright(c) 2006-2007, Ext JS, LLC.
35452  *
35453  * Originally Released Under LGPL - original licence link has changed is not relivant.
35454  *
35455  * Fork - LGPL
35456  * <script type="text/javascript">
35457  */
35458  
35459 /**
35460  * @class Roo.bootstrap.layout.Region
35461  * @extends Roo.bootstrap.layout.Basic
35462  * This class represents a region in a layout manager.
35463  
35464  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35465  * @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})
35466  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35467  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35468  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35469  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35470  * @cfg {String}    title           The title for the region (overrides panel titles)
35471  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35472  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35473  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35474  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35475  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35476  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35477  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35478  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35479  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35480  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35481
35482  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35483  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35484  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35485  * @cfg {Number}    width           For East/West panels
35486  * @cfg {Number}    height          For North/South panels
35487  * @cfg {Boolean}   split           To show the splitter
35488  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35489  * 
35490  * @cfg {string}   cls             Extra CSS classes to add to region
35491  * 
35492  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35493  * @cfg {string}   region  the region that it inhabits..
35494  *
35495
35496  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35497  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35498
35499  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35500  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35501  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35502  */
35503 Roo.bootstrap.layout.Region = function(config)
35504 {
35505     this.applyConfig(config);
35506
35507     var mgr = config.mgr;
35508     var pos = config.region;
35509     config.skipConfig = true;
35510     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35511     
35512     if (mgr.el) {
35513         this.onRender(mgr.el);   
35514     }
35515      
35516     this.visible = true;
35517     this.collapsed = false;
35518     this.unrendered_panels = [];
35519 };
35520
35521 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35522
35523     position: '', // set by wrapper (eg. north/south etc..)
35524     unrendered_panels : null,  // unrendered panels.
35525     createBody : function(){
35526         /** This region's body element 
35527         * @type Roo.Element */
35528         this.bodyEl = this.el.createChild({
35529                 tag: "div",
35530                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35531         });
35532     },
35533
35534     onRender: function(ctr, pos)
35535     {
35536         var dh = Roo.DomHelper;
35537         /** This region's container element 
35538         * @type Roo.Element */
35539         this.el = dh.append(ctr.dom, {
35540                 tag: "div",
35541                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35542             }, true);
35543         /** This region's title element 
35544         * @type Roo.Element */
35545     
35546         this.titleEl = dh.append(this.el.dom,
35547             {
35548                     tag: "div",
35549                     unselectable: "on",
35550                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35551                     children:[
35552                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35553                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35554                     ]}, true);
35555         
35556         this.titleEl.enableDisplayMode();
35557         /** This region's title text element 
35558         * @type HTMLElement */
35559         this.titleTextEl = this.titleEl.dom.firstChild;
35560         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35561         /*
35562         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35563         this.closeBtn.enableDisplayMode();
35564         this.closeBtn.on("click", this.closeClicked, this);
35565         this.closeBtn.hide();
35566     */
35567         this.createBody(this.config);
35568         if(this.config.hideWhenEmpty){
35569             this.hide();
35570             this.on("paneladded", this.validateVisibility, this);
35571             this.on("panelremoved", this.validateVisibility, this);
35572         }
35573         if(this.autoScroll){
35574             this.bodyEl.setStyle("overflow", "auto");
35575         }else{
35576             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35577         }
35578         //if(c.titlebar !== false){
35579             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35580                 this.titleEl.hide();
35581             }else{
35582                 this.titleEl.show();
35583                 if(this.config.title){
35584                     this.titleTextEl.innerHTML = this.config.title;
35585                 }
35586             }
35587         //}
35588         if(this.config.collapsed){
35589             this.collapse(true);
35590         }
35591         if(this.config.hidden){
35592             this.hide();
35593         }
35594         
35595         if (this.unrendered_panels && this.unrendered_panels.length) {
35596             for (var i =0;i< this.unrendered_panels.length; i++) {
35597                 this.add(this.unrendered_panels[i]);
35598             }
35599             this.unrendered_panels = null;
35600             
35601         }
35602         
35603     },
35604     
35605     applyConfig : function(c)
35606     {
35607         /*
35608          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35609             var dh = Roo.DomHelper;
35610             if(c.titlebar !== false){
35611                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35612                 this.collapseBtn.on("click", this.collapse, this);
35613                 this.collapseBtn.enableDisplayMode();
35614                 /*
35615                 if(c.showPin === true || this.showPin){
35616                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35617                     this.stickBtn.enableDisplayMode();
35618                     this.stickBtn.on("click", this.expand, this);
35619                     this.stickBtn.hide();
35620                 }
35621                 
35622             }
35623             */
35624             /** This region's collapsed element
35625             * @type Roo.Element */
35626             /*
35627              *
35628             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35629                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35630             ]}, true);
35631             
35632             if(c.floatable !== false){
35633                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35634                this.collapsedEl.on("click", this.collapseClick, this);
35635             }
35636
35637             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35638                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35639                    id: "message", unselectable: "on", style:{"float":"left"}});
35640                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35641              }
35642             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35643             this.expandBtn.on("click", this.expand, this);
35644             
35645         }
35646         
35647         if(this.collapseBtn){
35648             this.collapseBtn.setVisible(c.collapsible == true);
35649         }
35650         
35651         this.cmargins = c.cmargins || this.cmargins ||
35652                          (this.position == "west" || this.position == "east" ?
35653                              {top: 0, left: 2, right:2, bottom: 0} :
35654                              {top: 2, left: 0, right:0, bottom: 2});
35655         */
35656         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35657         
35658         
35659         this.bottomTabs = c.tabPosition != "top";
35660         
35661         this.autoScroll = c.autoScroll || false;
35662         
35663         
35664        
35665         
35666         this.duration = c.duration || .30;
35667         this.slideDuration = c.slideDuration || .45;
35668         this.config = c;
35669        
35670     },
35671     /**
35672      * Returns true if this region is currently visible.
35673      * @return {Boolean}
35674      */
35675     isVisible : function(){
35676         return this.visible;
35677     },
35678
35679     /**
35680      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35681      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35682      */
35683     //setCollapsedTitle : function(title){
35684     //    title = title || "&#160;";
35685      //   if(this.collapsedTitleTextEl){
35686       //      this.collapsedTitleTextEl.innerHTML = title;
35687        // }
35688     //},
35689
35690     getBox : function(){
35691         var b;
35692       //  if(!this.collapsed){
35693             b = this.el.getBox(false, true);
35694        // }else{
35695           //  b = this.collapsedEl.getBox(false, true);
35696         //}
35697         return b;
35698     },
35699
35700     getMargins : function(){
35701         return this.margins;
35702         //return this.collapsed ? this.cmargins : this.margins;
35703     },
35704 /*
35705     highlight : function(){
35706         this.el.addClass("x-layout-panel-dragover");
35707     },
35708
35709     unhighlight : function(){
35710         this.el.removeClass("x-layout-panel-dragover");
35711     },
35712 */
35713     updateBox : function(box)
35714     {
35715         if (!this.bodyEl) {
35716             return; // not rendered yet..
35717         }
35718         
35719         this.box = box;
35720         if(!this.collapsed){
35721             this.el.dom.style.left = box.x + "px";
35722             this.el.dom.style.top = box.y + "px";
35723             this.updateBody(box.width, box.height);
35724         }else{
35725             this.collapsedEl.dom.style.left = box.x + "px";
35726             this.collapsedEl.dom.style.top = box.y + "px";
35727             this.collapsedEl.setSize(box.width, box.height);
35728         }
35729         if(this.tabs){
35730             this.tabs.autoSizeTabs();
35731         }
35732     },
35733
35734     updateBody : function(w, h)
35735     {
35736         if(w !== null){
35737             this.el.setWidth(w);
35738             w -= this.el.getBorderWidth("rl");
35739             if(this.config.adjustments){
35740                 w += this.config.adjustments[0];
35741             }
35742         }
35743         if(h !== null && h > 0){
35744             this.el.setHeight(h);
35745             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35746             h -= this.el.getBorderWidth("tb");
35747             if(this.config.adjustments){
35748                 h += this.config.adjustments[1];
35749             }
35750             this.bodyEl.setHeight(h);
35751             if(this.tabs){
35752                 h = this.tabs.syncHeight(h);
35753             }
35754         }
35755         if(this.panelSize){
35756             w = w !== null ? w : this.panelSize.width;
35757             h = h !== null ? h : this.panelSize.height;
35758         }
35759         if(this.activePanel){
35760             var el = this.activePanel.getEl();
35761             w = w !== null ? w : el.getWidth();
35762             h = h !== null ? h : el.getHeight();
35763             this.panelSize = {width: w, height: h};
35764             this.activePanel.setSize(w, h);
35765         }
35766         if(Roo.isIE && this.tabs){
35767             this.tabs.el.repaint();
35768         }
35769     },
35770
35771     /**
35772      * Returns the container element for this region.
35773      * @return {Roo.Element}
35774      */
35775     getEl : function(){
35776         return this.el;
35777     },
35778
35779     /**
35780      * Hides this region.
35781      */
35782     hide : function(){
35783         //if(!this.collapsed){
35784             this.el.dom.style.left = "-2000px";
35785             this.el.hide();
35786         //}else{
35787          //   this.collapsedEl.dom.style.left = "-2000px";
35788          //   this.collapsedEl.hide();
35789        // }
35790         this.visible = false;
35791         this.fireEvent("visibilitychange", this, false);
35792     },
35793
35794     /**
35795      * Shows this region if it was previously hidden.
35796      */
35797     show : function(){
35798         //if(!this.collapsed){
35799             this.el.show();
35800         //}else{
35801         //    this.collapsedEl.show();
35802        // }
35803         this.visible = true;
35804         this.fireEvent("visibilitychange", this, true);
35805     },
35806 /*
35807     closeClicked : function(){
35808         if(this.activePanel){
35809             this.remove(this.activePanel);
35810         }
35811     },
35812
35813     collapseClick : function(e){
35814         if(this.isSlid){
35815            e.stopPropagation();
35816            this.slideIn();
35817         }else{
35818            e.stopPropagation();
35819            this.slideOut();
35820         }
35821     },
35822 */
35823     /**
35824      * Collapses this region.
35825      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35826      */
35827     /*
35828     collapse : function(skipAnim, skipCheck = false){
35829         if(this.collapsed) {
35830             return;
35831         }
35832         
35833         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35834             
35835             this.collapsed = true;
35836             if(this.split){
35837                 this.split.el.hide();
35838             }
35839             if(this.config.animate && skipAnim !== true){
35840                 this.fireEvent("invalidated", this);
35841                 this.animateCollapse();
35842             }else{
35843                 this.el.setLocation(-20000,-20000);
35844                 this.el.hide();
35845                 this.collapsedEl.show();
35846                 this.fireEvent("collapsed", this);
35847                 this.fireEvent("invalidated", this);
35848             }
35849         }
35850         
35851     },
35852 */
35853     animateCollapse : function(){
35854         // overridden
35855     },
35856
35857     /**
35858      * Expands this region if it was previously collapsed.
35859      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35860      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35861      */
35862     /*
35863     expand : function(e, skipAnim){
35864         if(e) {
35865             e.stopPropagation();
35866         }
35867         if(!this.collapsed || this.el.hasActiveFx()) {
35868             return;
35869         }
35870         if(this.isSlid){
35871             this.afterSlideIn();
35872             skipAnim = true;
35873         }
35874         this.collapsed = false;
35875         if(this.config.animate && skipAnim !== true){
35876             this.animateExpand();
35877         }else{
35878             this.el.show();
35879             if(this.split){
35880                 this.split.el.show();
35881             }
35882             this.collapsedEl.setLocation(-2000,-2000);
35883             this.collapsedEl.hide();
35884             this.fireEvent("invalidated", this);
35885             this.fireEvent("expanded", this);
35886         }
35887     },
35888 */
35889     animateExpand : function(){
35890         // overridden
35891     },
35892
35893     initTabs : function()
35894     {
35895         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35896         
35897         var ts = new Roo.bootstrap.panel.Tabs({
35898                 el: this.bodyEl.dom,
35899                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35900                 disableTooltips: this.config.disableTabTips,
35901                 toolbar : this.config.toolbar
35902             });
35903         
35904         if(this.config.hideTabs){
35905             ts.stripWrap.setDisplayed(false);
35906         }
35907         this.tabs = ts;
35908         ts.resizeTabs = this.config.resizeTabs === true;
35909         ts.minTabWidth = this.config.minTabWidth || 40;
35910         ts.maxTabWidth = this.config.maxTabWidth || 250;
35911         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35912         ts.monitorResize = false;
35913         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35914         ts.bodyEl.addClass('roo-layout-tabs-body');
35915         this.panels.each(this.initPanelAsTab, this);
35916     },
35917
35918     initPanelAsTab : function(panel){
35919         var ti = this.tabs.addTab(
35920             panel.getEl().id,
35921             panel.getTitle(),
35922             null,
35923             this.config.closeOnTab && panel.isClosable(),
35924             panel.tpl
35925         );
35926         if(panel.tabTip !== undefined){
35927             ti.setTooltip(panel.tabTip);
35928         }
35929         ti.on("activate", function(){
35930               this.setActivePanel(panel);
35931         }, this);
35932         
35933         if(this.config.closeOnTab){
35934             ti.on("beforeclose", function(t, e){
35935                 e.cancel = true;
35936                 this.remove(panel);
35937             }, this);
35938         }
35939         
35940         panel.tabItem = ti;
35941         
35942         return ti;
35943     },
35944
35945     updatePanelTitle : function(panel, title)
35946     {
35947         if(this.activePanel == panel){
35948             this.updateTitle(title);
35949         }
35950         if(this.tabs){
35951             var ti = this.tabs.getTab(panel.getEl().id);
35952             ti.setText(title);
35953             if(panel.tabTip !== undefined){
35954                 ti.setTooltip(panel.tabTip);
35955             }
35956         }
35957     },
35958
35959     updateTitle : function(title){
35960         if(this.titleTextEl && !this.config.title){
35961             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35962         }
35963     },
35964
35965     setActivePanel : function(panel)
35966     {
35967         panel = this.getPanel(panel);
35968         if(this.activePanel && this.activePanel != panel){
35969             if(this.activePanel.setActiveState(false) === false){
35970                 return;
35971             }
35972         }
35973         this.activePanel = panel;
35974         panel.setActiveState(true);
35975         if(this.panelSize){
35976             panel.setSize(this.panelSize.width, this.panelSize.height);
35977         }
35978         if(this.closeBtn){
35979             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35980         }
35981         this.updateTitle(panel.getTitle());
35982         if(this.tabs){
35983             this.fireEvent("invalidated", this);
35984         }
35985         this.fireEvent("panelactivated", this, panel);
35986     },
35987
35988     /**
35989      * Shows the specified panel.
35990      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35991      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35992      */
35993     showPanel : function(panel)
35994     {
35995         panel = this.getPanel(panel);
35996         if(panel){
35997             if(this.tabs){
35998                 var tab = this.tabs.getTab(panel.getEl().id);
35999                 if(tab.isHidden()){
36000                     this.tabs.unhideTab(tab.id);
36001                 }
36002                 tab.activate();
36003             }else{
36004                 this.setActivePanel(panel);
36005             }
36006         }
36007         return panel;
36008     },
36009
36010     /**
36011      * Get the active panel for this region.
36012      * @return {Roo.ContentPanel} The active panel or null
36013      */
36014     getActivePanel : function(){
36015         return this.activePanel;
36016     },
36017
36018     validateVisibility : function(){
36019         if(this.panels.getCount() < 1){
36020             this.updateTitle("&#160;");
36021             this.closeBtn.hide();
36022             this.hide();
36023         }else{
36024             if(!this.isVisible()){
36025                 this.show();
36026             }
36027         }
36028     },
36029
36030     /**
36031      * Adds the passed ContentPanel(s) to this region.
36032      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36033      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36034      */
36035     add : function(panel)
36036     {
36037         if(arguments.length > 1){
36038             for(var i = 0, len = arguments.length; i < len; i++) {
36039                 this.add(arguments[i]);
36040             }
36041             return null;
36042         }
36043         
36044         // if we have not been rendered yet, then we can not really do much of this..
36045         if (!this.bodyEl) {
36046             this.unrendered_panels.push(panel);
36047             return panel;
36048         }
36049         
36050         
36051         
36052         
36053         if(this.hasPanel(panel)){
36054             this.showPanel(panel);
36055             return panel;
36056         }
36057         panel.setRegion(this);
36058         this.panels.add(panel);
36059        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36060             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36061             // and hide them... ???
36062             this.bodyEl.dom.appendChild(panel.getEl().dom);
36063             if(panel.background !== true){
36064                 this.setActivePanel(panel);
36065             }
36066             this.fireEvent("paneladded", this, panel);
36067             return panel;
36068         }
36069         */
36070         if(!this.tabs){
36071             this.initTabs();
36072         }else{
36073             this.initPanelAsTab(panel);
36074         }
36075         
36076         
36077         if(panel.background !== true){
36078             this.tabs.activate(panel.getEl().id);
36079         }
36080         this.fireEvent("paneladded", this, panel);
36081         return panel;
36082     },
36083
36084     /**
36085      * Hides the tab for the specified panel.
36086      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36087      */
36088     hidePanel : function(panel){
36089         if(this.tabs && (panel = this.getPanel(panel))){
36090             this.tabs.hideTab(panel.getEl().id);
36091         }
36092     },
36093
36094     /**
36095      * Unhides the tab for a previously hidden panel.
36096      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36097      */
36098     unhidePanel : function(panel){
36099         if(this.tabs && (panel = this.getPanel(panel))){
36100             this.tabs.unhideTab(panel.getEl().id);
36101         }
36102     },
36103
36104     clearPanels : function(){
36105         while(this.panels.getCount() > 0){
36106              this.remove(this.panels.first());
36107         }
36108     },
36109
36110     /**
36111      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36112      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36113      * @param {Boolean} preservePanel Overrides the config preservePanel option
36114      * @return {Roo.ContentPanel} The panel that was removed
36115      */
36116     remove : function(panel, preservePanel)
36117     {
36118         panel = this.getPanel(panel);
36119         if(!panel){
36120             return null;
36121         }
36122         var e = {};
36123         this.fireEvent("beforeremove", this, panel, e);
36124         if(e.cancel === true){
36125             return null;
36126         }
36127         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36128         var panelId = panel.getId();
36129         this.panels.removeKey(panelId);
36130         if(preservePanel){
36131             document.body.appendChild(panel.getEl().dom);
36132         }
36133         if(this.tabs){
36134             this.tabs.removeTab(panel.getEl().id);
36135         }else if (!preservePanel){
36136             this.bodyEl.dom.removeChild(panel.getEl().dom);
36137         }
36138         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36139             var p = this.panels.first();
36140             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36141             tempEl.appendChild(p.getEl().dom);
36142             this.bodyEl.update("");
36143             this.bodyEl.dom.appendChild(p.getEl().dom);
36144             tempEl = null;
36145             this.updateTitle(p.getTitle());
36146             this.tabs = null;
36147             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36148             this.setActivePanel(p);
36149         }
36150         panel.setRegion(null);
36151         if(this.activePanel == panel){
36152             this.activePanel = null;
36153         }
36154         if(this.config.autoDestroy !== false && preservePanel !== true){
36155             try{panel.destroy();}catch(e){}
36156         }
36157         this.fireEvent("panelremoved", this, panel);
36158         return panel;
36159     },
36160
36161     /**
36162      * Returns the TabPanel component used by this region
36163      * @return {Roo.TabPanel}
36164      */
36165     getTabs : function(){
36166         return this.tabs;
36167     },
36168
36169     createTool : function(parentEl, className){
36170         var btn = Roo.DomHelper.append(parentEl, {
36171             tag: "div",
36172             cls: "x-layout-tools-button",
36173             children: [ {
36174                 tag: "div",
36175                 cls: "roo-layout-tools-button-inner " + className,
36176                 html: "&#160;"
36177             }]
36178         }, true);
36179         btn.addClassOnOver("roo-layout-tools-button-over");
36180         return btn;
36181     }
36182 });/*
36183  * Based on:
36184  * Ext JS Library 1.1.1
36185  * Copyright(c) 2006-2007, Ext JS, LLC.
36186  *
36187  * Originally Released Under LGPL - original licence link has changed is not relivant.
36188  *
36189  * Fork - LGPL
36190  * <script type="text/javascript">
36191  */
36192  
36193
36194
36195 /**
36196  * @class Roo.SplitLayoutRegion
36197  * @extends Roo.LayoutRegion
36198  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36199  */
36200 Roo.bootstrap.layout.Split = function(config){
36201     this.cursor = config.cursor;
36202     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36203 };
36204
36205 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36206 {
36207     splitTip : "Drag to resize.",
36208     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36209     useSplitTips : false,
36210
36211     applyConfig : function(config){
36212         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36213     },
36214     
36215     onRender : function(ctr,pos) {
36216         
36217         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36218         if(!this.config.split){
36219             return;
36220         }
36221         if(!this.split){
36222             
36223             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36224                             tag: "div",
36225                             id: this.el.id + "-split",
36226                             cls: "roo-layout-split roo-layout-split-"+this.position,
36227                             html: "&#160;"
36228             });
36229             /** The SplitBar for this region 
36230             * @type Roo.SplitBar */
36231             // does not exist yet...
36232             Roo.log([this.position, this.orientation]);
36233             
36234             this.split = new Roo.bootstrap.SplitBar({
36235                 dragElement : splitEl,
36236                 resizingElement: this.el,
36237                 orientation : this.orientation
36238             });
36239             
36240             this.split.on("moved", this.onSplitMove, this);
36241             this.split.useShim = this.config.useShim === true;
36242             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36243             if(this.useSplitTips){
36244                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36245             }
36246             //if(config.collapsible){
36247             //    this.split.el.on("dblclick", this.collapse,  this);
36248             //}
36249         }
36250         if(typeof this.config.minSize != "undefined"){
36251             this.split.minSize = this.config.minSize;
36252         }
36253         if(typeof this.config.maxSize != "undefined"){
36254             this.split.maxSize = this.config.maxSize;
36255         }
36256         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36257             this.hideSplitter();
36258         }
36259         
36260     },
36261
36262     getHMaxSize : function(){
36263          var cmax = this.config.maxSize || 10000;
36264          var center = this.mgr.getRegion("center");
36265          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36266     },
36267
36268     getVMaxSize : function(){
36269          var cmax = this.config.maxSize || 10000;
36270          var center = this.mgr.getRegion("center");
36271          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36272     },
36273
36274     onSplitMove : function(split, newSize){
36275         this.fireEvent("resized", this, newSize);
36276     },
36277     
36278     /** 
36279      * Returns the {@link Roo.SplitBar} for this region.
36280      * @return {Roo.SplitBar}
36281      */
36282     getSplitBar : function(){
36283         return this.split;
36284     },
36285     
36286     hide : function(){
36287         this.hideSplitter();
36288         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36289     },
36290
36291     hideSplitter : function(){
36292         if(this.split){
36293             this.split.el.setLocation(-2000,-2000);
36294             this.split.el.hide();
36295         }
36296     },
36297
36298     show : function(){
36299         if(this.split){
36300             this.split.el.show();
36301         }
36302         Roo.bootstrap.layout.Split.superclass.show.call(this);
36303     },
36304     
36305     beforeSlide: function(){
36306         if(Roo.isGecko){// firefox overflow auto bug workaround
36307             this.bodyEl.clip();
36308             if(this.tabs) {
36309                 this.tabs.bodyEl.clip();
36310             }
36311             if(this.activePanel){
36312                 this.activePanel.getEl().clip();
36313                 
36314                 if(this.activePanel.beforeSlide){
36315                     this.activePanel.beforeSlide();
36316                 }
36317             }
36318         }
36319     },
36320     
36321     afterSlide : function(){
36322         if(Roo.isGecko){// firefox overflow auto bug workaround
36323             this.bodyEl.unclip();
36324             if(this.tabs) {
36325                 this.tabs.bodyEl.unclip();
36326             }
36327             if(this.activePanel){
36328                 this.activePanel.getEl().unclip();
36329                 if(this.activePanel.afterSlide){
36330                     this.activePanel.afterSlide();
36331                 }
36332             }
36333         }
36334     },
36335
36336     initAutoHide : function(){
36337         if(this.autoHide !== false){
36338             if(!this.autoHideHd){
36339                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36340                 this.autoHideHd = {
36341                     "mouseout": function(e){
36342                         if(!e.within(this.el, true)){
36343                             st.delay(500);
36344                         }
36345                     },
36346                     "mouseover" : function(e){
36347                         st.cancel();
36348                     },
36349                     scope : this
36350                 };
36351             }
36352             this.el.on(this.autoHideHd);
36353         }
36354     },
36355
36356     clearAutoHide : function(){
36357         if(this.autoHide !== false){
36358             this.el.un("mouseout", this.autoHideHd.mouseout);
36359             this.el.un("mouseover", this.autoHideHd.mouseover);
36360         }
36361     },
36362
36363     clearMonitor : function(){
36364         Roo.get(document).un("click", this.slideInIf, this);
36365     },
36366
36367     // these names are backwards but not changed for compat
36368     slideOut : function(){
36369         if(this.isSlid || this.el.hasActiveFx()){
36370             return;
36371         }
36372         this.isSlid = true;
36373         if(this.collapseBtn){
36374             this.collapseBtn.hide();
36375         }
36376         this.closeBtnState = this.closeBtn.getStyle('display');
36377         this.closeBtn.hide();
36378         if(this.stickBtn){
36379             this.stickBtn.show();
36380         }
36381         this.el.show();
36382         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36383         this.beforeSlide();
36384         this.el.setStyle("z-index", 10001);
36385         this.el.slideIn(this.getSlideAnchor(), {
36386             callback: function(){
36387                 this.afterSlide();
36388                 this.initAutoHide();
36389                 Roo.get(document).on("click", this.slideInIf, this);
36390                 this.fireEvent("slideshow", this);
36391             },
36392             scope: this,
36393             block: true
36394         });
36395     },
36396
36397     afterSlideIn : function(){
36398         this.clearAutoHide();
36399         this.isSlid = false;
36400         this.clearMonitor();
36401         this.el.setStyle("z-index", "");
36402         if(this.collapseBtn){
36403             this.collapseBtn.show();
36404         }
36405         this.closeBtn.setStyle('display', this.closeBtnState);
36406         if(this.stickBtn){
36407             this.stickBtn.hide();
36408         }
36409         this.fireEvent("slidehide", this);
36410     },
36411
36412     slideIn : function(cb){
36413         if(!this.isSlid || this.el.hasActiveFx()){
36414             Roo.callback(cb);
36415             return;
36416         }
36417         this.isSlid = false;
36418         this.beforeSlide();
36419         this.el.slideOut(this.getSlideAnchor(), {
36420             callback: function(){
36421                 this.el.setLeftTop(-10000, -10000);
36422                 this.afterSlide();
36423                 this.afterSlideIn();
36424                 Roo.callback(cb);
36425             },
36426             scope: this,
36427             block: true
36428         });
36429     },
36430     
36431     slideInIf : function(e){
36432         if(!e.within(this.el)){
36433             this.slideIn();
36434         }
36435     },
36436
36437     animateCollapse : function(){
36438         this.beforeSlide();
36439         this.el.setStyle("z-index", 20000);
36440         var anchor = this.getSlideAnchor();
36441         this.el.slideOut(anchor, {
36442             callback : function(){
36443                 this.el.setStyle("z-index", "");
36444                 this.collapsedEl.slideIn(anchor, {duration:.3});
36445                 this.afterSlide();
36446                 this.el.setLocation(-10000,-10000);
36447                 this.el.hide();
36448                 this.fireEvent("collapsed", this);
36449             },
36450             scope: this,
36451             block: true
36452         });
36453     },
36454
36455     animateExpand : function(){
36456         this.beforeSlide();
36457         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36458         this.el.setStyle("z-index", 20000);
36459         this.collapsedEl.hide({
36460             duration:.1
36461         });
36462         this.el.slideIn(this.getSlideAnchor(), {
36463             callback : function(){
36464                 this.el.setStyle("z-index", "");
36465                 this.afterSlide();
36466                 if(this.split){
36467                     this.split.el.show();
36468                 }
36469                 this.fireEvent("invalidated", this);
36470                 this.fireEvent("expanded", this);
36471             },
36472             scope: this,
36473             block: true
36474         });
36475     },
36476
36477     anchors : {
36478         "west" : "left",
36479         "east" : "right",
36480         "north" : "top",
36481         "south" : "bottom"
36482     },
36483
36484     sanchors : {
36485         "west" : "l",
36486         "east" : "r",
36487         "north" : "t",
36488         "south" : "b"
36489     },
36490
36491     canchors : {
36492         "west" : "tl-tr",
36493         "east" : "tr-tl",
36494         "north" : "tl-bl",
36495         "south" : "bl-tl"
36496     },
36497
36498     getAnchor : function(){
36499         return this.anchors[this.position];
36500     },
36501
36502     getCollapseAnchor : function(){
36503         return this.canchors[this.position];
36504     },
36505
36506     getSlideAnchor : function(){
36507         return this.sanchors[this.position];
36508     },
36509
36510     getAlignAdj : function(){
36511         var cm = this.cmargins;
36512         switch(this.position){
36513             case "west":
36514                 return [0, 0];
36515             break;
36516             case "east":
36517                 return [0, 0];
36518             break;
36519             case "north":
36520                 return [0, 0];
36521             break;
36522             case "south":
36523                 return [0, 0];
36524             break;
36525         }
36526     },
36527
36528     getExpandAdj : function(){
36529         var c = this.collapsedEl, cm = this.cmargins;
36530         switch(this.position){
36531             case "west":
36532                 return [-(cm.right+c.getWidth()+cm.left), 0];
36533             break;
36534             case "east":
36535                 return [cm.right+c.getWidth()+cm.left, 0];
36536             break;
36537             case "north":
36538                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36539             break;
36540             case "south":
36541                 return [0, cm.top+cm.bottom+c.getHeight()];
36542             break;
36543         }
36544     }
36545 });/*
36546  * Based on:
36547  * Ext JS Library 1.1.1
36548  * Copyright(c) 2006-2007, Ext JS, LLC.
36549  *
36550  * Originally Released Under LGPL - original licence link has changed is not relivant.
36551  *
36552  * Fork - LGPL
36553  * <script type="text/javascript">
36554  */
36555 /*
36556  * These classes are private internal classes
36557  */
36558 Roo.bootstrap.layout.Center = function(config){
36559     config.region = "center";
36560     Roo.bootstrap.layout.Region.call(this, config);
36561     this.visible = true;
36562     this.minWidth = config.minWidth || 20;
36563     this.minHeight = config.minHeight || 20;
36564 };
36565
36566 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36567     hide : function(){
36568         // center panel can't be hidden
36569     },
36570     
36571     show : function(){
36572         // center panel can't be hidden
36573     },
36574     
36575     getMinWidth: function(){
36576         return this.minWidth;
36577     },
36578     
36579     getMinHeight: function(){
36580         return this.minHeight;
36581     }
36582 });
36583
36584
36585
36586
36587  
36588
36589
36590
36591
36592
36593 Roo.bootstrap.layout.North = function(config)
36594 {
36595     config.region = 'north';
36596     config.cursor = 'n-resize';
36597     
36598     Roo.bootstrap.layout.Split.call(this, config);
36599     
36600     
36601     if(this.split){
36602         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36603         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36604         this.split.el.addClass("roo-layout-split-v");
36605     }
36606     var size = config.initialSize || config.height;
36607     if(typeof size != "undefined"){
36608         this.el.setHeight(size);
36609     }
36610 };
36611 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36612 {
36613     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36614     
36615     
36616     
36617     getBox : function(){
36618         if(this.collapsed){
36619             return this.collapsedEl.getBox();
36620         }
36621         var box = this.el.getBox();
36622         if(this.split){
36623             box.height += this.split.el.getHeight();
36624         }
36625         return box;
36626     },
36627     
36628     updateBox : function(box){
36629         if(this.split && !this.collapsed){
36630             box.height -= this.split.el.getHeight();
36631             this.split.el.setLeft(box.x);
36632             this.split.el.setTop(box.y+box.height);
36633             this.split.el.setWidth(box.width);
36634         }
36635         if(this.collapsed){
36636             this.updateBody(box.width, null);
36637         }
36638         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36639     }
36640 });
36641
36642
36643
36644
36645
36646 Roo.bootstrap.layout.South = function(config){
36647     config.region = 'south';
36648     config.cursor = 's-resize';
36649     Roo.bootstrap.layout.Split.call(this, config);
36650     if(this.split){
36651         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36652         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36653         this.split.el.addClass("roo-layout-split-v");
36654     }
36655     var size = config.initialSize || config.height;
36656     if(typeof size != "undefined"){
36657         this.el.setHeight(size);
36658     }
36659 };
36660
36661 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36662     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36663     getBox : function(){
36664         if(this.collapsed){
36665             return this.collapsedEl.getBox();
36666         }
36667         var box = this.el.getBox();
36668         if(this.split){
36669             var sh = this.split.el.getHeight();
36670             box.height += sh;
36671             box.y -= sh;
36672         }
36673         return box;
36674     },
36675     
36676     updateBox : function(box){
36677         if(this.split && !this.collapsed){
36678             var sh = this.split.el.getHeight();
36679             box.height -= sh;
36680             box.y += sh;
36681             this.split.el.setLeft(box.x);
36682             this.split.el.setTop(box.y-sh);
36683             this.split.el.setWidth(box.width);
36684         }
36685         if(this.collapsed){
36686             this.updateBody(box.width, null);
36687         }
36688         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36689     }
36690 });
36691
36692 Roo.bootstrap.layout.East = function(config){
36693     config.region = "east";
36694     config.cursor = "e-resize";
36695     Roo.bootstrap.layout.Split.call(this, config);
36696     if(this.split){
36697         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36698         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36699         this.split.el.addClass("roo-layout-split-h");
36700     }
36701     var size = config.initialSize || config.width;
36702     if(typeof size != "undefined"){
36703         this.el.setWidth(size);
36704     }
36705 };
36706 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36707     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36708     getBox : function(){
36709         if(this.collapsed){
36710             return this.collapsedEl.getBox();
36711         }
36712         var box = this.el.getBox();
36713         if(this.split){
36714             var sw = this.split.el.getWidth();
36715             box.width += sw;
36716             box.x -= sw;
36717         }
36718         return box;
36719     },
36720
36721     updateBox : function(box){
36722         if(this.split && !this.collapsed){
36723             var sw = this.split.el.getWidth();
36724             box.width -= sw;
36725             this.split.el.setLeft(box.x);
36726             this.split.el.setTop(box.y);
36727             this.split.el.setHeight(box.height);
36728             box.x += sw;
36729         }
36730         if(this.collapsed){
36731             this.updateBody(null, box.height);
36732         }
36733         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36734     }
36735 });
36736
36737 Roo.bootstrap.layout.West = function(config){
36738     config.region = "west";
36739     config.cursor = "w-resize";
36740     
36741     Roo.bootstrap.layout.Split.call(this, config);
36742     if(this.split){
36743         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36744         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36745         this.split.el.addClass("roo-layout-split-h");
36746     }
36747     
36748 };
36749 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36750     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36751     
36752     onRender: function(ctr, pos)
36753     {
36754         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36755         var size = this.config.initialSize || this.config.width;
36756         if(typeof size != "undefined"){
36757             this.el.setWidth(size);
36758         }
36759     },
36760     
36761     getBox : function(){
36762         if(this.collapsed){
36763             return this.collapsedEl.getBox();
36764         }
36765         var box = this.el.getBox();
36766         if(this.split){
36767             box.width += this.split.el.getWidth();
36768         }
36769         return box;
36770     },
36771     
36772     updateBox : function(box){
36773         if(this.split && !this.collapsed){
36774             var sw = this.split.el.getWidth();
36775             box.width -= sw;
36776             this.split.el.setLeft(box.x+box.width);
36777             this.split.el.setTop(box.y);
36778             this.split.el.setHeight(box.height);
36779         }
36780         if(this.collapsed){
36781             this.updateBody(null, box.height);
36782         }
36783         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36784     }
36785 });
36786 Roo.namespace("Roo.bootstrap.panel");/*
36787  * Based on:
36788  * Ext JS Library 1.1.1
36789  * Copyright(c) 2006-2007, Ext JS, LLC.
36790  *
36791  * Originally Released Under LGPL - original licence link has changed is not relivant.
36792  *
36793  * Fork - LGPL
36794  * <script type="text/javascript">
36795  */
36796 /**
36797  * @class Roo.ContentPanel
36798  * @extends Roo.util.Observable
36799  * A basic ContentPanel element.
36800  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36801  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36802  * @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
36803  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36804  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36805  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36806  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36807  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36808  * @cfg {String} title          The title for this panel
36809  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36810  * @cfg {String} url            Calls {@link #setUrl} with this value
36811  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36812  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36813  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36814  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36815  * @cfg {Boolean} badges render the badges
36816
36817  * @constructor
36818  * Create a new ContentPanel.
36819  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36820  * @param {String/Object} config A string to set only the title or a config object
36821  * @param {String} content (optional) Set the HTML content for this panel
36822  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36823  */
36824 Roo.bootstrap.panel.Content = function( config){
36825     
36826     this.tpl = config.tpl || false;
36827     
36828     var el = config.el;
36829     var content = config.content;
36830
36831     if(config.autoCreate){ // xtype is available if this is called from factory
36832         el = Roo.id();
36833     }
36834     this.el = Roo.get(el);
36835     if(!this.el && config && config.autoCreate){
36836         if(typeof config.autoCreate == "object"){
36837             if(!config.autoCreate.id){
36838                 config.autoCreate.id = config.id||el;
36839             }
36840             this.el = Roo.DomHelper.append(document.body,
36841                         config.autoCreate, true);
36842         }else{
36843             var elcfg =  {   tag: "div",
36844                             cls: "roo-layout-inactive-content",
36845                             id: config.id||el
36846                             };
36847             if (config.html) {
36848                 elcfg.html = config.html;
36849                 
36850             }
36851                         
36852             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36853         }
36854     } 
36855     this.closable = false;
36856     this.loaded = false;
36857     this.active = false;
36858    
36859       
36860     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36861         
36862         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36863         
36864         this.wrapEl = this.el; //this.el.wrap();
36865         var ti = [];
36866         if (config.toolbar.items) {
36867             ti = config.toolbar.items ;
36868             delete config.toolbar.items ;
36869         }
36870         
36871         var nitems = [];
36872         this.toolbar.render(this.wrapEl, 'before');
36873         for(var i =0;i < ti.length;i++) {
36874           //  Roo.log(['add child', items[i]]);
36875             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36876         }
36877         this.toolbar.items = nitems;
36878         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36879         delete config.toolbar;
36880         
36881     }
36882     /*
36883     // xtype created footer. - not sure if will work as we normally have to render first..
36884     if (this.footer && !this.footer.el && this.footer.xtype) {
36885         if (!this.wrapEl) {
36886             this.wrapEl = this.el.wrap();
36887         }
36888     
36889         this.footer.container = this.wrapEl.createChild();
36890          
36891         this.footer = Roo.factory(this.footer, Roo);
36892         
36893     }
36894     */
36895     
36896      if(typeof config == "string"){
36897         this.title = config;
36898     }else{
36899         Roo.apply(this, config);
36900     }
36901     
36902     if(this.resizeEl){
36903         this.resizeEl = Roo.get(this.resizeEl, true);
36904     }else{
36905         this.resizeEl = this.el;
36906     }
36907     // handle view.xtype
36908     
36909  
36910     
36911     
36912     this.addEvents({
36913         /**
36914          * @event activate
36915          * Fires when this panel is activated. 
36916          * @param {Roo.ContentPanel} this
36917          */
36918         "activate" : true,
36919         /**
36920          * @event deactivate
36921          * Fires when this panel is activated. 
36922          * @param {Roo.ContentPanel} this
36923          */
36924         "deactivate" : true,
36925
36926         /**
36927          * @event resize
36928          * Fires when this panel is resized if fitToFrame is true.
36929          * @param {Roo.ContentPanel} this
36930          * @param {Number} width The width after any component adjustments
36931          * @param {Number} height The height after any component adjustments
36932          */
36933         "resize" : true,
36934         
36935          /**
36936          * @event render
36937          * Fires when this tab is created
36938          * @param {Roo.ContentPanel} this
36939          */
36940         "render" : true
36941         
36942         
36943         
36944     });
36945     
36946
36947     
36948     
36949     if(this.autoScroll){
36950         this.resizeEl.setStyle("overflow", "auto");
36951     } else {
36952         // fix randome scrolling
36953         //this.el.on('scroll', function() {
36954         //    Roo.log('fix random scolling');
36955         //    this.scrollTo('top',0); 
36956         //});
36957     }
36958     content = content || this.content;
36959     if(content){
36960         this.setContent(content);
36961     }
36962     if(config && config.url){
36963         this.setUrl(this.url, this.params, this.loadOnce);
36964     }
36965     
36966     
36967     
36968     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36969     
36970     if (this.view && typeof(this.view.xtype) != 'undefined') {
36971         this.view.el = this.el.appendChild(document.createElement("div"));
36972         this.view = Roo.factory(this.view); 
36973         this.view.render  &&  this.view.render(false, '');  
36974     }
36975     
36976     
36977     this.fireEvent('render', this);
36978 };
36979
36980 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36981     
36982     tabTip : '',
36983     
36984     setRegion : function(region){
36985         this.region = region;
36986         this.setActiveClass(region && !this.background);
36987     },
36988     
36989     
36990     setActiveClass: function(state)
36991     {
36992         if(state){
36993            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36994            this.el.setStyle('position','relative');
36995         }else{
36996            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36997            this.el.setStyle('position', 'absolute');
36998         } 
36999     },
37000     
37001     /**
37002      * Returns the toolbar for this Panel if one was configured. 
37003      * @return {Roo.Toolbar} 
37004      */
37005     getToolbar : function(){
37006         return this.toolbar;
37007     },
37008     
37009     setActiveState : function(active)
37010     {
37011         this.active = active;
37012         this.setActiveClass(active);
37013         if(!active){
37014             if(this.fireEvent("deactivate", this) === false){
37015                 return false;
37016             }
37017             return true;
37018         }
37019         this.fireEvent("activate", this);
37020         return true;
37021     },
37022     /**
37023      * Updates this panel's element
37024      * @param {String} content The new content
37025      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37026     */
37027     setContent : function(content, loadScripts){
37028         this.el.update(content, loadScripts);
37029     },
37030
37031     ignoreResize : function(w, h){
37032         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37033             return true;
37034         }else{
37035             this.lastSize = {width: w, height: h};
37036             return false;
37037         }
37038     },
37039     /**
37040      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37041      * @return {Roo.UpdateManager} The UpdateManager
37042      */
37043     getUpdateManager : function(){
37044         return this.el.getUpdateManager();
37045     },
37046      /**
37047      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37048      * @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:
37049 <pre><code>
37050 panel.load({
37051     url: "your-url.php",
37052     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37053     callback: yourFunction,
37054     scope: yourObject, //(optional scope)
37055     discardUrl: false,
37056     nocache: false,
37057     text: "Loading...",
37058     timeout: 30,
37059     scripts: false
37060 });
37061 </code></pre>
37062      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37063      * 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.
37064      * @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}
37065      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37066      * @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.
37067      * @return {Roo.ContentPanel} this
37068      */
37069     load : function(){
37070         var um = this.el.getUpdateManager();
37071         um.update.apply(um, arguments);
37072         return this;
37073     },
37074
37075
37076     /**
37077      * 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.
37078      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37079      * @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)
37080      * @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)
37081      * @return {Roo.UpdateManager} The UpdateManager
37082      */
37083     setUrl : function(url, params, loadOnce){
37084         if(this.refreshDelegate){
37085             this.removeListener("activate", this.refreshDelegate);
37086         }
37087         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37088         this.on("activate", this.refreshDelegate);
37089         return this.el.getUpdateManager();
37090     },
37091     
37092     _handleRefresh : function(url, params, loadOnce){
37093         if(!loadOnce || !this.loaded){
37094             var updater = this.el.getUpdateManager();
37095             updater.update(url, params, this._setLoaded.createDelegate(this));
37096         }
37097     },
37098     
37099     _setLoaded : function(){
37100         this.loaded = true;
37101     }, 
37102     
37103     /**
37104      * Returns this panel's id
37105      * @return {String} 
37106      */
37107     getId : function(){
37108         return this.el.id;
37109     },
37110     
37111     /** 
37112      * Returns this panel's element - used by regiosn to add.
37113      * @return {Roo.Element} 
37114      */
37115     getEl : function(){
37116         return this.wrapEl || this.el;
37117     },
37118     
37119    
37120     
37121     adjustForComponents : function(width, height)
37122     {
37123         //Roo.log('adjustForComponents ');
37124         if(this.resizeEl != this.el){
37125             width -= this.el.getFrameWidth('lr');
37126             height -= this.el.getFrameWidth('tb');
37127         }
37128         if(this.toolbar){
37129             var te = this.toolbar.getEl();
37130             te.setWidth(width);
37131             height -= te.getHeight();
37132         }
37133         if(this.footer){
37134             var te = this.footer.getEl();
37135             te.setWidth(width);
37136             height -= te.getHeight();
37137         }
37138         
37139         
37140         if(this.adjustments){
37141             width += this.adjustments[0];
37142             height += this.adjustments[1];
37143         }
37144         return {"width": width, "height": height};
37145     },
37146     
37147     setSize : function(width, height){
37148         if(this.fitToFrame && !this.ignoreResize(width, height)){
37149             if(this.fitContainer && this.resizeEl != this.el){
37150                 this.el.setSize(width, height);
37151             }
37152             var size = this.adjustForComponents(width, height);
37153             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37154             this.fireEvent('resize', this, size.width, size.height);
37155         }
37156     },
37157     
37158     /**
37159      * Returns this panel's title
37160      * @return {String} 
37161      */
37162     getTitle : function(){
37163         
37164         if (typeof(this.title) != 'object') {
37165             return this.title;
37166         }
37167         
37168         var t = '';
37169         for (var k in this.title) {
37170             if (!this.title.hasOwnProperty(k)) {
37171                 continue;
37172             }
37173             
37174             if (k.indexOf('-') >= 0) {
37175                 var s = k.split('-');
37176                 for (var i = 0; i<s.length; i++) {
37177                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37178                 }
37179             } else {
37180                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37181             }
37182         }
37183         return t;
37184     },
37185     
37186     /**
37187      * Set this panel's title
37188      * @param {String} title
37189      */
37190     setTitle : function(title){
37191         this.title = title;
37192         if(this.region){
37193             this.region.updatePanelTitle(this, title);
37194         }
37195     },
37196     
37197     /**
37198      * Returns true is this panel was configured to be closable
37199      * @return {Boolean} 
37200      */
37201     isClosable : function(){
37202         return this.closable;
37203     },
37204     
37205     beforeSlide : function(){
37206         this.el.clip();
37207         this.resizeEl.clip();
37208     },
37209     
37210     afterSlide : function(){
37211         this.el.unclip();
37212         this.resizeEl.unclip();
37213     },
37214     
37215     /**
37216      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37217      *   Will fail silently if the {@link #setUrl} method has not been called.
37218      *   This does not activate the panel, just updates its content.
37219      */
37220     refresh : function(){
37221         if(this.refreshDelegate){
37222            this.loaded = false;
37223            this.refreshDelegate();
37224         }
37225     },
37226     
37227     /**
37228      * Destroys this panel
37229      */
37230     destroy : function(){
37231         this.el.removeAllListeners();
37232         var tempEl = document.createElement("span");
37233         tempEl.appendChild(this.el.dom);
37234         tempEl.innerHTML = "";
37235         this.el.remove();
37236         this.el = null;
37237     },
37238     
37239     /**
37240      * form - if the content panel contains a form - this is a reference to it.
37241      * @type {Roo.form.Form}
37242      */
37243     form : false,
37244     /**
37245      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37246      *    This contains a reference to it.
37247      * @type {Roo.View}
37248      */
37249     view : false,
37250     
37251       /**
37252      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37253      * <pre><code>
37254
37255 layout.addxtype({
37256        xtype : 'Form',
37257        items: [ .... ]
37258    }
37259 );
37260
37261 </code></pre>
37262      * @param {Object} cfg Xtype definition of item to add.
37263      */
37264     
37265     
37266     getChildContainer: function () {
37267         return this.getEl();
37268     }
37269     
37270     
37271     /*
37272         var  ret = new Roo.factory(cfg);
37273         return ret;
37274         
37275         
37276         // add form..
37277         if (cfg.xtype.match(/^Form$/)) {
37278             
37279             var el;
37280             //if (this.footer) {
37281             //    el = this.footer.container.insertSibling(false, 'before');
37282             //} else {
37283                 el = this.el.createChild();
37284             //}
37285
37286             this.form = new  Roo.form.Form(cfg);
37287             
37288             
37289             if ( this.form.allItems.length) {
37290                 this.form.render(el.dom);
37291             }
37292             return this.form;
37293         }
37294         // should only have one of theses..
37295         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37296             // views.. should not be just added - used named prop 'view''
37297             
37298             cfg.el = this.el.appendChild(document.createElement("div"));
37299             // factory?
37300             
37301             var ret = new Roo.factory(cfg);
37302              
37303              ret.render && ret.render(false, ''); // render blank..
37304             this.view = ret;
37305             return ret;
37306         }
37307         return false;
37308     }
37309     \*/
37310 });
37311  
37312 /**
37313  * @class Roo.bootstrap.panel.Grid
37314  * @extends Roo.bootstrap.panel.Content
37315  * @constructor
37316  * Create a new GridPanel.
37317  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37318  * @param {Object} config A the config object
37319   
37320  */
37321
37322
37323
37324 Roo.bootstrap.panel.Grid = function(config)
37325 {
37326     
37327       
37328     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37329         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37330
37331     config.el = this.wrapper;
37332     //this.el = this.wrapper;
37333     
37334       if (config.container) {
37335         // ctor'ed from a Border/panel.grid
37336         
37337         
37338         this.wrapper.setStyle("overflow", "hidden");
37339         this.wrapper.addClass('roo-grid-container');
37340
37341     }
37342     
37343     
37344     if(config.toolbar){
37345         var tool_el = this.wrapper.createChild();    
37346         this.toolbar = Roo.factory(config.toolbar);
37347         var ti = [];
37348         if (config.toolbar.items) {
37349             ti = config.toolbar.items ;
37350             delete config.toolbar.items ;
37351         }
37352         
37353         var nitems = [];
37354         this.toolbar.render(tool_el);
37355         for(var i =0;i < ti.length;i++) {
37356           //  Roo.log(['add child', items[i]]);
37357             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37358         }
37359         this.toolbar.items = nitems;
37360         
37361         delete config.toolbar;
37362     }
37363     
37364     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37365     config.grid.scrollBody = true;;
37366     config.grid.monitorWindowResize = false; // turn off autosizing
37367     config.grid.autoHeight = false;
37368     config.grid.autoWidth = false;
37369     
37370     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37371     
37372     if (config.background) {
37373         // render grid on panel activation (if panel background)
37374         this.on('activate', function(gp) {
37375             if (!gp.grid.rendered) {
37376                 gp.grid.render(this.wrapper);
37377                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37378             }
37379         });
37380             
37381     } else {
37382         this.grid.render(this.wrapper);
37383         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37384
37385     }
37386     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37387     // ??? needed ??? config.el = this.wrapper;
37388     
37389     
37390     
37391   
37392     // xtype created footer. - not sure if will work as we normally have to render first..
37393     if (this.footer && !this.footer.el && this.footer.xtype) {
37394         
37395         var ctr = this.grid.getView().getFooterPanel(true);
37396         this.footer.dataSource = this.grid.dataSource;
37397         this.footer = Roo.factory(this.footer, Roo);
37398         this.footer.render(ctr);
37399         
37400     }
37401     
37402     
37403     
37404     
37405      
37406 };
37407
37408 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37409     getId : function(){
37410         return this.grid.id;
37411     },
37412     
37413     /**
37414      * Returns the grid for this panel
37415      * @return {Roo.bootstrap.Table} 
37416      */
37417     getGrid : function(){
37418         return this.grid;    
37419     },
37420     
37421     setSize : function(width, height){
37422         if(!this.ignoreResize(width, height)){
37423             var grid = this.grid;
37424             var size = this.adjustForComponents(width, height);
37425             var gridel = grid.getGridEl();
37426             gridel.setSize(size.width, size.height);
37427             /*
37428             var thd = grid.getGridEl().select('thead',true).first();
37429             var tbd = grid.getGridEl().select('tbody', true).first();
37430             if (tbd) {
37431                 tbd.setSize(width, height - thd.getHeight());
37432             }
37433             */
37434             grid.autoSize();
37435         }
37436     },
37437      
37438     
37439     
37440     beforeSlide : function(){
37441         this.grid.getView().scroller.clip();
37442     },
37443     
37444     afterSlide : function(){
37445         this.grid.getView().scroller.unclip();
37446     },
37447     
37448     destroy : function(){
37449         this.grid.destroy();
37450         delete this.grid;
37451         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37452     }
37453 });
37454
37455 /**
37456  * @class Roo.bootstrap.panel.Nest
37457  * @extends Roo.bootstrap.panel.Content
37458  * @constructor
37459  * Create a new Panel, that can contain a layout.Border.
37460  * 
37461  * 
37462  * @param {Roo.BorderLayout} layout The layout for this panel
37463  * @param {String/Object} config A string to set only the title or a config object
37464  */
37465 Roo.bootstrap.panel.Nest = function(config)
37466 {
37467     // construct with only one argument..
37468     /* FIXME - implement nicer consturctors
37469     if (layout.layout) {
37470         config = layout;
37471         layout = config.layout;
37472         delete config.layout;
37473     }
37474     if (layout.xtype && !layout.getEl) {
37475         // then layout needs constructing..
37476         layout = Roo.factory(layout, Roo);
37477     }
37478     */
37479     
37480     config.el =  config.layout.getEl();
37481     
37482     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37483     
37484     config.layout.monitorWindowResize = false; // turn off autosizing
37485     this.layout = config.layout;
37486     this.layout.getEl().addClass("roo-layout-nested-layout");
37487     
37488     
37489     
37490     
37491 };
37492
37493 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37494
37495     setSize : function(width, height){
37496         if(!this.ignoreResize(width, height)){
37497             var size = this.adjustForComponents(width, height);
37498             var el = this.layout.getEl();
37499             if (size.height < 1) {
37500                 el.setWidth(size.width);   
37501             } else {
37502                 el.setSize(size.width, size.height);
37503             }
37504             var touch = el.dom.offsetWidth;
37505             this.layout.layout();
37506             // ie requires a double layout on the first pass
37507             if(Roo.isIE && !this.initialized){
37508                 this.initialized = true;
37509                 this.layout.layout();
37510             }
37511         }
37512     },
37513     
37514     // activate all subpanels if not currently active..
37515     
37516     setActiveState : function(active){
37517         this.active = active;
37518         this.setActiveClass(active);
37519         
37520         if(!active){
37521             this.fireEvent("deactivate", this);
37522             return;
37523         }
37524         
37525         this.fireEvent("activate", this);
37526         // not sure if this should happen before or after..
37527         if (!this.layout) {
37528             return; // should not happen..
37529         }
37530         var reg = false;
37531         for (var r in this.layout.regions) {
37532             reg = this.layout.getRegion(r);
37533             if (reg.getActivePanel()) {
37534                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37535                 reg.setActivePanel(reg.getActivePanel());
37536                 continue;
37537             }
37538             if (!reg.panels.length) {
37539                 continue;
37540             }
37541             reg.showPanel(reg.getPanel(0));
37542         }
37543         
37544         
37545         
37546         
37547     },
37548     
37549     /**
37550      * Returns the nested BorderLayout for this panel
37551      * @return {Roo.BorderLayout} 
37552      */
37553     getLayout : function(){
37554         return this.layout;
37555     },
37556     
37557      /**
37558      * Adds a xtype elements to the layout of the nested panel
37559      * <pre><code>
37560
37561 panel.addxtype({
37562        xtype : 'ContentPanel',
37563        region: 'west',
37564        items: [ .... ]
37565    }
37566 );
37567
37568 panel.addxtype({
37569         xtype : 'NestedLayoutPanel',
37570         region: 'west',
37571         layout: {
37572            center: { },
37573            west: { }   
37574         },
37575         items : [ ... list of content panels or nested layout panels.. ]
37576    }
37577 );
37578 </code></pre>
37579      * @param {Object} cfg Xtype definition of item to add.
37580      */
37581     addxtype : function(cfg) {
37582         return this.layout.addxtype(cfg);
37583     
37584     }
37585 });        /*
37586  * Based on:
37587  * Ext JS Library 1.1.1
37588  * Copyright(c) 2006-2007, Ext JS, LLC.
37589  *
37590  * Originally Released Under LGPL - original licence link has changed is not relivant.
37591  *
37592  * Fork - LGPL
37593  * <script type="text/javascript">
37594  */
37595 /**
37596  * @class Roo.TabPanel
37597  * @extends Roo.util.Observable
37598  * A lightweight tab container.
37599  * <br><br>
37600  * Usage:
37601  * <pre><code>
37602 // basic tabs 1, built from existing content
37603 var tabs = new Roo.TabPanel("tabs1");
37604 tabs.addTab("script", "View Script");
37605 tabs.addTab("markup", "View Markup");
37606 tabs.activate("script");
37607
37608 // more advanced tabs, built from javascript
37609 var jtabs = new Roo.TabPanel("jtabs");
37610 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37611
37612 // set up the UpdateManager
37613 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37614 var updater = tab2.getUpdateManager();
37615 updater.setDefaultUrl("ajax1.htm");
37616 tab2.on('activate', updater.refresh, updater, true);
37617
37618 // Use setUrl for Ajax loading
37619 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37620 tab3.setUrl("ajax2.htm", null, true);
37621
37622 // Disabled tab
37623 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37624 tab4.disable();
37625
37626 jtabs.activate("jtabs-1");
37627  * </code></pre>
37628  * @constructor
37629  * Create a new TabPanel.
37630  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37631  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37632  */
37633 Roo.bootstrap.panel.Tabs = function(config){
37634     /**
37635     * The container element for this TabPanel.
37636     * @type Roo.Element
37637     */
37638     this.el = Roo.get(config.el);
37639     delete config.el;
37640     if(config){
37641         if(typeof config == "boolean"){
37642             this.tabPosition = config ? "bottom" : "top";
37643         }else{
37644             Roo.apply(this, config);
37645         }
37646     }
37647     
37648     if(this.tabPosition == "bottom"){
37649         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37650         this.el.addClass("roo-tabs-bottom");
37651     }
37652     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37653     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37654     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37655     if(Roo.isIE){
37656         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37657     }
37658     if(this.tabPosition != "bottom"){
37659         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37660          * @type Roo.Element
37661          */
37662         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37663         this.el.addClass("roo-tabs-top");
37664     }
37665     this.items = [];
37666
37667     this.bodyEl.setStyle("position", "relative");
37668
37669     this.active = null;
37670     this.activateDelegate = this.activate.createDelegate(this);
37671
37672     this.addEvents({
37673         /**
37674          * @event tabchange
37675          * Fires when the active tab changes
37676          * @param {Roo.TabPanel} this
37677          * @param {Roo.TabPanelItem} activePanel The new active tab
37678          */
37679         "tabchange": true,
37680         /**
37681          * @event beforetabchange
37682          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37683          * @param {Roo.TabPanel} this
37684          * @param {Object} e Set cancel to true on this object to cancel the tab change
37685          * @param {Roo.TabPanelItem} tab The tab being changed to
37686          */
37687         "beforetabchange" : true
37688     });
37689
37690     Roo.EventManager.onWindowResize(this.onResize, this);
37691     this.cpad = this.el.getPadding("lr");
37692     this.hiddenCount = 0;
37693
37694
37695     // toolbar on the tabbar support...
37696     if (this.toolbar) {
37697         alert("no toolbar support yet");
37698         this.toolbar  = false;
37699         /*
37700         var tcfg = this.toolbar;
37701         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37702         this.toolbar = new Roo.Toolbar(tcfg);
37703         if (Roo.isSafari) {
37704             var tbl = tcfg.container.child('table', true);
37705             tbl.setAttribute('width', '100%');
37706         }
37707         */
37708         
37709     }
37710    
37711
37712
37713     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37714 };
37715
37716 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37717     /*
37718      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37719      */
37720     tabPosition : "top",
37721     /*
37722      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37723      */
37724     currentTabWidth : 0,
37725     /*
37726      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37727      */
37728     minTabWidth : 40,
37729     /*
37730      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37731      */
37732     maxTabWidth : 250,
37733     /*
37734      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37735      */
37736     preferredTabWidth : 175,
37737     /*
37738      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37739      */
37740     resizeTabs : false,
37741     /*
37742      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37743      */
37744     monitorResize : true,
37745     /*
37746      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37747      */
37748     toolbar : false,
37749
37750     /**
37751      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37752      * @param {String} id The id of the div to use <b>or create</b>
37753      * @param {String} text The text for the tab
37754      * @param {String} content (optional) Content to put in the TabPanelItem body
37755      * @param {Boolean} closable (optional) True to create a close icon on the tab
37756      * @return {Roo.TabPanelItem} The created TabPanelItem
37757      */
37758     addTab : function(id, text, content, closable, tpl)
37759     {
37760         var item = new Roo.bootstrap.panel.TabItem({
37761             panel: this,
37762             id : id,
37763             text : text,
37764             closable : closable,
37765             tpl : tpl
37766         });
37767         this.addTabItem(item);
37768         if(content){
37769             item.setContent(content);
37770         }
37771         return item;
37772     },
37773
37774     /**
37775      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37776      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37777      * @return {Roo.TabPanelItem}
37778      */
37779     getTab : function(id){
37780         return this.items[id];
37781     },
37782
37783     /**
37784      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37785      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37786      */
37787     hideTab : function(id){
37788         var t = this.items[id];
37789         if(!t.isHidden()){
37790            t.setHidden(true);
37791            this.hiddenCount++;
37792            this.autoSizeTabs();
37793         }
37794     },
37795
37796     /**
37797      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37798      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37799      */
37800     unhideTab : function(id){
37801         var t = this.items[id];
37802         if(t.isHidden()){
37803            t.setHidden(false);
37804            this.hiddenCount--;
37805            this.autoSizeTabs();
37806         }
37807     },
37808
37809     /**
37810      * Adds an existing {@link Roo.TabPanelItem}.
37811      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37812      */
37813     addTabItem : function(item){
37814         this.items[item.id] = item;
37815         this.items.push(item);
37816       //  if(this.resizeTabs){
37817     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37818   //         this.autoSizeTabs();
37819 //        }else{
37820 //            item.autoSize();
37821        // }
37822     },
37823
37824     /**
37825      * Removes a {@link Roo.TabPanelItem}.
37826      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37827      */
37828     removeTab : function(id){
37829         var items = this.items;
37830         var tab = items[id];
37831         if(!tab) { return; }
37832         var index = items.indexOf(tab);
37833         if(this.active == tab && items.length > 1){
37834             var newTab = this.getNextAvailable(index);
37835             if(newTab) {
37836                 newTab.activate();
37837             }
37838         }
37839         this.stripEl.dom.removeChild(tab.pnode.dom);
37840         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37841             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37842         }
37843         items.splice(index, 1);
37844         delete this.items[tab.id];
37845         tab.fireEvent("close", tab);
37846         tab.purgeListeners();
37847         this.autoSizeTabs();
37848     },
37849
37850     getNextAvailable : function(start){
37851         var items = this.items;
37852         var index = start;
37853         // look for a next tab that will slide over to
37854         // replace the one being removed
37855         while(index < items.length){
37856             var item = items[++index];
37857             if(item && !item.isHidden()){
37858                 return item;
37859             }
37860         }
37861         // if one isn't found select the previous tab (on the left)
37862         index = start;
37863         while(index >= 0){
37864             var item = items[--index];
37865             if(item && !item.isHidden()){
37866                 return item;
37867             }
37868         }
37869         return null;
37870     },
37871
37872     /**
37873      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37874      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37875      */
37876     disableTab : function(id){
37877         var tab = this.items[id];
37878         if(tab && this.active != tab){
37879             tab.disable();
37880         }
37881     },
37882
37883     /**
37884      * Enables a {@link Roo.TabPanelItem} that is disabled.
37885      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37886      */
37887     enableTab : function(id){
37888         var tab = this.items[id];
37889         tab.enable();
37890     },
37891
37892     /**
37893      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37894      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37895      * @return {Roo.TabPanelItem} The TabPanelItem.
37896      */
37897     activate : function(id){
37898         var tab = this.items[id];
37899         if(!tab){
37900             return null;
37901         }
37902         if(tab == this.active || tab.disabled){
37903             return tab;
37904         }
37905         var e = {};
37906         this.fireEvent("beforetabchange", this, e, tab);
37907         if(e.cancel !== true && !tab.disabled){
37908             if(this.active){
37909                 this.active.hide();
37910             }
37911             this.active = this.items[id];
37912             this.active.show();
37913             this.fireEvent("tabchange", this, this.active);
37914         }
37915         return tab;
37916     },
37917
37918     /**
37919      * Gets the active {@link Roo.TabPanelItem}.
37920      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37921      */
37922     getActiveTab : function(){
37923         return this.active;
37924     },
37925
37926     /**
37927      * Updates the tab body element to fit the height of the container element
37928      * for overflow scrolling
37929      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37930      */
37931     syncHeight : function(targetHeight){
37932         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37933         var bm = this.bodyEl.getMargins();
37934         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37935         this.bodyEl.setHeight(newHeight);
37936         return newHeight;
37937     },
37938
37939     onResize : function(){
37940         if(this.monitorResize){
37941             this.autoSizeTabs();
37942         }
37943     },
37944
37945     /**
37946      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37947      */
37948     beginUpdate : function(){
37949         this.updating = true;
37950     },
37951
37952     /**
37953      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37954      */
37955     endUpdate : function(){
37956         this.updating = false;
37957         this.autoSizeTabs();
37958     },
37959
37960     /**
37961      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37962      */
37963     autoSizeTabs : function(){
37964         var count = this.items.length;
37965         var vcount = count - this.hiddenCount;
37966         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37967             return;
37968         }
37969         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37970         var availWidth = Math.floor(w / vcount);
37971         var b = this.stripBody;
37972         if(b.getWidth() > w){
37973             var tabs = this.items;
37974             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37975             if(availWidth < this.minTabWidth){
37976                 /*if(!this.sleft){    // incomplete scrolling code
37977                     this.createScrollButtons();
37978                 }
37979                 this.showScroll();
37980                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37981             }
37982         }else{
37983             if(this.currentTabWidth < this.preferredTabWidth){
37984                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37985             }
37986         }
37987     },
37988
37989     /**
37990      * Returns the number of tabs in this TabPanel.
37991      * @return {Number}
37992      */
37993      getCount : function(){
37994          return this.items.length;
37995      },
37996
37997     /**
37998      * Resizes all the tabs to the passed width
37999      * @param {Number} The new width
38000      */
38001     setTabWidth : function(width){
38002         this.currentTabWidth = width;
38003         for(var i = 0, len = this.items.length; i < len; i++) {
38004                 if(!this.items[i].isHidden()) {
38005                 this.items[i].setWidth(width);
38006             }
38007         }
38008     },
38009
38010     /**
38011      * Destroys this TabPanel
38012      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38013      */
38014     destroy : function(removeEl){
38015         Roo.EventManager.removeResizeListener(this.onResize, this);
38016         for(var i = 0, len = this.items.length; i < len; i++){
38017             this.items[i].purgeListeners();
38018         }
38019         if(removeEl === true){
38020             this.el.update("");
38021             this.el.remove();
38022         }
38023     },
38024     
38025     createStrip : function(container)
38026     {
38027         var strip = document.createElement("nav");
38028         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38029         container.appendChild(strip);
38030         return strip;
38031     },
38032     
38033     createStripList : function(strip)
38034     {
38035         // div wrapper for retard IE
38036         // returns the "tr" element.
38037         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38038         //'<div class="x-tabs-strip-wrap">'+
38039           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38040           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38041         return strip.firstChild; //.firstChild.firstChild.firstChild;
38042     },
38043     createBody : function(container)
38044     {
38045         var body = document.createElement("div");
38046         Roo.id(body, "tab-body");
38047         //Roo.fly(body).addClass("x-tabs-body");
38048         Roo.fly(body).addClass("tab-content");
38049         container.appendChild(body);
38050         return body;
38051     },
38052     createItemBody :function(bodyEl, id){
38053         var body = Roo.getDom(id);
38054         if(!body){
38055             body = document.createElement("div");
38056             body.id = id;
38057         }
38058         //Roo.fly(body).addClass("x-tabs-item-body");
38059         Roo.fly(body).addClass("tab-pane");
38060          bodyEl.insertBefore(body, bodyEl.firstChild);
38061         return body;
38062     },
38063     /** @private */
38064     createStripElements :  function(stripEl, text, closable, tpl)
38065     {
38066         var td = document.createElement("li"); // was td..
38067         
38068         
38069         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38070         
38071         
38072         stripEl.appendChild(td);
38073         /*if(closable){
38074             td.className = "x-tabs-closable";
38075             if(!this.closeTpl){
38076                 this.closeTpl = new Roo.Template(
38077                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38078                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38079                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38080                 );
38081             }
38082             var el = this.closeTpl.overwrite(td, {"text": text});
38083             var close = el.getElementsByTagName("div")[0];
38084             var inner = el.getElementsByTagName("em")[0];
38085             return {"el": el, "close": close, "inner": inner};
38086         } else {
38087         */
38088         // not sure what this is..
38089 //            if(!this.tabTpl){
38090                 //this.tabTpl = new Roo.Template(
38091                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38092                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38093                 //);
38094 //                this.tabTpl = new Roo.Template(
38095 //                   '<a href="#">' +
38096 //                   '<span unselectable="on"' +
38097 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38098 //                            ' >{text}</span></a>'
38099 //                );
38100 //                
38101 //            }
38102
38103
38104             var template = tpl || this.tabTpl || false;
38105             
38106             if(!template){
38107                 
38108                 template = new Roo.Template(
38109                    '<a href="#">' +
38110                    '<span unselectable="on"' +
38111                             (this.disableTooltips ? '' : ' title="{text}"') +
38112                             ' >{text}</span></a>'
38113                 );
38114             }
38115             
38116             switch (typeof(template)) {
38117                 case 'object' :
38118                     break;
38119                 case 'string' :
38120                     template = new Roo.Template(template);
38121                     break;
38122                 default :
38123                     break;
38124             }
38125             
38126             var el = template.overwrite(td, {"text": text});
38127             
38128             var inner = el.getElementsByTagName("span")[0];
38129             
38130             return {"el": el, "inner": inner};
38131             
38132     }
38133         
38134     
38135 });
38136
38137 /**
38138  * @class Roo.TabPanelItem
38139  * @extends Roo.util.Observable
38140  * Represents an individual item (tab plus body) in a TabPanel.
38141  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38142  * @param {String} id The id of this TabPanelItem
38143  * @param {String} text The text for the tab of this TabPanelItem
38144  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38145  */
38146 Roo.bootstrap.panel.TabItem = function(config){
38147     /**
38148      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38149      * @type Roo.TabPanel
38150      */
38151     this.tabPanel = config.panel;
38152     /**
38153      * The id for this TabPanelItem
38154      * @type String
38155      */
38156     this.id = config.id;
38157     /** @private */
38158     this.disabled = false;
38159     /** @private */
38160     this.text = config.text;
38161     /** @private */
38162     this.loaded = false;
38163     this.closable = config.closable;
38164
38165     /**
38166      * The body element for this TabPanelItem.
38167      * @type Roo.Element
38168      */
38169     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38170     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38171     this.bodyEl.setStyle("display", "block");
38172     this.bodyEl.setStyle("zoom", "1");
38173     //this.hideAction();
38174
38175     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38176     /** @private */
38177     this.el = Roo.get(els.el);
38178     this.inner = Roo.get(els.inner, true);
38179     this.textEl = Roo.get(this.el.dom.firstChild, true);
38180     this.pnode = Roo.get(els.el.parentNode, true);
38181 //    this.el.on("mousedown", this.onTabMouseDown, this);
38182     this.el.on("click", this.onTabClick, this);
38183     /** @private */
38184     if(config.closable){
38185         var c = Roo.get(els.close, true);
38186         c.dom.title = this.closeText;
38187         c.addClassOnOver("close-over");
38188         c.on("click", this.closeClick, this);
38189      }
38190
38191     this.addEvents({
38192          /**
38193          * @event activate
38194          * Fires when this tab becomes the active tab.
38195          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38196          * @param {Roo.TabPanelItem} this
38197          */
38198         "activate": true,
38199         /**
38200          * @event beforeclose
38201          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38202          * @param {Roo.TabPanelItem} this
38203          * @param {Object} e Set cancel to true on this object to cancel the close.
38204          */
38205         "beforeclose": true,
38206         /**
38207          * @event close
38208          * Fires when this tab is closed.
38209          * @param {Roo.TabPanelItem} this
38210          */
38211          "close": true,
38212         /**
38213          * @event deactivate
38214          * Fires when this tab is no longer the active tab.
38215          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38216          * @param {Roo.TabPanelItem} this
38217          */
38218          "deactivate" : true
38219     });
38220     this.hidden = false;
38221
38222     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38223 };
38224
38225 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38226            {
38227     purgeListeners : function(){
38228        Roo.util.Observable.prototype.purgeListeners.call(this);
38229        this.el.removeAllListeners();
38230     },
38231     /**
38232      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38233      */
38234     show : function(){
38235         this.pnode.addClass("active");
38236         this.showAction();
38237         if(Roo.isOpera){
38238             this.tabPanel.stripWrap.repaint();
38239         }
38240         this.fireEvent("activate", this.tabPanel, this);
38241     },
38242
38243     /**
38244      * Returns true if this tab is the active tab.
38245      * @return {Boolean}
38246      */
38247     isActive : function(){
38248         return this.tabPanel.getActiveTab() == this;
38249     },
38250
38251     /**
38252      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38253      */
38254     hide : function(){
38255         this.pnode.removeClass("active");
38256         this.hideAction();
38257         this.fireEvent("deactivate", this.tabPanel, this);
38258     },
38259
38260     hideAction : function(){
38261         this.bodyEl.hide();
38262         this.bodyEl.setStyle("position", "absolute");
38263         this.bodyEl.setLeft("-20000px");
38264         this.bodyEl.setTop("-20000px");
38265     },
38266
38267     showAction : function(){
38268         this.bodyEl.setStyle("position", "relative");
38269         this.bodyEl.setTop("");
38270         this.bodyEl.setLeft("");
38271         this.bodyEl.show();
38272     },
38273
38274     /**
38275      * Set the tooltip for the tab.
38276      * @param {String} tooltip The tab's tooltip
38277      */
38278     setTooltip : function(text){
38279         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38280             this.textEl.dom.qtip = text;
38281             this.textEl.dom.removeAttribute('title');
38282         }else{
38283             this.textEl.dom.title = text;
38284         }
38285     },
38286
38287     onTabClick : function(e){
38288         e.preventDefault();
38289         this.tabPanel.activate(this.id);
38290     },
38291
38292     onTabMouseDown : function(e){
38293         e.preventDefault();
38294         this.tabPanel.activate(this.id);
38295     },
38296 /*
38297     getWidth : function(){
38298         return this.inner.getWidth();
38299     },
38300
38301     setWidth : function(width){
38302         var iwidth = width - this.pnode.getPadding("lr");
38303         this.inner.setWidth(iwidth);
38304         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38305         this.pnode.setWidth(width);
38306     },
38307 */
38308     /**
38309      * Show or hide the tab
38310      * @param {Boolean} hidden True to hide or false to show.
38311      */
38312     setHidden : function(hidden){
38313         this.hidden = hidden;
38314         this.pnode.setStyle("display", hidden ? "none" : "");
38315     },
38316
38317     /**
38318      * Returns true if this tab is "hidden"
38319      * @return {Boolean}
38320      */
38321     isHidden : function(){
38322         return this.hidden;
38323     },
38324
38325     /**
38326      * Returns the text for this tab
38327      * @return {String}
38328      */
38329     getText : function(){
38330         return this.text;
38331     },
38332     /*
38333     autoSize : function(){
38334         //this.el.beginMeasure();
38335         this.textEl.setWidth(1);
38336         /*
38337          *  #2804 [new] Tabs in Roojs
38338          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38339          */
38340         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38341         //this.el.endMeasure();
38342     //},
38343
38344     /**
38345      * Sets the text for the tab (Note: this also sets the tooltip text)
38346      * @param {String} text The tab's text and tooltip
38347      */
38348     setText : function(text){
38349         this.text = text;
38350         this.textEl.update(text);
38351         this.setTooltip(text);
38352         //if(!this.tabPanel.resizeTabs){
38353         //    this.autoSize();
38354         //}
38355     },
38356     /**
38357      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38358      */
38359     activate : function(){
38360         this.tabPanel.activate(this.id);
38361     },
38362
38363     /**
38364      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38365      */
38366     disable : function(){
38367         if(this.tabPanel.active != this){
38368             this.disabled = true;
38369             this.pnode.addClass("disabled");
38370         }
38371     },
38372
38373     /**
38374      * Enables this TabPanelItem if it was previously disabled.
38375      */
38376     enable : function(){
38377         this.disabled = false;
38378         this.pnode.removeClass("disabled");
38379     },
38380
38381     /**
38382      * Sets the content for this TabPanelItem.
38383      * @param {String} content The content
38384      * @param {Boolean} loadScripts true to look for and load scripts
38385      */
38386     setContent : function(content, loadScripts){
38387         this.bodyEl.update(content, loadScripts);
38388     },
38389
38390     /**
38391      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38392      * @return {Roo.UpdateManager} The UpdateManager
38393      */
38394     getUpdateManager : function(){
38395         return this.bodyEl.getUpdateManager();
38396     },
38397
38398     /**
38399      * Set a URL to be used to load the content for this TabPanelItem.
38400      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38401      * @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)
38402      * @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)
38403      * @return {Roo.UpdateManager} The UpdateManager
38404      */
38405     setUrl : function(url, params, loadOnce){
38406         if(this.refreshDelegate){
38407             this.un('activate', this.refreshDelegate);
38408         }
38409         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38410         this.on("activate", this.refreshDelegate);
38411         return this.bodyEl.getUpdateManager();
38412     },
38413
38414     /** @private */
38415     _handleRefresh : function(url, params, loadOnce){
38416         if(!loadOnce || !this.loaded){
38417             var updater = this.bodyEl.getUpdateManager();
38418             updater.update(url, params, this._setLoaded.createDelegate(this));
38419         }
38420     },
38421
38422     /**
38423      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38424      *   Will fail silently if the setUrl method has not been called.
38425      *   This does not activate the panel, just updates its content.
38426      */
38427     refresh : function(){
38428         if(this.refreshDelegate){
38429            this.loaded = false;
38430            this.refreshDelegate();
38431         }
38432     },
38433
38434     /** @private */
38435     _setLoaded : function(){
38436         this.loaded = true;
38437     },
38438
38439     /** @private */
38440     closeClick : function(e){
38441         var o = {};
38442         e.stopEvent();
38443         this.fireEvent("beforeclose", this, o);
38444         if(o.cancel !== true){
38445             this.tabPanel.removeTab(this.id);
38446         }
38447     },
38448     /**
38449      * The text displayed in the tooltip for the close icon.
38450      * @type String
38451      */
38452     closeText : "Close this tab"
38453 });
38454 /**
38455 *    This script refer to:
38456 *    Title: International Telephone Input
38457 *    Author: Jack O'Connor
38458 *    Code version:  v12.1.12
38459 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38460 **/
38461
38462 Roo.bootstrap.PhoneInputData = function() {
38463     var d = [
38464       [
38465         "Afghanistan (‫افغانستان‬‎)",
38466         "af",
38467         "93"
38468       ],
38469       [
38470         "Albania (Shqipëri)",
38471         "al",
38472         "355"
38473       ],
38474       [
38475         "Algeria (‫الجزائر‬‎)",
38476         "dz",
38477         "213"
38478       ],
38479       [
38480         "American Samoa",
38481         "as",
38482         "1684"
38483       ],
38484       [
38485         "Andorra",
38486         "ad",
38487         "376"
38488       ],
38489       [
38490         "Angola",
38491         "ao",
38492         "244"
38493       ],
38494       [
38495         "Anguilla",
38496         "ai",
38497         "1264"
38498       ],
38499       [
38500         "Antigua and Barbuda",
38501         "ag",
38502         "1268"
38503       ],
38504       [
38505         "Argentina",
38506         "ar",
38507         "54"
38508       ],
38509       [
38510         "Armenia (Հայաստան)",
38511         "am",
38512         "374"
38513       ],
38514       [
38515         "Aruba",
38516         "aw",
38517         "297"
38518       ],
38519       [
38520         "Australia",
38521         "au",
38522         "61",
38523         0
38524       ],
38525       [
38526         "Austria (Österreich)",
38527         "at",
38528         "43"
38529       ],
38530       [
38531         "Azerbaijan (Azərbaycan)",
38532         "az",
38533         "994"
38534       ],
38535       [
38536         "Bahamas",
38537         "bs",
38538         "1242"
38539       ],
38540       [
38541         "Bahrain (‫البحرين‬‎)",
38542         "bh",
38543         "973"
38544       ],
38545       [
38546         "Bangladesh (বাংলাদেশ)",
38547         "bd",
38548         "880"
38549       ],
38550       [
38551         "Barbados",
38552         "bb",
38553         "1246"
38554       ],
38555       [
38556         "Belarus (Беларусь)",
38557         "by",
38558         "375"
38559       ],
38560       [
38561         "Belgium (België)",
38562         "be",
38563         "32"
38564       ],
38565       [
38566         "Belize",
38567         "bz",
38568         "501"
38569       ],
38570       [
38571         "Benin (Bénin)",
38572         "bj",
38573         "229"
38574       ],
38575       [
38576         "Bermuda",
38577         "bm",
38578         "1441"
38579       ],
38580       [
38581         "Bhutan (འབྲུག)",
38582         "bt",
38583         "975"
38584       ],
38585       [
38586         "Bolivia",
38587         "bo",
38588         "591"
38589       ],
38590       [
38591         "Bosnia and Herzegovina (Босна и Херцеговина)",
38592         "ba",
38593         "387"
38594       ],
38595       [
38596         "Botswana",
38597         "bw",
38598         "267"
38599       ],
38600       [
38601         "Brazil (Brasil)",
38602         "br",
38603         "55"
38604       ],
38605       [
38606         "British Indian Ocean Territory",
38607         "io",
38608         "246"
38609       ],
38610       [
38611         "British Virgin Islands",
38612         "vg",
38613         "1284"
38614       ],
38615       [
38616         "Brunei",
38617         "bn",
38618         "673"
38619       ],
38620       [
38621         "Bulgaria (България)",
38622         "bg",
38623         "359"
38624       ],
38625       [
38626         "Burkina Faso",
38627         "bf",
38628         "226"
38629       ],
38630       [
38631         "Burundi (Uburundi)",
38632         "bi",
38633         "257"
38634       ],
38635       [
38636         "Cambodia (កម្ពុជា)",
38637         "kh",
38638         "855"
38639       ],
38640       [
38641         "Cameroon (Cameroun)",
38642         "cm",
38643         "237"
38644       ],
38645       [
38646         "Canada",
38647         "ca",
38648         "1",
38649         1,
38650         ["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"]
38651       ],
38652       [
38653         "Cape Verde (Kabu Verdi)",
38654         "cv",
38655         "238"
38656       ],
38657       [
38658         "Caribbean Netherlands",
38659         "bq",
38660         "599",
38661         1
38662       ],
38663       [
38664         "Cayman Islands",
38665         "ky",
38666         "1345"
38667       ],
38668       [
38669         "Central African Republic (République centrafricaine)",
38670         "cf",
38671         "236"
38672       ],
38673       [
38674         "Chad (Tchad)",
38675         "td",
38676         "235"
38677       ],
38678       [
38679         "Chile",
38680         "cl",
38681         "56"
38682       ],
38683       [
38684         "China (中国)",
38685         "cn",
38686         "86"
38687       ],
38688       [
38689         "Christmas Island",
38690         "cx",
38691         "61",
38692         2
38693       ],
38694       [
38695         "Cocos (Keeling) Islands",
38696         "cc",
38697         "61",
38698         1
38699       ],
38700       [
38701         "Colombia",
38702         "co",
38703         "57"
38704       ],
38705       [
38706         "Comoros (‫جزر القمر‬‎)",
38707         "km",
38708         "269"
38709       ],
38710       [
38711         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38712         "cd",
38713         "243"
38714       ],
38715       [
38716         "Congo (Republic) (Congo-Brazzaville)",
38717         "cg",
38718         "242"
38719       ],
38720       [
38721         "Cook Islands",
38722         "ck",
38723         "682"
38724       ],
38725       [
38726         "Costa Rica",
38727         "cr",
38728         "506"
38729       ],
38730       [
38731         "Côte d’Ivoire",
38732         "ci",
38733         "225"
38734       ],
38735       [
38736         "Croatia (Hrvatska)",
38737         "hr",
38738         "385"
38739       ],
38740       [
38741         "Cuba",
38742         "cu",
38743         "53"
38744       ],
38745       [
38746         "Curaçao",
38747         "cw",
38748         "599",
38749         0
38750       ],
38751       [
38752         "Cyprus (Κύπρος)",
38753         "cy",
38754         "357"
38755       ],
38756       [
38757         "Czech Republic (Česká republika)",
38758         "cz",
38759         "420"
38760       ],
38761       [
38762         "Denmark (Danmark)",
38763         "dk",
38764         "45"
38765       ],
38766       [
38767         "Djibouti",
38768         "dj",
38769         "253"
38770       ],
38771       [
38772         "Dominica",
38773         "dm",
38774         "1767"
38775       ],
38776       [
38777         "Dominican Republic (República Dominicana)",
38778         "do",
38779         "1",
38780         2,
38781         ["809", "829", "849"]
38782       ],
38783       [
38784         "Ecuador",
38785         "ec",
38786         "593"
38787       ],
38788       [
38789         "Egypt (‫مصر‬‎)",
38790         "eg",
38791         "20"
38792       ],
38793       [
38794         "El Salvador",
38795         "sv",
38796         "503"
38797       ],
38798       [
38799         "Equatorial Guinea (Guinea Ecuatorial)",
38800         "gq",
38801         "240"
38802       ],
38803       [
38804         "Eritrea",
38805         "er",
38806         "291"
38807       ],
38808       [
38809         "Estonia (Eesti)",
38810         "ee",
38811         "372"
38812       ],
38813       [
38814         "Ethiopia",
38815         "et",
38816         "251"
38817       ],
38818       [
38819         "Falkland Islands (Islas Malvinas)",
38820         "fk",
38821         "500"
38822       ],
38823       [
38824         "Faroe Islands (Føroyar)",
38825         "fo",
38826         "298"
38827       ],
38828       [
38829         "Fiji",
38830         "fj",
38831         "679"
38832       ],
38833       [
38834         "Finland (Suomi)",
38835         "fi",
38836         "358",
38837         0
38838       ],
38839       [
38840         "France",
38841         "fr",
38842         "33"
38843       ],
38844       [
38845         "French Guiana (Guyane française)",
38846         "gf",
38847         "594"
38848       ],
38849       [
38850         "French Polynesia (Polynésie française)",
38851         "pf",
38852         "689"
38853       ],
38854       [
38855         "Gabon",
38856         "ga",
38857         "241"
38858       ],
38859       [
38860         "Gambia",
38861         "gm",
38862         "220"
38863       ],
38864       [
38865         "Georgia (საქართველო)",
38866         "ge",
38867         "995"
38868       ],
38869       [
38870         "Germany (Deutschland)",
38871         "de",
38872         "49"
38873       ],
38874       [
38875         "Ghana (Gaana)",
38876         "gh",
38877         "233"
38878       ],
38879       [
38880         "Gibraltar",
38881         "gi",
38882         "350"
38883       ],
38884       [
38885         "Greece (Ελλάδα)",
38886         "gr",
38887         "30"
38888       ],
38889       [
38890         "Greenland (Kalaallit Nunaat)",
38891         "gl",
38892         "299"
38893       ],
38894       [
38895         "Grenada",
38896         "gd",
38897         "1473"
38898       ],
38899       [
38900         "Guadeloupe",
38901         "gp",
38902         "590",
38903         0
38904       ],
38905       [
38906         "Guam",
38907         "gu",
38908         "1671"
38909       ],
38910       [
38911         "Guatemala",
38912         "gt",
38913         "502"
38914       ],
38915       [
38916         "Guernsey",
38917         "gg",
38918         "44",
38919         1
38920       ],
38921       [
38922         "Guinea (Guinée)",
38923         "gn",
38924         "224"
38925       ],
38926       [
38927         "Guinea-Bissau (Guiné Bissau)",
38928         "gw",
38929         "245"
38930       ],
38931       [
38932         "Guyana",
38933         "gy",
38934         "592"
38935       ],
38936       [
38937         "Haiti",
38938         "ht",
38939         "509"
38940       ],
38941       [
38942         "Honduras",
38943         "hn",
38944         "504"
38945       ],
38946       [
38947         "Hong Kong (香港)",
38948         "hk",
38949         "852"
38950       ],
38951       [
38952         "Hungary (Magyarország)",
38953         "hu",
38954         "36"
38955       ],
38956       [
38957         "Iceland (Ísland)",
38958         "is",
38959         "354"
38960       ],
38961       [
38962         "India (भारत)",
38963         "in",
38964         "91"
38965       ],
38966       [
38967         "Indonesia",
38968         "id",
38969         "62"
38970       ],
38971       [
38972         "Iran (‫ایران‬‎)",
38973         "ir",
38974         "98"
38975       ],
38976       [
38977         "Iraq (‫العراق‬‎)",
38978         "iq",
38979         "964"
38980       ],
38981       [
38982         "Ireland",
38983         "ie",
38984         "353"
38985       ],
38986       [
38987         "Isle of Man",
38988         "im",
38989         "44",
38990         2
38991       ],
38992       [
38993         "Israel (‫ישראל‬‎)",
38994         "il",
38995         "972"
38996       ],
38997       [
38998         "Italy (Italia)",
38999         "it",
39000         "39",
39001         0
39002       ],
39003       [
39004         "Jamaica",
39005         "jm",
39006         "1876"
39007       ],
39008       [
39009         "Japan (日本)",
39010         "jp",
39011         "81"
39012       ],
39013       [
39014         "Jersey",
39015         "je",
39016         "44",
39017         3
39018       ],
39019       [
39020         "Jordan (‫الأردن‬‎)",
39021         "jo",
39022         "962"
39023       ],
39024       [
39025         "Kazakhstan (Казахстан)",
39026         "kz",
39027         "7",
39028         1
39029       ],
39030       [
39031         "Kenya",
39032         "ke",
39033         "254"
39034       ],
39035       [
39036         "Kiribati",
39037         "ki",
39038         "686"
39039       ],
39040       [
39041         "Kosovo",
39042         "xk",
39043         "383"
39044       ],
39045       [
39046         "Kuwait (‫الكويت‬‎)",
39047         "kw",
39048         "965"
39049       ],
39050       [
39051         "Kyrgyzstan (Кыргызстан)",
39052         "kg",
39053         "996"
39054       ],
39055       [
39056         "Laos (ລາວ)",
39057         "la",
39058         "856"
39059       ],
39060       [
39061         "Latvia (Latvija)",
39062         "lv",
39063         "371"
39064       ],
39065       [
39066         "Lebanon (‫لبنان‬‎)",
39067         "lb",
39068         "961"
39069       ],
39070       [
39071         "Lesotho",
39072         "ls",
39073         "266"
39074       ],
39075       [
39076         "Liberia",
39077         "lr",
39078         "231"
39079       ],
39080       [
39081         "Libya (‫ليبيا‬‎)",
39082         "ly",
39083         "218"
39084       ],
39085       [
39086         "Liechtenstein",
39087         "li",
39088         "423"
39089       ],
39090       [
39091         "Lithuania (Lietuva)",
39092         "lt",
39093         "370"
39094       ],
39095       [
39096         "Luxembourg",
39097         "lu",
39098         "352"
39099       ],
39100       [
39101         "Macau (澳門)",
39102         "mo",
39103         "853"
39104       ],
39105       [
39106         "Macedonia (FYROM) (Македонија)",
39107         "mk",
39108         "389"
39109       ],
39110       [
39111         "Madagascar (Madagasikara)",
39112         "mg",
39113         "261"
39114       ],
39115       [
39116         "Malawi",
39117         "mw",
39118         "265"
39119       ],
39120       [
39121         "Malaysia",
39122         "my",
39123         "60"
39124       ],
39125       [
39126         "Maldives",
39127         "mv",
39128         "960"
39129       ],
39130       [
39131         "Mali",
39132         "ml",
39133         "223"
39134       ],
39135       [
39136         "Malta",
39137         "mt",
39138         "356"
39139       ],
39140       [
39141         "Marshall Islands",
39142         "mh",
39143         "692"
39144       ],
39145       [
39146         "Martinique",
39147         "mq",
39148         "596"
39149       ],
39150       [
39151         "Mauritania (‫موريتانيا‬‎)",
39152         "mr",
39153         "222"
39154       ],
39155       [
39156         "Mauritius (Moris)",
39157         "mu",
39158         "230"
39159       ],
39160       [
39161         "Mayotte",
39162         "yt",
39163         "262",
39164         1
39165       ],
39166       [
39167         "Mexico (México)",
39168         "mx",
39169         "52"
39170       ],
39171       [
39172         "Micronesia",
39173         "fm",
39174         "691"
39175       ],
39176       [
39177         "Moldova (Republica Moldova)",
39178         "md",
39179         "373"
39180       ],
39181       [
39182         "Monaco",
39183         "mc",
39184         "377"
39185       ],
39186       [
39187         "Mongolia (Монгол)",
39188         "mn",
39189         "976"
39190       ],
39191       [
39192         "Montenegro (Crna Gora)",
39193         "me",
39194         "382"
39195       ],
39196       [
39197         "Montserrat",
39198         "ms",
39199         "1664"
39200       ],
39201       [
39202         "Morocco (‫المغرب‬‎)",
39203         "ma",
39204         "212",
39205         0
39206       ],
39207       [
39208         "Mozambique (Moçambique)",
39209         "mz",
39210         "258"
39211       ],
39212       [
39213         "Myanmar (Burma) (မြန်မာ)",
39214         "mm",
39215         "95"
39216       ],
39217       [
39218         "Namibia (Namibië)",
39219         "na",
39220         "264"
39221       ],
39222       [
39223         "Nauru",
39224         "nr",
39225         "674"
39226       ],
39227       [
39228         "Nepal (नेपाल)",
39229         "np",
39230         "977"
39231       ],
39232       [
39233         "Netherlands (Nederland)",
39234         "nl",
39235         "31"
39236       ],
39237       [
39238         "New Caledonia (Nouvelle-Calédonie)",
39239         "nc",
39240         "687"
39241       ],
39242       [
39243         "New Zealand",
39244         "nz",
39245         "64"
39246       ],
39247       [
39248         "Nicaragua",
39249         "ni",
39250         "505"
39251       ],
39252       [
39253         "Niger (Nijar)",
39254         "ne",
39255         "227"
39256       ],
39257       [
39258         "Nigeria",
39259         "ng",
39260         "234"
39261       ],
39262       [
39263         "Niue",
39264         "nu",
39265         "683"
39266       ],
39267       [
39268         "Norfolk Island",
39269         "nf",
39270         "672"
39271       ],
39272       [
39273         "North Korea (조선 민주주의 인민 공화국)",
39274         "kp",
39275         "850"
39276       ],
39277       [
39278         "Northern Mariana Islands",
39279         "mp",
39280         "1670"
39281       ],
39282       [
39283         "Norway (Norge)",
39284         "no",
39285         "47",
39286         0
39287       ],
39288       [
39289         "Oman (‫عُمان‬‎)",
39290         "om",
39291         "968"
39292       ],
39293       [
39294         "Pakistan (‫پاکستان‬‎)",
39295         "pk",
39296         "92"
39297       ],
39298       [
39299         "Palau",
39300         "pw",
39301         "680"
39302       ],
39303       [
39304         "Palestine (‫فلسطين‬‎)",
39305         "ps",
39306         "970"
39307       ],
39308       [
39309         "Panama (Panamá)",
39310         "pa",
39311         "507"
39312       ],
39313       [
39314         "Papua New Guinea",
39315         "pg",
39316         "675"
39317       ],
39318       [
39319         "Paraguay",
39320         "py",
39321         "595"
39322       ],
39323       [
39324         "Peru (Perú)",
39325         "pe",
39326         "51"
39327       ],
39328       [
39329         "Philippines",
39330         "ph",
39331         "63"
39332       ],
39333       [
39334         "Poland (Polska)",
39335         "pl",
39336         "48"
39337       ],
39338       [
39339         "Portugal",
39340         "pt",
39341         "351"
39342       ],
39343       [
39344         "Puerto Rico",
39345         "pr",
39346         "1",
39347         3,
39348         ["787", "939"]
39349       ],
39350       [
39351         "Qatar (‫قطر‬‎)",
39352         "qa",
39353         "974"
39354       ],
39355       [
39356         "Réunion (La Réunion)",
39357         "re",
39358         "262",
39359         0
39360       ],
39361       [
39362         "Romania (România)",
39363         "ro",
39364         "40"
39365       ],
39366       [
39367         "Russia (Россия)",
39368         "ru",
39369         "7",
39370         0
39371       ],
39372       [
39373         "Rwanda",
39374         "rw",
39375         "250"
39376       ],
39377       [
39378         "Saint Barthélemy",
39379         "bl",
39380         "590",
39381         1
39382       ],
39383       [
39384         "Saint Helena",
39385         "sh",
39386         "290"
39387       ],
39388       [
39389         "Saint Kitts and Nevis",
39390         "kn",
39391         "1869"
39392       ],
39393       [
39394         "Saint Lucia",
39395         "lc",
39396         "1758"
39397       ],
39398       [
39399         "Saint Martin (Saint-Martin (partie française))",
39400         "mf",
39401         "590",
39402         2
39403       ],
39404       [
39405         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39406         "pm",
39407         "508"
39408       ],
39409       [
39410         "Saint Vincent and the Grenadines",
39411         "vc",
39412         "1784"
39413       ],
39414       [
39415         "Samoa",
39416         "ws",
39417         "685"
39418       ],
39419       [
39420         "San Marino",
39421         "sm",
39422         "378"
39423       ],
39424       [
39425         "São Tomé and Príncipe (São Tomé e Príncipe)",
39426         "st",
39427         "239"
39428       ],
39429       [
39430         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39431         "sa",
39432         "966"
39433       ],
39434       [
39435         "Senegal (Sénégal)",
39436         "sn",
39437         "221"
39438       ],
39439       [
39440         "Serbia (Србија)",
39441         "rs",
39442         "381"
39443       ],
39444       [
39445         "Seychelles",
39446         "sc",
39447         "248"
39448       ],
39449       [
39450         "Sierra Leone",
39451         "sl",
39452         "232"
39453       ],
39454       [
39455         "Singapore",
39456         "sg",
39457         "65"
39458       ],
39459       [
39460         "Sint Maarten",
39461         "sx",
39462         "1721"
39463       ],
39464       [
39465         "Slovakia (Slovensko)",
39466         "sk",
39467         "421"
39468       ],
39469       [
39470         "Slovenia (Slovenija)",
39471         "si",
39472         "386"
39473       ],
39474       [
39475         "Solomon Islands",
39476         "sb",
39477         "677"
39478       ],
39479       [
39480         "Somalia (Soomaaliya)",
39481         "so",
39482         "252"
39483       ],
39484       [
39485         "South Africa",
39486         "za",
39487         "27"
39488       ],
39489       [
39490         "South Korea (대한민국)",
39491         "kr",
39492         "82"
39493       ],
39494       [
39495         "South Sudan (‫جنوب السودان‬‎)",
39496         "ss",
39497         "211"
39498       ],
39499       [
39500         "Spain (España)",
39501         "es",
39502         "34"
39503       ],
39504       [
39505         "Sri Lanka (ශ්‍රී ලංකාව)",
39506         "lk",
39507         "94"
39508       ],
39509       [
39510         "Sudan (‫السودان‬‎)",
39511         "sd",
39512         "249"
39513       ],
39514       [
39515         "Suriname",
39516         "sr",
39517         "597"
39518       ],
39519       [
39520         "Svalbard and Jan Mayen",
39521         "sj",
39522         "47",
39523         1
39524       ],
39525       [
39526         "Swaziland",
39527         "sz",
39528         "268"
39529       ],
39530       [
39531         "Sweden (Sverige)",
39532         "se",
39533         "46"
39534       ],
39535       [
39536         "Switzerland (Schweiz)",
39537         "ch",
39538         "41"
39539       ],
39540       [
39541         "Syria (‫سوريا‬‎)",
39542         "sy",
39543         "963"
39544       ],
39545       [
39546         "Taiwan (台灣)",
39547         "tw",
39548         "886"
39549       ],
39550       [
39551         "Tajikistan",
39552         "tj",
39553         "992"
39554       ],
39555       [
39556         "Tanzania",
39557         "tz",
39558         "255"
39559       ],
39560       [
39561         "Thailand (ไทย)",
39562         "th",
39563         "66"
39564       ],
39565       [
39566         "Timor-Leste",
39567         "tl",
39568         "670"
39569       ],
39570       [
39571         "Togo",
39572         "tg",
39573         "228"
39574       ],
39575       [
39576         "Tokelau",
39577         "tk",
39578         "690"
39579       ],
39580       [
39581         "Tonga",
39582         "to",
39583         "676"
39584       ],
39585       [
39586         "Trinidad and Tobago",
39587         "tt",
39588         "1868"
39589       ],
39590       [
39591         "Tunisia (‫تونس‬‎)",
39592         "tn",
39593         "216"
39594       ],
39595       [
39596         "Turkey (Türkiye)",
39597         "tr",
39598         "90"
39599       ],
39600       [
39601         "Turkmenistan",
39602         "tm",
39603         "993"
39604       ],
39605       [
39606         "Turks and Caicos Islands",
39607         "tc",
39608         "1649"
39609       ],
39610       [
39611         "Tuvalu",
39612         "tv",
39613         "688"
39614       ],
39615       [
39616         "U.S. Virgin Islands",
39617         "vi",
39618         "1340"
39619       ],
39620       [
39621         "Uganda",
39622         "ug",
39623         "256"
39624       ],
39625       [
39626         "Ukraine (Україна)",
39627         "ua",
39628         "380"
39629       ],
39630       [
39631         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39632         "ae",
39633         "971"
39634       ],
39635       [
39636         "United Kingdom",
39637         "gb",
39638         "44",
39639         0
39640       ],
39641       [
39642         "United States",
39643         "us",
39644         "1",
39645         0
39646       ],
39647       [
39648         "Uruguay",
39649         "uy",
39650         "598"
39651       ],
39652       [
39653         "Uzbekistan (Oʻzbekiston)",
39654         "uz",
39655         "998"
39656       ],
39657       [
39658         "Vanuatu",
39659         "vu",
39660         "678"
39661       ],
39662       [
39663         "Vatican City (Città del Vaticano)",
39664         "va",
39665         "39",
39666         1
39667       ],
39668       [
39669         "Venezuela",
39670         "ve",
39671         "58"
39672       ],
39673       [
39674         "Vietnam (Việt Nam)",
39675         "vn",
39676         "84"
39677       ],
39678       [
39679         "Wallis and Futuna (Wallis-et-Futuna)",
39680         "wf",
39681         "681"
39682       ],
39683       [
39684         "Western Sahara (‫الصحراء الغربية‬‎)",
39685         "eh",
39686         "212",
39687         1
39688       ],
39689       [
39690         "Yemen (‫اليمن‬‎)",
39691         "ye",
39692         "967"
39693       ],
39694       [
39695         "Zambia",
39696         "zm",
39697         "260"
39698       ],
39699       [
39700         "Zimbabwe",
39701         "zw",
39702         "263"
39703       ],
39704       [
39705         "Åland Islands",
39706         "ax",
39707         "358",
39708         1
39709       ]
39710   ];
39711   
39712   return d;
39713 }/**
39714 *    This script refer to:
39715 *    Title: International Telephone Input
39716 *    Author: Jack O'Connor
39717 *    Code version:  v12.1.12
39718 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39719 **/
39720
39721 /**
39722  * @class Roo.bootstrap.PhoneInput
39723  * @extends Roo.bootstrap.TriggerField
39724  * An input with International dial-code selection
39725  
39726  * @cfg {String} defaultDialCode default '+852'
39727  * @cfg {Array} preferedCountries default []
39728   
39729  * @constructor
39730  * Create a new PhoneInput.
39731  * @param {Object} config Configuration options
39732  */
39733
39734 Roo.bootstrap.PhoneInput = function(config) {
39735     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39736 };
39737
39738 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39739         
39740         listWidth: undefined,
39741         
39742         selectedClass: 'active',
39743         
39744         invalidClass : "has-warning",
39745         
39746         validClass: 'has-success',
39747         
39748         allowed: '0123456789',
39749         
39750         /**
39751          * @cfg {String} defaultDialCode The default dial code when initializing the input
39752          */
39753         defaultDialCode: '+852',
39754         
39755         /**
39756          * @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
39757          */
39758         preferedCountries: false,
39759         
39760         getAutoCreate : function()
39761         {
39762             var data = Roo.bootstrap.PhoneInputData();
39763             var align = this.labelAlign || this.parentLabelAlign();
39764             var id = Roo.id();
39765             
39766             this.allCountries = [];
39767             this.dialCodeMapping = [];
39768             
39769             for (var i = 0; i < data.length; i++) {
39770               var c = data[i];
39771               this.allCountries[i] = {
39772                 name: c[0],
39773                 iso2: c[1],
39774                 dialCode: c[2],
39775                 priority: c[3] || 0,
39776                 areaCodes: c[4] || null
39777               };
39778               this.dialCodeMapping[c[2]] = {
39779                   name: c[0],
39780                   iso2: c[1],
39781                   priority: c[3] || 0,
39782                   areaCodes: c[4] || null
39783               };
39784             }
39785             
39786             var cfg = {
39787                 cls: 'form-group',
39788                 cn: []
39789             };
39790             
39791             var input =  {
39792                 tag: 'input',
39793                 id : id,
39794                 cls : 'form-control tel-input',
39795                 autocomplete: 'new-password'
39796             };
39797             
39798             var hiddenInput = {
39799                 tag: 'input',
39800                 type: 'hidden',
39801                 cls: 'hidden-tel-input'
39802             };
39803             
39804             if (this.name) {
39805                 hiddenInput.name = this.name;
39806             }
39807             
39808             if (this.disabled) {
39809                 input.disabled = true;
39810             }
39811             
39812             var flag_container = {
39813                 tag: 'div',
39814                 cls: 'flag-box',
39815                 cn: [
39816                     {
39817                         tag: 'div',
39818                         cls: 'flag'
39819                     },
39820                     {
39821                         tag: 'div',
39822                         cls: 'caret'
39823                     }
39824                 ]
39825             };
39826             
39827             var box = {
39828                 tag: 'div',
39829                 cls: this.hasFeedback ? 'has-feedback' : '',
39830                 cn: [
39831                     hiddenInput,
39832                     input,
39833                     {
39834                         tag: 'input',
39835                         cls: 'dial-code-holder',
39836                         disabled: true
39837                     }
39838                 ]
39839             };
39840             
39841             var container = {
39842                 cls: 'roo-select2-container input-group',
39843                 cn: [
39844                     flag_container,
39845                     box
39846                 ]
39847             };
39848             
39849             if (this.fieldLabel.length) {
39850                 var indicator = {
39851                     tag: 'i',
39852                     tooltip: 'This field is required'
39853                 };
39854                 
39855                 var label = {
39856                     tag: 'label',
39857                     'for':  id,
39858                     cls: 'control-label',
39859                     cn: []
39860                 };
39861                 
39862                 var label_text = {
39863                     tag: 'span',
39864                     html: this.fieldLabel
39865                 };
39866                 
39867                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39868                 label.cn = [
39869                     indicator,
39870                     label_text
39871                 ];
39872                 
39873                 if(this.indicatorpos == 'right') {
39874                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39875                     label.cn = [
39876                         label_text,
39877                         indicator
39878                     ];
39879                 }
39880                 
39881                 if(align == 'left') {
39882                     container = {
39883                         tag: 'div',
39884                         cn: [
39885                             container
39886                         ]
39887                     };
39888                     
39889                     if(this.labelWidth > 12){
39890                         label.style = "width: " + this.labelWidth + 'px';
39891                     }
39892                     if(this.labelWidth < 13 && this.labelmd == 0){
39893                         this.labelmd = this.labelWidth;
39894                     }
39895                     if(this.labellg > 0){
39896                         label.cls += ' col-lg-' + this.labellg;
39897                         input.cls += ' col-lg-' + (12 - this.labellg);
39898                     }
39899                     if(this.labelmd > 0){
39900                         label.cls += ' col-md-' + this.labelmd;
39901                         container.cls += ' col-md-' + (12 - this.labelmd);
39902                     }
39903                     if(this.labelsm > 0){
39904                         label.cls += ' col-sm-' + this.labelsm;
39905                         container.cls += ' col-sm-' + (12 - this.labelsm);
39906                     }
39907                     if(this.labelxs > 0){
39908                         label.cls += ' col-xs-' + this.labelxs;
39909                         container.cls += ' col-xs-' + (12 - this.labelxs);
39910                     }
39911                 }
39912             }
39913             
39914             cfg.cn = [
39915                 label,
39916                 container
39917             ];
39918             
39919             var settings = this;
39920             
39921             ['xs','sm','md','lg'].map(function(size){
39922                 if (settings[size]) {
39923                     cfg.cls += ' col-' + size + '-' + settings[size];
39924                 }
39925             });
39926             
39927             this.store = new Roo.data.Store({
39928                 proxy : new Roo.data.MemoryProxy({}),
39929                 reader : new Roo.data.JsonReader({
39930                     fields : [
39931                         {
39932                             'name' : 'name',
39933                             'type' : 'string'
39934                         },
39935                         {
39936                             'name' : 'iso2',
39937                             'type' : 'string'
39938                         },
39939                         {
39940                             'name' : 'dialCode',
39941                             'type' : 'string'
39942                         },
39943                         {
39944                             'name' : 'priority',
39945                             'type' : 'string'
39946                         },
39947                         {
39948                             'name' : 'areaCodes',
39949                             'type' : 'string'
39950                         }
39951                     ]
39952                 })
39953             });
39954             
39955             if(!this.preferedCountries) {
39956                 this.preferedCountries = [
39957                     'hk',
39958                     'gb',
39959                     'us'
39960                 ];
39961             }
39962             
39963             var p = this.preferedCountries.reverse();
39964             
39965             if(p) {
39966                 for (var i = 0; i < p.length; i++) {
39967                     for (var j = 0; j < this.allCountries.length; j++) {
39968                         if(this.allCountries[j].iso2 == p[i]) {
39969                             var t = this.allCountries[j];
39970                             this.allCountries.splice(j,1);
39971                             this.allCountries.unshift(t);
39972                         }
39973                     } 
39974                 }
39975             }
39976             
39977             this.store.proxy.data = {
39978                 success: true,
39979                 data: this.allCountries
39980             };
39981             
39982             return cfg;
39983         },
39984         
39985         initEvents : function()
39986         {
39987             this.createList();
39988             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39989             
39990             this.indicator = this.indicatorEl();
39991             this.flag = this.flagEl();
39992             this.dialCodeHolder = this.dialCodeHolderEl();
39993             
39994             this.trigger = this.el.select('div.flag-box',true).first();
39995             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39996             
39997             var _this = this;
39998             
39999             (function(){
40000                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40001                 _this.list.setWidth(lw);
40002             }).defer(100);
40003             
40004             this.list.on('mouseover', this.onViewOver, this);
40005             this.list.on('mousemove', this.onViewMove, this);
40006             this.inputEl().on("keyup", this.onKeyUp, this);
40007             
40008             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40009
40010             this.view = new Roo.View(this.list, this.tpl, {
40011                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40012             });
40013             
40014             this.view.on('click', this.onViewClick, this);
40015             this.setValue(this.defaultDialCode);
40016         },
40017         
40018         onTriggerClick : function(e)
40019         {
40020             Roo.log('trigger click');
40021             if(this.disabled){
40022                 return;
40023             }
40024             
40025             if(this.isExpanded()){
40026                 this.collapse();
40027                 this.hasFocus = false;
40028             }else {
40029                 this.store.load({});
40030                 this.hasFocus = true;
40031                 this.expand();
40032             }
40033         },
40034         
40035         isExpanded : function()
40036         {
40037             return this.list.isVisible();
40038         },
40039         
40040         collapse : function()
40041         {
40042             if(!this.isExpanded()){
40043                 return;
40044             }
40045             this.list.hide();
40046             Roo.get(document).un('mousedown', this.collapseIf, this);
40047             Roo.get(document).un('mousewheel', this.collapseIf, this);
40048             this.fireEvent('collapse', this);
40049             this.validate();
40050         },
40051         
40052         expand : function()
40053         {
40054             Roo.log('expand');
40055
40056             if(this.isExpanded() || !this.hasFocus){
40057                 return;
40058             }
40059             
40060             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40061             this.list.setWidth(lw);
40062             
40063             this.list.show();
40064             this.restrictHeight();
40065             
40066             Roo.get(document).on('mousedown', this.collapseIf, this);
40067             Roo.get(document).on('mousewheel', this.collapseIf, this);
40068             
40069             this.fireEvent('expand', this);
40070         },
40071         
40072         restrictHeight : function()
40073         {
40074             this.list.alignTo(this.inputEl(), this.listAlign);
40075             this.list.alignTo(this.inputEl(), this.listAlign);
40076         },
40077         
40078         onViewOver : function(e, t)
40079         {
40080             if(this.inKeyMode){
40081                 return;
40082             }
40083             var item = this.view.findItemFromChild(t);
40084             
40085             if(item){
40086                 var index = this.view.indexOf(item);
40087                 this.select(index, false);
40088             }
40089         },
40090
40091         // private
40092         onViewClick : function(view, doFocus, el, e)
40093         {
40094             var index = this.view.getSelectedIndexes()[0];
40095             
40096             var r = this.store.getAt(index);
40097             
40098             if(r){
40099                 this.onSelect(r, index);
40100             }
40101             if(doFocus !== false && !this.blockFocus){
40102                 this.inputEl().focus();
40103             }
40104         },
40105         
40106         onViewMove : function(e, t)
40107         {
40108             this.inKeyMode = false;
40109         },
40110         
40111         select : function(index, scrollIntoView)
40112         {
40113             this.selectedIndex = index;
40114             this.view.select(index);
40115             if(scrollIntoView !== false){
40116                 var el = this.view.getNode(index);
40117                 if(el){
40118                     this.list.scrollChildIntoView(el, false);
40119                 }
40120             }
40121         },
40122         
40123         createList : function()
40124         {
40125             this.list = Roo.get(document.body).createChild({
40126                 tag: 'ul',
40127                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40128                 style: 'display:none'
40129             });
40130             
40131             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40132         },
40133         
40134         collapseIf : function(e)
40135         {
40136             var in_combo  = e.within(this.el);
40137             var in_list =  e.within(this.list);
40138             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40139             
40140             if (in_combo || in_list || is_list) {
40141                 return;
40142             }
40143             this.collapse();
40144         },
40145         
40146         onSelect : function(record, index)
40147         {
40148             if(this.fireEvent('beforeselect', this, record, index) !== false){
40149                 
40150                 this.setFlagClass(record.data.iso2);
40151                 this.setDialCode(record.data.dialCode);
40152                 this.hasFocus = false;
40153                 this.collapse();
40154                 this.fireEvent('select', this, record, index);
40155             }
40156         },
40157         
40158         flagEl : function()
40159         {
40160             var flag = this.el.select('div.flag',true).first();
40161             if(!flag){
40162                 return false;
40163             }
40164             return flag;
40165         },
40166         
40167         dialCodeHolderEl : function()
40168         {
40169             var d = this.el.select('input.dial-code-holder',true).first();
40170             if(!d){
40171                 return false;
40172             }
40173             return d;
40174         },
40175         
40176         setDialCode : function(v)
40177         {
40178             this.dialCodeHolder.dom.value = '+'+v;
40179         },
40180         
40181         setFlagClass : function(n)
40182         {
40183             this.flag.dom.className = 'flag '+n;
40184         },
40185         
40186         getValue : function()
40187         {
40188             var v = this.inputEl().getValue();
40189             if(this.dialCodeHolder) {
40190                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40191             }
40192             return v;
40193         },
40194         
40195         setValue : function(v)
40196         {
40197             var d = this.getDialCode(v);
40198             
40199             //invalid dial code
40200             if(v.length == 0 || !d || d.length == 0) {
40201                 if(this.rendered){
40202                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40203                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40204                 }
40205                 return;
40206             }
40207             
40208             //valid dial code
40209             this.setFlagClass(this.dialCodeMapping[d].iso2);
40210             this.setDialCode(d);
40211             this.inputEl().dom.value = v.replace('+'+d,'');
40212             this.hiddenEl().dom.value = this.getValue();
40213             
40214             this.validate();
40215         },
40216         
40217         getDialCode : function(v)
40218         {
40219             v = v ||  '';
40220             
40221             if (v.length == 0) {
40222                 return this.dialCodeHolder.dom.value;
40223             }
40224             
40225             var dialCode = "";
40226             if (v.charAt(0) != "+") {
40227                 return false;
40228             }
40229             var numericChars = "";
40230             for (var i = 1; i < v.length; i++) {
40231               var c = v.charAt(i);
40232               if (!isNaN(c)) {
40233                 numericChars += c;
40234                 if (this.dialCodeMapping[numericChars]) {
40235                   dialCode = v.substr(1, i);
40236                 }
40237                 if (numericChars.length == 4) {
40238                   break;
40239                 }
40240               }
40241             }
40242             return dialCode;
40243         },
40244         
40245         reset : function()
40246         {
40247             this.setValue(this.defaultDialCode);
40248             this.validate();
40249         },
40250         
40251         hiddenEl : function()
40252         {
40253             return this.el.select('input.hidden-tel-input',true).first();
40254         },
40255         
40256         onKeyUp : function(e){
40257             
40258             var k = e.getKey();
40259             var c = e.getCharCode();
40260             
40261             if(
40262                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40263                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40264             ){
40265                 e.stopEvent();
40266             }
40267             
40268             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40269             //     return;
40270             // }
40271             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40272                 e.stopEvent();
40273             }
40274             
40275             this.setValue(this.getValue());
40276         }
40277         
40278 });
40279 /**
40280  * @class Roo.bootstrap.MoneyField
40281  * @extends Roo.bootstrap.ComboBox
40282  * Bootstrap MoneyField class
40283  * 
40284  * @constructor
40285  * Create a new MoneyField.
40286  * @param {Object} config Configuration options
40287  */
40288
40289 Roo.bootstrap.MoneyField = function(config) {
40290     
40291     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40292     
40293 };
40294
40295 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40296     
40297     /**
40298      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40299      */
40300     allowDecimals : true,
40301     /**
40302      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40303      */
40304     decimalSeparator : ".",
40305     /**
40306      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40307      */
40308     decimalPrecision : 0,
40309     /**
40310      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40311      */
40312     allowNegative : true,
40313     /**
40314      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40315      */
40316     allowZero: true,
40317     /**
40318      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40319      */
40320     minValue : Number.NEGATIVE_INFINITY,
40321     /**
40322      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40323      */
40324     maxValue : Number.MAX_VALUE,
40325     /**
40326      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40327      */
40328     minText : "The minimum value for this field is {0}",
40329     /**
40330      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40331      */
40332     maxText : "The maximum value for this field is {0}",
40333     /**
40334      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40335      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40336      */
40337     nanText : "{0} is not a valid number",
40338     /**
40339      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40340      */
40341     castInt : true,
40342     /**
40343      * @cfg {String} defaults currency of the MoneyField
40344      * value should be in lkey
40345      */
40346     defaultCurrency : false,
40347     /**
40348      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40349      */
40350     thousandsDelimiter : false,
40351     
40352     
40353     inputlg : 9,
40354     inputmd : 9,
40355     inputsm : 9,
40356     inputxs : 6,
40357     
40358     store : false,
40359     
40360     getAutoCreate : function()
40361     {
40362         var align = this.labelAlign || this.parentLabelAlign();
40363         
40364         var id = Roo.id();
40365
40366         var cfg = {
40367             cls: 'form-group',
40368             cn: []
40369         };
40370
40371         var input =  {
40372             tag: 'input',
40373             id : id,
40374             cls : 'form-control roo-money-amount-input',
40375             autocomplete: 'new-password'
40376         };
40377         
40378         var hiddenInput = {
40379             tag: 'input',
40380             type: 'hidden',
40381             id: Roo.id(),
40382             cls: 'hidden-number-input'
40383         };
40384         
40385         if (this.name) {
40386             hiddenInput.name = this.name;
40387         }
40388
40389         if (this.disabled) {
40390             input.disabled = true;
40391         }
40392
40393         var clg = 12 - this.inputlg;
40394         var cmd = 12 - this.inputmd;
40395         var csm = 12 - this.inputsm;
40396         var cxs = 12 - this.inputxs;
40397         
40398         var container = {
40399             tag : 'div',
40400             cls : 'row roo-money-field',
40401             cn : [
40402                 {
40403                     tag : 'div',
40404                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40405                     cn : [
40406                         {
40407                             tag : 'div',
40408                             cls: 'roo-select2-container input-group',
40409                             cn: [
40410                                 {
40411                                     tag : 'input',
40412                                     cls : 'form-control roo-money-currency-input',
40413                                     autocomplete: 'new-password',
40414                                     readOnly : 1,
40415                                     name : this.currencyName
40416                                 },
40417                                 {
40418                                     tag :'span',
40419                                     cls : 'input-group-addon',
40420                                     cn : [
40421                                         {
40422                                             tag: 'span',
40423                                             cls: 'caret'
40424                                         }
40425                                     ]
40426                                 }
40427                             ]
40428                         }
40429                     ]
40430                 },
40431                 {
40432                     tag : 'div',
40433                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40434                     cn : [
40435                         {
40436                             tag: 'div',
40437                             cls: this.hasFeedback ? 'has-feedback' : '',
40438                             cn: [
40439                                 input
40440                             ]
40441                         }
40442                     ]
40443                 }
40444             ]
40445             
40446         };
40447         
40448         if (this.fieldLabel.length) {
40449             var indicator = {
40450                 tag: 'i',
40451                 tooltip: 'This field is required'
40452             };
40453
40454             var label = {
40455                 tag: 'label',
40456                 'for':  id,
40457                 cls: 'control-label',
40458                 cn: []
40459             };
40460
40461             var label_text = {
40462                 tag: 'span',
40463                 html: this.fieldLabel
40464             };
40465
40466             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40467             label.cn = [
40468                 indicator,
40469                 label_text
40470             ];
40471
40472             if(this.indicatorpos == 'right') {
40473                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40474                 label.cn = [
40475                     label_text,
40476                     indicator
40477                 ];
40478             }
40479
40480             if(align == 'left') {
40481                 container = {
40482                     tag: 'div',
40483                     cn: [
40484                         container
40485                     ]
40486                 };
40487
40488                 if(this.labelWidth > 12){
40489                     label.style = "width: " + this.labelWidth + 'px';
40490                 }
40491                 if(this.labelWidth < 13 && this.labelmd == 0){
40492                     this.labelmd = this.labelWidth;
40493                 }
40494                 if(this.labellg > 0){
40495                     label.cls += ' col-lg-' + this.labellg;
40496                     input.cls += ' col-lg-' + (12 - this.labellg);
40497                 }
40498                 if(this.labelmd > 0){
40499                     label.cls += ' col-md-' + this.labelmd;
40500                     container.cls += ' col-md-' + (12 - this.labelmd);
40501                 }
40502                 if(this.labelsm > 0){
40503                     label.cls += ' col-sm-' + this.labelsm;
40504                     container.cls += ' col-sm-' + (12 - this.labelsm);
40505                 }
40506                 if(this.labelxs > 0){
40507                     label.cls += ' col-xs-' + this.labelxs;
40508                     container.cls += ' col-xs-' + (12 - this.labelxs);
40509                 }
40510             }
40511         }
40512
40513         cfg.cn = [
40514             label,
40515             container,
40516             hiddenInput
40517         ];
40518         
40519         var settings = this;
40520
40521         ['xs','sm','md','lg'].map(function(size){
40522             if (settings[size]) {
40523                 cfg.cls += ' col-' + size + '-' + settings[size];
40524             }
40525         });
40526         
40527         return cfg;
40528     },
40529     
40530     initEvents : function()
40531     {
40532         this.indicator = this.indicatorEl();
40533         
40534         this.initCurrencyEvent();
40535         
40536         this.initNumberEvent();
40537     },
40538     
40539     initCurrencyEvent : function()
40540     {
40541         if (!this.store) {
40542             throw "can not find store for combo";
40543         }
40544         
40545         this.store = Roo.factory(this.store, Roo.data);
40546         this.store.parent = this;
40547         
40548         this.createList();
40549         
40550         this.triggerEl = this.el.select('.input-group-addon', true).first();
40551         
40552         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40553         
40554         var _this = this;
40555         
40556         (function(){
40557             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40558             _this.list.setWidth(lw);
40559         }).defer(100);
40560         
40561         this.list.on('mouseover', this.onViewOver, this);
40562         this.list.on('mousemove', this.onViewMove, this);
40563         this.list.on('scroll', this.onViewScroll, this);
40564         
40565         if(!this.tpl){
40566             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40567         }
40568         
40569         this.view = new Roo.View(this.list, this.tpl, {
40570             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40571         });
40572         
40573         this.view.on('click', this.onViewClick, this);
40574         
40575         this.store.on('beforeload', this.onBeforeLoad, this);
40576         this.store.on('load', this.onLoad, this);
40577         this.store.on('loadexception', this.onLoadException, this);
40578         
40579         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40580             "up" : function(e){
40581                 this.inKeyMode = true;
40582                 this.selectPrev();
40583             },
40584
40585             "down" : function(e){
40586                 if(!this.isExpanded()){
40587                     this.onTriggerClick();
40588                 }else{
40589                     this.inKeyMode = true;
40590                     this.selectNext();
40591                 }
40592             },
40593
40594             "enter" : function(e){
40595                 this.collapse();
40596                 
40597                 if(this.fireEvent("specialkey", this, e)){
40598                     this.onViewClick(false);
40599                 }
40600                 
40601                 return true;
40602             },
40603
40604             "esc" : function(e){
40605                 this.collapse();
40606             },
40607
40608             "tab" : function(e){
40609                 this.collapse();
40610                 
40611                 if(this.fireEvent("specialkey", this, e)){
40612                     this.onViewClick(false);
40613                 }
40614                 
40615                 return true;
40616             },
40617
40618             scope : this,
40619
40620             doRelay : function(foo, bar, hname){
40621                 if(hname == 'down' || this.scope.isExpanded()){
40622                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40623                 }
40624                 return true;
40625             },
40626
40627             forceKeyDown: true
40628         });
40629         
40630         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40631         
40632     },
40633     
40634     initNumberEvent : function(e)
40635     {
40636         this.inputEl().on("keydown" , this.fireKey,  this);
40637         this.inputEl().on("focus", this.onFocus,  this);
40638         this.inputEl().on("blur", this.onBlur,  this);
40639         
40640         this.inputEl().relayEvent('keyup', this);
40641         
40642         if(this.indicator){
40643             this.indicator.addClass('invisible');
40644         }
40645  
40646         this.originalValue = this.getValue();
40647         
40648         if(this.validationEvent == 'keyup'){
40649             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40650             this.inputEl().on('keyup', this.filterValidation, this);
40651         }
40652         else if(this.validationEvent !== false){
40653             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40654         }
40655         
40656         if(this.selectOnFocus){
40657             this.on("focus", this.preFocus, this);
40658             
40659         }
40660         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40661             this.inputEl().on("keypress", this.filterKeys, this);
40662         } else {
40663             this.inputEl().relayEvent('keypress', this);
40664         }
40665         
40666         var allowed = "0123456789";
40667         
40668         if(this.allowDecimals){
40669             allowed += this.decimalSeparator;
40670         }
40671         
40672         if(this.allowNegative){
40673             allowed += "-";
40674         }
40675         
40676         if(this.thousandsDelimiter) {
40677             allowed += ",";
40678         }
40679         
40680         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40681         
40682         var keyPress = function(e){
40683             
40684             var k = e.getKey();
40685             
40686             var c = e.getCharCode();
40687             
40688             if(
40689                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40690                     allowed.indexOf(String.fromCharCode(c)) === -1
40691             ){
40692                 e.stopEvent();
40693                 return;
40694             }
40695             
40696             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40697                 return;
40698             }
40699             
40700             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40701                 e.stopEvent();
40702             }
40703         };
40704         
40705         this.inputEl().on("keypress", keyPress, this);
40706         
40707     },
40708     
40709     onTriggerClick : function(e)
40710     {   
40711         if(this.disabled){
40712             return;
40713         }
40714         
40715         this.page = 0;
40716         this.loadNext = false;
40717         
40718         if(this.isExpanded()){
40719             this.collapse();
40720             return;
40721         }
40722         
40723         this.hasFocus = true;
40724         
40725         if(this.triggerAction == 'all') {
40726             this.doQuery(this.allQuery, true);
40727             return;
40728         }
40729         
40730         this.doQuery(this.getRawValue());
40731     },
40732     
40733     getCurrency : function()
40734     {   
40735         var v = this.currencyEl().getValue();
40736         
40737         return v;
40738     },
40739     
40740     restrictHeight : function()
40741     {
40742         this.list.alignTo(this.currencyEl(), this.listAlign);
40743         this.list.alignTo(this.currencyEl(), this.listAlign);
40744     },
40745     
40746     onViewClick : function(view, doFocus, el, e)
40747     {
40748         var index = this.view.getSelectedIndexes()[0];
40749         
40750         var r = this.store.getAt(index);
40751         
40752         if(r){
40753             this.onSelect(r, index);
40754         }
40755     },
40756     
40757     onSelect : function(record, index){
40758         
40759         if(this.fireEvent('beforeselect', this, record, index) !== false){
40760         
40761             this.setFromCurrencyData(index > -1 ? record.data : false);
40762             
40763             this.collapse();
40764             
40765             this.fireEvent('select', this, record, index);
40766         }
40767     },
40768     
40769     setFromCurrencyData : function(o)
40770     {
40771         var currency = '';
40772         
40773         this.lastCurrency = o;
40774         
40775         if (this.currencyField) {
40776             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40777         } else {
40778             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40779         }
40780         
40781         this.lastSelectionText = currency;
40782         
40783         //setting default currency
40784         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40785             this.setCurrency(this.defaultCurrency);
40786             return;
40787         }
40788         
40789         this.setCurrency(currency);
40790     },
40791     
40792     setFromData : function(o)
40793     {
40794         var c = {};
40795         
40796         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40797         
40798         this.setFromCurrencyData(c);
40799         
40800         var value = '';
40801         
40802         if (this.name) {
40803             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40804         } else {
40805             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40806         }
40807         
40808         this.setValue(value);
40809         
40810     },
40811     
40812     setCurrency : function(v)
40813     {   
40814         this.currencyValue = v;
40815         
40816         if(this.rendered){
40817             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40818             this.validate();
40819         }
40820     },
40821     
40822     setValue : function(v)
40823     {
40824         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40825         
40826         this.value = v;
40827         
40828         if(this.rendered){
40829             
40830             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40831             
40832             this.inputEl().dom.value = (v == '') ? '' :
40833                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40834             
40835             if(!this.allowZero && v === '0') {
40836                 this.hiddenEl().dom.value = '';
40837                 this.inputEl().dom.value = '';
40838             }
40839             
40840             this.validate();
40841         }
40842     },
40843     
40844     getRawValue : function()
40845     {
40846         var v = this.inputEl().getValue();
40847         
40848         return v;
40849     },
40850     
40851     getValue : function()
40852     {
40853         return this.fixPrecision(this.parseValue(this.getRawValue()));
40854     },
40855     
40856     parseValue : function(value)
40857     {
40858         if(this.thousandsDelimiter) {
40859             value += "";
40860             r = new RegExp(",", "g");
40861             value = value.replace(r, "");
40862         }
40863         
40864         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40865         return isNaN(value) ? '' : value;
40866         
40867     },
40868     
40869     fixPrecision : function(value)
40870     {
40871         if(this.thousandsDelimiter) {
40872             value += "";
40873             r = new RegExp(",", "g");
40874             value = value.replace(r, "");
40875         }
40876         
40877         var nan = isNaN(value);
40878         
40879         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40880             return nan ? '' : value;
40881         }
40882         return parseFloat(value).toFixed(this.decimalPrecision);
40883     },
40884     
40885     decimalPrecisionFcn : function(v)
40886     {
40887         return Math.floor(v);
40888     },
40889     
40890     validateValue : function(value)
40891     {
40892         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40893             return false;
40894         }
40895         
40896         var num = this.parseValue(value);
40897         
40898         if(isNaN(num)){
40899             this.markInvalid(String.format(this.nanText, value));
40900             return false;
40901         }
40902         
40903         if(num < this.minValue){
40904             this.markInvalid(String.format(this.minText, this.minValue));
40905             return false;
40906         }
40907         
40908         if(num > this.maxValue){
40909             this.markInvalid(String.format(this.maxText, this.maxValue));
40910             return false;
40911         }
40912         
40913         return true;
40914     },
40915     
40916     validate : function()
40917     {
40918         if(this.disabled || this.allowBlank){
40919             this.markValid();
40920             return true;
40921         }
40922         
40923         var currency = this.getCurrency();
40924         
40925         if(this.validateValue(this.getRawValue()) && currency.length){
40926             this.markValid();
40927             return true;
40928         }
40929         
40930         this.markInvalid();
40931         return false;
40932     },
40933     
40934     getName: function()
40935     {
40936         return this.name;
40937     },
40938     
40939     beforeBlur : function()
40940     {
40941         if(!this.castInt){
40942             return;
40943         }
40944         
40945         var v = this.parseValue(this.getRawValue());
40946         
40947         if(v || v == 0){
40948             this.setValue(v);
40949         }
40950     },
40951     
40952     onBlur : function()
40953     {
40954         this.beforeBlur();
40955         
40956         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40957             //this.el.removeClass(this.focusClass);
40958         }
40959         
40960         this.hasFocus = false;
40961         
40962         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40963             this.validate();
40964         }
40965         
40966         var v = this.getValue();
40967         
40968         if(String(v) !== String(this.startValue)){
40969             this.fireEvent('change', this, v, this.startValue);
40970         }
40971         
40972         this.fireEvent("blur", this);
40973     },
40974     
40975     inputEl : function()
40976     {
40977         return this.el.select('.roo-money-amount-input', true).first();
40978     },
40979     
40980     currencyEl : function()
40981     {
40982         return this.el.select('.roo-money-currency-input', true).first();
40983     },
40984     
40985     hiddenEl : function()
40986     {
40987         return this.el.select('input.hidden-number-input',true).first();
40988     }
40989     
40990 });