70aaa9c6616be22e2baeb397b95a42d266231391
[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(
2843                 this.height &&
2844                 this.height <= Roo.lib.Dom.getViewportHeight(true) - 60
2845             ) {
2846                 this.setSize(w, this.height);
2847                 return;
2848             }
2849             
2850             if(
2851                 this.height &&
2852                 this.height > Roo.lib.Dom.getViewportHeight(true) - 60
2853             ) {
2854                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2855                 return;
2856             }
2857             
2858             if(!this.fit_content) {
2859                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2860                 return;
2861             }
2862             
2863             var body_childs = this.bodyEl.dom.childNodes;
2864             // does not seem to give enough space...
2865             var full_height = 60 + this.headerEl.getHeight() + this.footerEl.getHeight();
2866             for(var i = 0; i < body_childs.length; i++) {
2867                 full_height += body_childs[i].offsetHeight;
2868             }
2869             
2870             this.setSize(w, Math.min(full_height, Roo.lib.Dom.getViewportHeight(true) - 60));
2871         }
2872         
2873     },
2874
2875     setSize : function(w,h)
2876     {
2877         if (!w && !h) {
2878             return;
2879         }
2880         this.resizeTo(w,h);
2881     },
2882
2883     show : function() {
2884
2885         if (!this.rendered) {
2886             this.render();
2887         }
2888
2889         //this.el.setStyle('display', 'block');
2890         this.el.removeClass('hideing');        
2891         this.el.addClass('show');
2892  
2893         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2894             var _this = this;
2895             (function(){
2896                 this.el.addClass('in');
2897             }).defer(50, this);
2898         }else{
2899             this.el.addClass('in');
2900         }
2901
2902         // not sure how we can show data in here..
2903         //if (this.tmpl) {
2904         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2905         //}
2906
2907         Roo.get(document.body).addClass("x-body-masked");
2908         
2909         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2910         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2911         this.maskEl.addClass('show');
2912         
2913         this.resize();
2914         
2915         this.fireEvent('show', this);
2916
2917         // set zindex here - otherwise it appears to be ignored...
2918         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2919
2920         (function () {
2921             this.items.forEach( function(e) {
2922                 e.layout ? e.layout() : false;
2923
2924             });
2925         }).defer(100,this);
2926
2927     },
2928     hide : function()
2929     {
2930         if(this.fireEvent("beforehide", this) !== false){
2931             this.maskEl.removeClass('show');
2932             Roo.get(document.body).removeClass("x-body-masked");
2933             this.el.removeClass('in');
2934             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2935
2936             if(this.animate){ // why
2937                 this.el.addClass('hideing');
2938                 (function(){
2939                     if (!this.el.hasClass('hideing')) {
2940                         return; // it's been shown again...
2941                     }
2942                     this.el.removeClass('show');
2943                     this.el.removeClass('hideing');
2944                 }).defer(150,this);
2945                 
2946             }else{
2947                  this.el.removeClass('show');
2948             }
2949             this.fireEvent('hide', this);
2950         }
2951     },
2952     isVisible : function()
2953     {
2954         
2955         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2956         
2957     },
2958
2959     addButton : function(str, cb)
2960     {
2961
2962
2963         var b = Roo.apply({}, { html : str } );
2964         b.xns = b.xns || Roo.bootstrap;
2965         b.xtype = b.xtype || 'Button';
2966         if (typeof(b.listeners) == 'undefined') {
2967             b.listeners = { click : cb.createDelegate(this)  };
2968         }
2969
2970         var btn = Roo.factory(b);
2971
2972         btn.render(this.el.select('.modal-footer div').first());
2973
2974         return btn;
2975
2976     },
2977
2978     setDefaultButton : function(btn)
2979     {
2980         //this.el.select('.modal-footer').()
2981     },
2982     diff : false,
2983
2984     resizeTo: function(w,h)
2985     {
2986         // skip.. ?? why??
2987
2988         this.dialogEl.setWidth(w);
2989         if (this.diff === false) {
2990             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2991         }
2992
2993         this.bodyEl.setHeight(h-this.diff);
2994
2995         this.fireEvent('resize', this);
2996
2997     },
2998     setContentSize  : function(w, h)
2999     {
3000
3001     },
3002     onButtonClick: function(btn,e)
3003     {
3004         //Roo.log([a,b,c]);
3005         this.fireEvent('btnclick', btn.name, e);
3006     },
3007      /**
3008      * Set the title of the Dialog
3009      * @param {String} str new Title
3010      */
3011     setTitle: function(str) {
3012         this.titleEl.dom.innerHTML = str;
3013     },
3014     /**
3015      * Set the body of the Dialog
3016      * @param {String} str new Title
3017      */
3018     setBody: function(str) {
3019         this.bodyEl.dom.innerHTML = str;
3020     },
3021     /**
3022      * Set the body of the Dialog using the template
3023      * @param {Obj} data - apply this data to the template and replace the body contents.
3024      */
3025     applyBody: function(obj)
3026     {
3027         if (!this.tmpl) {
3028             Roo.log("Error - using apply Body without a template");
3029             //code
3030         }
3031         this.tmpl.overwrite(this.bodyEl, obj);
3032     }
3033
3034 });
3035
3036
3037 Roo.apply(Roo.bootstrap.Modal,  {
3038     /**
3039          * Button config that displays a single OK button
3040          * @type Object
3041          */
3042         OK :  [{
3043             name : 'ok',
3044             weight : 'primary',
3045             html : 'OK'
3046         }],
3047         /**
3048          * Button config that displays Yes and No buttons
3049          * @type Object
3050          */
3051         YESNO : [
3052             {
3053                 name  : 'no',
3054                 html : 'No'
3055             },
3056             {
3057                 name  :'yes',
3058                 weight : 'primary',
3059                 html : 'Yes'
3060             }
3061         ],
3062
3063         /**
3064          * Button config that displays OK and Cancel buttons
3065          * @type Object
3066          */
3067         OKCANCEL : [
3068             {
3069                name : 'cancel',
3070                 html : 'Cancel'
3071             },
3072             {
3073                 name : 'ok',
3074                 weight : 'primary',
3075                 html : 'OK'
3076             }
3077         ],
3078         /**
3079          * Button config that displays Yes, No and Cancel buttons
3080          * @type Object
3081          */
3082         YESNOCANCEL : [
3083             {
3084                 name : 'yes',
3085                 weight : 'primary',
3086                 html : 'Yes'
3087             },
3088             {
3089                 name : 'no',
3090                 html : 'No'
3091             },
3092             {
3093                 name : 'cancel',
3094                 html : 'Cancel'
3095             }
3096         ],
3097         
3098         zIndex : 10001
3099 });
3100 /*
3101  * - LGPL
3102  *
3103  * messagebox - can be used as a replace
3104  * 
3105  */
3106 /**
3107  * @class Roo.MessageBox
3108  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3109  * Example usage:
3110  *<pre><code>
3111 // Basic alert:
3112 Roo.Msg.alert('Status', 'Changes saved successfully.');
3113
3114 // Prompt for user data:
3115 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3116     if (btn == 'ok'){
3117         // process text value...
3118     }
3119 });
3120
3121 // Show a dialog using config options:
3122 Roo.Msg.show({
3123    title:'Save Changes?',
3124    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3125    buttons: Roo.Msg.YESNOCANCEL,
3126    fn: processResult,
3127    animEl: 'elId'
3128 });
3129 </code></pre>
3130  * @singleton
3131  */
3132 Roo.bootstrap.MessageBox = function(){
3133     var dlg, opt, mask, waitTimer;
3134     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3135     var buttons, activeTextEl, bwidth;
3136
3137     
3138     // private
3139     var handleButton = function(button){
3140         dlg.hide();
3141         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3142     };
3143
3144     // private
3145     var handleHide = function(){
3146         if(opt && opt.cls){
3147             dlg.el.removeClass(opt.cls);
3148         }
3149         //if(waitTimer){
3150         //    Roo.TaskMgr.stop(waitTimer);
3151         //    waitTimer = null;
3152         //}
3153     };
3154
3155     // private
3156     var updateButtons = function(b){
3157         var width = 0;
3158         if(!b){
3159             buttons["ok"].hide();
3160             buttons["cancel"].hide();
3161             buttons["yes"].hide();
3162             buttons["no"].hide();
3163             //dlg.footer.dom.style.display = 'none';
3164             return width;
3165         }
3166         dlg.footerEl.dom.style.display = '';
3167         for(var k in buttons){
3168             if(typeof buttons[k] != "function"){
3169                 if(b[k]){
3170                     buttons[k].show();
3171                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3172                     width += buttons[k].el.getWidth()+15;
3173                 }else{
3174                     buttons[k].hide();
3175                 }
3176             }
3177         }
3178         return width;
3179     };
3180
3181     // private
3182     var handleEsc = function(d, k, e){
3183         if(opt && opt.closable !== false){
3184             dlg.hide();
3185         }
3186         if(e){
3187             e.stopEvent();
3188         }
3189     };
3190
3191     return {
3192         /**
3193          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3194          * @return {Roo.BasicDialog} The BasicDialog element
3195          */
3196         getDialog : function(){
3197            if(!dlg){
3198                 dlg = new Roo.bootstrap.Modal( {
3199                     //draggable: true,
3200                     //resizable:false,
3201                     //constraintoviewport:false,
3202                     //fixedcenter:true,
3203                     //collapsible : false,
3204                     //shim:true,
3205                     //modal: true,
3206                 //    width: 'auto',
3207                   //  height:100,
3208                     //buttonAlign:"center",
3209                     closeClick : function(){
3210                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3211                             handleButton("no");
3212                         }else{
3213                             handleButton("cancel");
3214                         }
3215                     }
3216                 });
3217                 dlg.render();
3218                 dlg.on("hide", handleHide);
3219                 mask = dlg.mask;
3220                 //dlg.addKeyListener(27, handleEsc);
3221                 buttons = {};
3222                 this.buttons = buttons;
3223                 var bt = this.buttonText;
3224                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3225                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3226                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3227                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3228                 //Roo.log(buttons);
3229                 bodyEl = dlg.bodyEl.createChild({
3230
3231                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3232                         '<textarea class="roo-mb-textarea"></textarea>' +
3233                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3234                 });
3235                 msgEl = bodyEl.dom.firstChild;
3236                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3237                 textboxEl.enableDisplayMode();
3238                 textboxEl.addKeyListener([10,13], function(){
3239                     if(dlg.isVisible() && opt && opt.buttons){
3240                         if(opt.buttons.ok){
3241                             handleButton("ok");
3242                         }else if(opt.buttons.yes){
3243                             handleButton("yes");
3244                         }
3245                     }
3246                 });
3247                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3248                 textareaEl.enableDisplayMode();
3249                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3250                 progressEl.enableDisplayMode();
3251                 
3252                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3253                 var pf = progressEl.dom.firstChild;
3254                 if (pf) {
3255                     pp = Roo.get(pf.firstChild);
3256                     pp.setHeight(pf.offsetHeight);
3257                 }
3258                 
3259             }
3260             return dlg;
3261         },
3262
3263         /**
3264          * Updates the message box body text
3265          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3266          * the XHTML-compliant non-breaking space character '&amp;#160;')
3267          * @return {Roo.MessageBox} This message box
3268          */
3269         updateText : function(text)
3270         {
3271             if(!dlg.isVisible() && !opt.width){
3272                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3273                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3274             }
3275             msgEl.innerHTML = text || '&#160;';
3276       
3277             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3278             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3279             var w = Math.max(
3280                     Math.min(opt.width || cw , this.maxWidth), 
3281                     Math.max(opt.minWidth || this.minWidth, bwidth)
3282             );
3283             if(opt.prompt){
3284                 activeTextEl.setWidth(w);
3285             }
3286             if(dlg.isVisible()){
3287                 dlg.fixedcenter = false;
3288             }
3289             // to big, make it scroll. = But as usual stupid IE does not support
3290             // !important..
3291             
3292             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3293                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3294                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3295             } else {
3296                 bodyEl.dom.style.height = '';
3297                 bodyEl.dom.style.overflowY = '';
3298             }
3299             if (cw > w) {
3300                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3301             } else {
3302                 bodyEl.dom.style.overflowX = '';
3303             }
3304             
3305             dlg.setContentSize(w, bodyEl.getHeight());
3306             if(dlg.isVisible()){
3307                 dlg.fixedcenter = true;
3308             }
3309             return this;
3310         },
3311
3312         /**
3313          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3314          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3315          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3316          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3317          * @return {Roo.MessageBox} This message box
3318          */
3319         updateProgress : function(value, text){
3320             if(text){
3321                 this.updateText(text);
3322             }
3323             
3324             if (pp) { // weird bug on my firefox - for some reason this is not defined
3325                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3326                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3327             }
3328             return this;
3329         },        
3330
3331         /**
3332          * Returns true if the message box is currently displayed
3333          * @return {Boolean} True if the message box is visible, else false
3334          */
3335         isVisible : function(){
3336             return dlg && dlg.isVisible();  
3337         },
3338
3339         /**
3340          * Hides the message box if it is displayed
3341          */
3342         hide : function(){
3343             if(this.isVisible()){
3344                 dlg.hide();
3345             }  
3346         },
3347
3348         /**
3349          * Displays a new message box, or reinitializes an existing message box, based on the config options
3350          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3351          * The following config object properties are supported:
3352          * <pre>
3353 Property    Type             Description
3354 ----------  ---------------  ------------------------------------------------------------------------------------
3355 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3356                                    closes (defaults to undefined)
3357 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3358                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3359 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3360                                    progress and wait dialogs will ignore this property and always hide the
3361                                    close button as they can only be closed programmatically.
3362 cls               String           A custom CSS class to apply to the message box element
3363 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3364                                    displayed (defaults to 75)
3365 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3366                                    function will be btn (the name of the button that was clicked, if applicable,
3367                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3368                                    Progress and wait dialogs will ignore this option since they do not respond to
3369                                    user actions and can only be closed programmatically, so any required function
3370                                    should be called by the same code after it closes the dialog.
3371 icon              String           A CSS class that provides a background image to be used as an icon for
3372                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3373 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3374 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3375 modal             Boolean          False to allow user interaction with the page while the message box is
3376                                    displayed (defaults to true)
3377 msg               String           A string that will replace the existing message box body text (defaults
3378                                    to the XHTML-compliant non-breaking space character '&#160;')
3379 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3380 progress          Boolean          True to display a progress bar (defaults to false)
3381 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3382 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3383 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3384 title             String           The title text
3385 value             String           The string value to set into the active textbox element if displayed
3386 wait              Boolean          True to display a progress bar (defaults to false)
3387 width             Number           The width of the dialog in pixels
3388 </pre>
3389          *
3390          * Example usage:
3391          * <pre><code>
3392 Roo.Msg.show({
3393    title: 'Address',
3394    msg: 'Please enter your address:',
3395    width: 300,
3396    buttons: Roo.MessageBox.OKCANCEL,
3397    multiline: true,
3398    fn: saveAddress,
3399    animEl: 'addAddressBtn'
3400 });
3401 </code></pre>
3402          * @param {Object} config Configuration options
3403          * @return {Roo.MessageBox} This message box
3404          */
3405         show : function(options)
3406         {
3407             
3408             // this causes nightmares if you show one dialog after another
3409             // especially on callbacks..
3410              
3411             if(this.isVisible()){
3412                 
3413                 this.hide();
3414                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3415                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3416                 Roo.log("New Dialog Message:" +  options.msg )
3417                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3418                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3419                 
3420             }
3421             var d = this.getDialog();
3422             opt = options;
3423             d.setTitle(opt.title || "&#160;");
3424             d.closeEl.setDisplayed(opt.closable !== false);
3425             activeTextEl = textboxEl;
3426             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3427             if(opt.prompt){
3428                 if(opt.multiline){
3429                     textboxEl.hide();
3430                     textareaEl.show();
3431                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3432                         opt.multiline : this.defaultTextHeight);
3433                     activeTextEl = textareaEl;
3434                 }else{
3435                     textboxEl.show();
3436                     textareaEl.hide();
3437                 }
3438             }else{
3439                 textboxEl.hide();
3440                 textareaEl.hide();
3441             }
3442             progressEl.setDisplayed(opt.progress === true);
3443             this.updateProgress(0);
3444             activeTextEl.dom.value = opt.value || "";
3445             if(opt.prompt){
3446                 dlg.setDefaultButton(activeTextEl);
3447             }else{
3448                 var bs = opt.buttons;
3449                 var db = null;
3450                 if(bs && bs.ok){
3451                     db = buttons["ok"];
3452                 }else if(bs && bs.yes){
3453                     db = buttons["yes"];
3454                 }
3455                 dlg.setDefaultButton(db);
3456             }
3457             bwidth = updateButtons(opt.buttons);
3458             this.updateText(opt.msg);
3459             if(opt.cls){
3460                 d.el.addClass(opt.cls);
3461             }
3462             d.proxyDrag = opt.proxyDrag === true;
3463             d.modal = opt.modal !== false;
3464             d.mask = opt.modal !== false ? mask : false;
3465             if(!d.isVisible()){
3466                 // force it to the end of the z-index stack so it gets a cursor in FF
3467                 document.body.appendChild(dlg.el.dom);
3468                 d.animateTarget = null;
3469                 d.show(options.animEl);
3470             }
3471             return this;
3472         },
3473
3474         /**
3475          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3476          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3477          * and closing the message box when the process is complete.
3478          * @param {String} title The title bar text
3479          * @param {String} msg The message box body text
3480          * @return {Roo.MessageBox} This message box
3481          */
3482         progress : function(title, msg){
3483             this.show({
3484                 title : title,
3485                 msg : msg,
3486                 buttons: false,
3487                 progress:true,
3488                 closable:false,
3489                 minWidth: this.minProgressWidth,
3490                 modal : true
3491             });
3492             return this;
3493         },
3494
3495         /**
3496          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3497          * If a callback function is passed it will be called after the user clicks the button, and the
3498          * id of the button that was clicked will be passed as the only parameter to the callback
3499          * (could also be the top-right close button).
3500          * @param {String} title The title bar text
3501          * @param {String} msg The message box body text
3502          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3503          * @param {Object} scope (optional) The scope of the callback function
3504          * @return {Roo.MessageBox} This message box
3505          */
3506         alert : function(title, msg, fn, scope)
3507         {
3508             this.show({
3509                 title : title,
3510                 msg : msg,
3511                 buttons: this.OK,
3512                 fn: fn,
3513                 closable : false,
3514                 scope : scope,
3515                 modal : true
3516             });
3517             return this;
3518         },
3519
3520         /**
3521          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3522          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3523          * You are responsible for closing the message box when the process is complete.
3524          * @param {String} msg The message box body text
3525          * @param {String} title (optional) The title bar text
3526          * @return {Roo.MessageBox} This message box
3527          */
3528         wait : function(msg, title){
3529             this.show({
3530                 title : title,
3531                 msg : msg,
3532                 buttons: false,
3533                 closable:false,
3534                 progress:true,
3535                 modal:true,
3536                 width:300,
3537                 wait:true
3538             });
3539             waitTimer = Roo.TaskMgr.start({
3540                 run: function(i){
3541                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3542                 },
3543                 interval: 1000
3544             });
3545             return this;
3546         },
3547
3548         /**
3549          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3550          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3551          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3552          * @param {String} title The title bar text
3553          * @param {String} msg The message box body text
3554          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3555          * @param {Object} scope (optional) The scope of the callback function
3556          * @return {Roo.MessageBox} This message box
3557          */
3558         confirm : function(title, msg, fn, scope){
3559             this.show({
3560                 title : title,
3561                 msg : msg,
3562                 buttons: this.YESNO,
3563                 fn: fn,
3564                 scope : scope,
3565                 modal : true
3566             });
3567             return this;
3568         },
3569
3570         /**
3571          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3572          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3573          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3574          * (could also be the top-right close button) and the text that was entered will be passed as the two
3575          * parameters to the callback.
3576          * @param {String} title The title bar text
3577          * @param {String} msg The message box body text
3578          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3579          * @param {Object} scope (optional) The scope of the callback function
3580          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3581          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3582          * @return {Roo.MessageBox} This message box
3583          */
3584         prompt : function(title, msg, fn, scope, multiline){
3585             this.show({
3586                 title : title,
3587                 msg : msg,
3588                 buttons: this.OKCANCEL,
3589                 fn: fn,
3590                 minWidth:250,
3591                 scope : scope,
3592                 prompt:true,
3593                 multiline: multiline,
3594                 modal : true
3595             });
3596             return this;
3597         },
3598
3599         /**
3600          * Button config that displays a single OK button
3601          * @type Object
3602          */
3603         OK : {ok:true},
3604         /**
3605          * Button config that displays Yes and No buttons
3606          * @type Object
3607          */
3608         YESNO : {yes:true, no:true},
3609         /**
3610          * Button config that displays OK and Cancel buttons
3611          * @type Object
3612          */
3613         OKCANCEL : {ok:true, cancel:true},
3614         /**
3615          * Button config that displays Yes, No and Cancel buttons
3616          * @type Object
3617          */
3618         YESNOCANCEL : {yes:true, no:true, cancel:true},
3619
3620         /**
3621          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3622          * @type Number
3623          */
3624         defaultTextHeight : 75,
3625         /**
3626          * The maximum width in pixels of the message box (defaults to 600)
3627          * @type Number
3628          */
3629         maxWidth : 600,
3630         /**
3631          * The minimum width in pixels of the message box (defaults to 100)
3632          * @type Number
3633          */
3634         minWidth : 100,
3635         /**
3636          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3637          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3638          * @type Number
3639          */
3640         minProgressWidth : 250,
3641         /**
3642          * An object containing the default button text strings that can be overriden for localized language support.
3643          * Supported properties are: ok, cancel, yes and no.
3644          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3645          * @type Object
3646          */
3647         buttonText : {
3648             ok : "OK",
3649             cancel : "Cancel",
3650             yes : "Yes",
3651             no : "No"
3652         }
3653     };
3654 }();
3655
3656 /**
3657  * Shorthand for {@link Roo.MessageBox}
3658  */
3659 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3660 Roo.Msg = Roo.Msg || Roo.MessageBox;
3661 /*
3662  * - LGPL
3663  *
3664  * navbar
3665  * 
3666  */
3667
3668 /**
3669  * @class Roo.bootstrap.Navbar
3670  * @extends Roo.bootstrap.Component
3671  * Bootstrap Navbar class
3672
3673  * @constructor
3674  * Create a new Navbar
3675  * @param {Object} config The config object
3676  */
3677
3678
3679 Roo.bootstrap.Navbar = function(config){
3680     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3681     this.addEvents({
3682         // raw events
3683         /**
3684          * @event beforetoggle
3685          * Fire before toggle the menu
3686          * @param {Roo.EventObject} e
3687          */
3688         "beforetoggle" : true
3689     });
3690 };
3691
3692 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3693     
3694     
3695    
3696     // private
3697     navItems : false,
3698     loadMask : false,
3699     
3700     
3701     getAutoCreate : function(){
3702         
3703         
3704         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3705         
3706     },
3707     
3708     initEvents :function ()
3709     {
3710         //Roo.log(this.el.select('.navbar-toggle',true));
3711         this.el.select('.navbar-toggle',true).on('click', function() {
3712             if(this.fireEvent('beforetoggle', this) !== false){
3713                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3714             }
3715             
3716         }, this);
3717         
3718         var mark = {
3719             tag: "div",
3720             cls:"x-dlg-mask"
3721         };
3722         
3723         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3724         
3725         var size = this.el.getSize();
3726         this.maskEl.setSize(size.width, size.height);
3727         this.maskEl.enableDisplayMode("block");
3728         this.maskEl.hide();
3729         
3730         if(this.loadMask){
3731             this.maskEl.show();
3732         }
3733     },
3734     
3735     
3736     getChildContainer : function()
3737     {
3738         if (this.el.select('.collapse').getCount()) {
3739             return this.el.select('.collapse',true).first();
3740         }
3741         
3742         return this.el;
3743     },
3744     
3745     mask : function()
3746     {
3747         this.maskEl.show();
3748     },
3749     
3750     unmask : function()
3751     {
3752         this.maskEl.hide();
3753     } 
3754     
3755     
3756     
3757     
3758 });
3759
3760
3761
3762  
3763
3764  /*
3765  * - LGPL
3766  *
3767  * navbar
3768  * 
3769  */
3770
3771 /**
3772  * @class Roo.bootstrap.NavSimplebar
3773  * @extends Roo.bootstrap.Navbar
3774  * Bootstrap Sidebar class
3775  *
3776  * @cfg {Boolean} inverse is inverted color
3777  * 
3778  * @cfg {String} type (nav | pills | tabs)
3779  * @cfg {Boolean} arrangement stacked | justified
3780  * @cfg {String} align (left | right) alignment
3781  * 
3782  * @cfg {Boolean} main (true|false) main nav bar? default false
3783  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3784  * 
3785  * @cfg {String} tag (header|footer|nav|div) default is nav 
3786
3787  * 
3788  * 
3789  * 
3790  * @constructor
3791  * Create a new Sidebar
3792  * @param {Object} config The config object
3793  */
3794
3795
3796 Roo.bootstrap.NavSimplebar = function(config){
3797     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3798 };
3799
3800 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3801     
3802     inverse: false,
3803     
3804     type: false,
3805     arrangement: '',
3806     align : false,
3807     
3808     
3809     
3810     main : false,
3811     
3812     
3813     tag : false,
3814     
3815     
3816     getAutoCreate : function(){
3817         
3818         
3819         var cfg = {
3820             tag : this.tag || 'div',
3821             cls : 'navbar'
3822         };
3823           
3824         
3825         cfg.cn = [
3826             {
3827                 cls: 'nav',
3828                 tag : 'ul'
3829             }
3830         ];
3831         
3832          
3833         this.type = this.type || 'nav';
3834         if (['tabs','pills'].indexOf(this.type)!==-1) {
3835             cfg.cn[0].cls += ' nav-' + this.type
3836         
3837         
3838         } else {
3839             if (this.type!=='nav') {
3840                 Roo.log('nav type must be nav/tabs/pills')
3841             }
3842             cfg.cn[0].cls += ' navbar-nav'
3843         }
3844         
3845         
3846         
3847         
3848         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3849             cfg.cn[0].cls += ' nav-' + this.arrangement;
3850         }
3851         
3852         
3853         if (this.align === 'right') {
3854             cfg.cn[0].cls += ' navbar-right';
3855         }
3856         
3857         if (this.inverse) {
3858             cfg.cls += ' navbar-inverse';
3859             
3860         }
3861         
3862         
3863         return cfg;
3864     
3865         
3866     }
3867     
3868     
3869     
3870 });
3871
3872
3873
3874  
3875
3876  
3877        /*
3878  * - LGPL
3879  *
3880  * navbar
3881  * 
3882  */
3883
3884 /**
3885  * @class Roo.bootstrap.NavHeaderbar
3886  * @extends Roo.bootstrap.NavSimplebar
3887  * Bootstrap Sidebar class
3888  *
3889  * @cfg {String} brand what is brand
3890  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3891  * @cfg {String} brand_href href of the brand
3892  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3893  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3894  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3895  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3896  * 
3897  * @constructor
3898  * Create a new Sidebar
3899  * @param {Object} config The config object
3900  */
3901
3902
3903 Roo.bootstrap.NavHeaderbar = function(config){
3904     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3905       
3906 };
3907
3908 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3909     
3910     position: '',
3911     brand: '',
3912     brand_href: false,
3913     srButton : true,
3914     autohide : false,
3915     desktopCenter : false,
3916    
3917     
3918     getAutoCreate : function(){
3919         
3920         var   cfg = {
3921             tag: this.nav || 'nav',
3922             cls: 'navbar',
3923             role: 'navigation',
3924             cn: []
3925         };
3926         
3927         var cn = cfg.cn;
3928         if (this.desktopCenter) {
3929             cn.push({cls : 'container', cn : []});
3930             cn = cn[0].cn;
3931         }
3932         
3933         if(this.srButton){
3934             cn.push({
3935                 tag: 'div',
3936                 cls: 'navbar-header',
3937                 cn: [
3938                     {
3939                         tag: 'button',
3940                         type: 'button',
3941                         cls: 'navbar-toggle',
3942                         'data-toggle': 'collapse',
3943                         cn: [
3944                             {
3945                                 tag: 'span',
3946                                 cls: 'sr-only',
3947                                 html: 'Toggle navigation'
3948                             },
3949                             {
3950                                 tag: 'span',
3951                                 cls: 'icon-bar'
3952                             },
3953                             {
3954                                 tag: 'span',
3955                                 cls: 'icon-bar'
3956                             },
3957                             {
3958                                 tag: 'span',
3959                                 cls: 'icon-bar'
3960                             }
3961                         ]
3962                     }
3963                 ]
3964             });
3965         }
3966         
3967         cn.push({
3968             tag: 'div',
3969             cls: 'collapse navbar-collapse',
3970             cn : []
3971         });
3972         
3973         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3974         
3975         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3976             cfg.cls += ' navbar-' + this.position;
3977             
3978             // tag can override this..
3979             
3980             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3981         }
3982         
3983         if (this.brand !== '') {
3984             cn[0].cn.push({
3985                 tag: 'a',
3986                 href: this.brand_href ? this.brand_href : '#',
3987                 cls: 'navbar-brand',
3988                 cn: [
3989                 this.brand
3990                 ]
3991             });
3992         }
3993         
3994         if(this.main){
3995             cfg.cls += ' main-nav';
3996         }
3997         
3998         
3999         return cfg;
4000
4001         
4002     },
4003     getHeaderChildContainer : function()
4004     {
4005         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4006             return this.el.select('.navbar-header',true).first();
4007         }
4008         
4009         return this.getChildContainer();
4010     },
4011     
4012     
4013     initEvents : function()
4014     {
4015         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4016         
4017         if (this.autohide) {
4018             
4019             var prevScroll = 0;
4020             var ft = this.el;
4021             
4022             Roo.get(document).on('scroll',function(e) {
4023                 var ns = Roo.get(document).getScroll().top;
4024                 var os = prevScroll;
4025                 prevScroll = ns;
4026                 
4027                 if(ns > os){
4028                     ft.removeClass('slideDown');
4029                     ft.addClass('slideUp');
4030                     return;
4031                 }
4032                 ft.removeClass('slideUp');
4033                 ft.addClass('slideDown');
4034                  
4035               
4036           },this);
4037         }
4038     }    
4039     
4040 });
4041
4042
4043
4044  
4045
4046  /*
4047  * - LGPL
4048  *
4049  * navbar
4050  * 
4051  */
4052
4053 /**
4054  * @class Roo.bootstrap.NavSidebar
4055  * @extends Roo.bootstrap.Navbar
4056  * Bootstrap Sidebar class
4057  * 
4058  * @constructor
4059  * Create a new Sidebar
4060  * @param {Object} config The config object
4061  */
4062
4063
4064 Roo.bootstrap.NavSidebar = function(config){
4065     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4066 };
4067
4068 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4069     
4070     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4071     
4072     getAutoCreate : function(){
4073         
4074         
4075         return  {
4076             tag: 'div',
4077             cls: 'sidebar sidebar-nav'
4078         };
4079     
4080         
4081     }
4082     
4083     
4084     
4085 });
4086
4087
4088
4089  
4090
4091  /*
4092  * - LGPL
4093  *
4094  * nav group
4095  * 
4096  */
4097
4098 /**
4099  * @class Roo.bootstrap.NavGroup
4100  * @extends Roo.bootstrap.Component
4101  * Bootstrap NavGroup class
4102  * @cfg {String} align (left|right)
4103  * @cfg {Boolean} inverse
4104  * @cfg {String} type (nav|pills|tab) default nav
4105  * @cfg {String} navId - reference Id for navbar.
4106
4107  * 
4108  * @constructor
4109  * Create a new nav group
4110  * @param {Object} config The config object
4111  */
4112
4113 Roo.bootstrap.NavGroup = function(config){
4114     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4115     this.navItems = [];
4116    
4117     Roo.bootstrap.NavGroup.register(this);
4118      this.addEvents({
4119         /**
4120              * @event changed
4121              * Fires when the active item changes
4122              * @param {Roo.bootstrap.NavGroup} this
4123              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4124              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4125          */
4126         'changed': true
4127      });
4128     
4129 };
4130
4131 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4132     
4133     align: '',
4134     inverse: false,
4135     form: false,
4136     type: 'nav',
4137     navId : '',
4138     // private
4139     
4140     navItems : false, 
4141     
4142     getAutoCreate : function()
4143     {
4144         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4145         
4146         cfg = {
4147             tag : 'ul',
4148             cls: 'nav' 
4149         };
4150         
4151         if (['tabs','pills'].indexOf(this.type)!==-1) {
4152             cfg.cls += ' nav-' + this.type
4153         } else {
4154             if (this.type!=='nav') {
4155                 Roo.log('nav type must be nav/tabs/pills')
4156             }
4157             cfg.cls += ' navbar-nav'
4158         }
4159         
4160         if (this.parent() && this.parent().sidebar) {
4161             cfg = {
4162                 tag: 'ul',
4163                 cls: 'dashboard-menu sidebar-menu'
4164             };
4165             
4166             return cfg;
4167         }
4168         
4169         if (this.form === true) {
4170             cfg = {
4171                 tag: 'form',
4172                 cls: 'navbar-form'
4173             };
4174             
4175             if (this.align === 'right') {
4176                 cfg.cls += ' navbar-right';
4177             } else {
4178                 cfg.cls += ' navbar-left';
4179             }
4180         }
4181         
4182         if (this.align === 'right') {
4183             cfg.cls += ' navbar-right';
4184         }
4185         
4186         if (this.inverse) {
4187             cfg.cls += ' navbar-inverse';
4188             
4189         }
4190         
4191         
4192         return cfg;
4193     },
4194     /**
4195     * sets the active Navigation item
4196     * @param {Roo.bootstrap.NavItem} the new current navitem
4197     */
4198     setActiveItem : function(item)
4199     {
4200         var prev = false;
4201         Roo.each(this.navItems, function(v){
4202             if (v == item) {
4203                 return ;
4204             }
4205             if (v.isActive()) {
4206                 v.setActive(false, true);
4207                 prev = v;
4208                 
4209             }
4210             
4211         });
4212
4213         item.setActive(true, true);
4214         this.fireEvent('changed', this, item, prev);
4215         
4216         
4217     },
4218     /**
4219     * gets the active Navigation item
4220     * @return {Roo.bootstrap.NavItem} the current navitem
4221     */
4222     getActive : function()
4223     {
4224         
4225         var prev = false;
4226         Roo.each(this.navItems, function(v){
4227             
4228             if (v.isActive()) {
4229                 prev = v;
4230                 
4231             }
4232             
4233         });
4234         return prev;
4235     },
4236     
4237     indexOfNav : function()
4238     {
4239         
4240         var prev = false;
4241         Roo.each(this.navItems, function(v,i){
4242             
4243             if (v.isActive()) {
4244                 prev = i;
4245                 
4246             }
4247             
4248         });
4249         return prev;
4250     },
4251     /**
4252     * adds a Navigation item
4253     * @param {Roo.bootstrap.NavItem} the navitem to add
4254     */
4255     addItem : function(cfg)
4256     {
4257         var cn = new Roo.bootstrap.NavItem(cfg);
4258         this.register(cn);
4259         cn.parentId = this.id;
4260         cn.onRender(this.el, null);
4261         return cn;
4262     },
4263     /**
4264     * register a Navigation item
4265     * @param {Roo.bootstrap.NavItem} the navitem to add
4266     */
4267     register : function(item)
4268     {
4269         this.navItems.push( item);
4270         item.navId = this.navId;
4271     
4272     },
4273     
4274     /**
4275     * clear all the Navigation item
4276     */
4277    
4278     clearAll : function()
4279     {
4280         this.navItems = [];
4281         this.el.dom.innerHTML = '';
4282     },
4283     
4284     getNavItem: function(tabId)
4285     {
4286         var ret = false;
4287         Roo.each(this.navItems, function(e) {
4288             if (e.tabId == tabId) {
4289                ret =  e;
4290                return false;
4291             }
4292             return true;
4293             
4294         });
4295         return ret;
4296     },
4297     
4298     setActiveNext : function()
4299     {
4300         var i = this.indexOfNav(this.getActive());
4301         if (i > this.navItems.length) {
4302             return;
4303         }
4304         this.setActiveItem(this.navItems[i+1]);
4305     },
4306     setActivePrev : function()
4307     {
4308         var i = this.indexOfNav(this.getActive());
4309         if (i  < 1) {
4310             return;
4311         }
4312         this.setActiveItem(this.navItems[i-1]);
4313     },
4314     clearWasActive : function(except) {
4315         Roo.each(this.navItems, function(e) {
4316             if (e.tabId != except.tabId && e.was_active) {
4317                e.was_active = false;
4318                return false;
4319             }
4320             return true;
4321             
4322         });
4323     },
4324     getWasActive : function ()
4325     {
4326         var r = false;
4327         Roo.each(this.navItems, function(e) {
4328             if (e.was_active) {
4329                r = e;
4330                return false;
4331             }
4332             return true;
4333             
4334         });
4335         return r;
4336     }
4337     
4338     
4339 });
4340
4341  
4342 Roo.apply(Roo.bootstrap.NavGroup, {
4343     
4344     groups: {},
4345      /**
4346     * register a Navigation Group
4347     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4348     */
4349     register : function(navgrp)
4350     {
4351         this.groups[navgrp.navId] = navgrp;
4352         
4353     },
4354     /**
4355     * fetch a Navigation Group based on the navigation ID
4356     * @param {string} the navgroup to add
4357     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4358     */
4359     get: function(navId) {
4360         if (typeof(this.groups[navId]) == 'undefined') {
4361             return false;
4362             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4363         }
4364         return this.groups[navId] ;
4365     }
4366     
4367     
4368     
4369 });
4370
4371  /*
4372  * - LGPL
4373  *
4374  * row
4375  * 
4376  */
4377
4378 /**
4379  * @class Roo.bootstrap.NavItem
4380  * @extends Roo.bootstrap.Component
4381  * Bootstrap Navbar.NavItem class
4382  * @cfg {String} href  link to
4383  * @cfg {String} html content of button
4384  * @cfg {String} badge text inside badge
4385  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4386  * @cfg {String} glyphicon name of glyphicon
4387  * @cfg {String} icon name of font awesome icon
4388  * @cfg {Boolean} active Is item active
4389  * @cfg {Boolean} disabled Is item disabled
4390  
4391  * @cfg {Boolean} preventDefault (true | false) default false
4392  * @cfg {String} tabId the tab that this item activates.
4393  * @cfg {String} tagtype (a|span) render as a href or span?
4394  * @cfg {Boolean} animateRef (true|false) link to element default false  
4395   
4396  * @constructor
4397  * Create a new Navbar Item
4398  * @param {Object} config The config object
4399  */
4400 Roo.bootstrap.NavItem = function(config){
4401     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4402     this.addEvents({
4403         // raw events
4404         /**
4405          * @event click
4406          * The raw click event for the entire grid.
4407          * @param {Roo.EventObject} e
4408          */
4409         "click" : true,
4410          /**
4411             * @event changed
4412             * Fires when the active item active state changes
4413             * @param {Roo.bootstrap.NavItem} this
4414             * @param {boolean} state the new state
4415              
4416          */
4417         'changed': true,
4418         /**
4419             * @event scrollto
4420             * Fires when scroll to element
4421             * @param {Roo.bootstrap.NavItem} this
4422             * @param {Object} options
4423             * @param {Roo.EventObject} e
4424              
4425          */
4426         'scrollto': true
4427     });
4428    
4429 };
4430
4431 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4432     
4433     href: false,
4434     html: '',
4435     badge: '',
4436     icon: false,
4437     glyphicon: false,
4438     active: false,
4439     preventDefault : false,
4440     tabId : false,
4441     tagtype : 'a',
4442     disabled : false,
4443     animateRef : false,
4444     was_active : false,
4445     
4446     getAutoCreate : function(){
4447          
4448         var cfg = {
4449             tag: 'li',
4450             cls: 'nav-item'
4451             
4452         };
4453         
4454         if (this.active) {
4455             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4456         }
4457         if (this.disabled) {
4458             cfg.cls += ' disabled';
4459         }
4460         
4461         if (this.href || this.html || this.glyphicon || this.icon) {
4462             cfg.cn = [
4463                 {
4464                     tag: this.tagtype,
4465                     href : this.href || "#",
4466                     html: this.html || ''
4467                 }
4468             ];
4469             
4470             if (this.icon) {
4471                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4472             }
4473
4474             if(this.glyphicon) {
4475                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4476             }
4477             
4478             if (this.menu) {
4479                 
4480                 cfg.cn[0].html += " <span class='caret'></span>";
4481              
4482             }
4483             
4484             if (this.badge !== '') {
4485                  
4486                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4487             }
4488         }
4489         
4490         
4491         
4492         return cfg;
4493     },
4494     initEvents: function() 
4495     {
4496         if (typeof (this.menu) != 'undefined') {
4497             this.menu.parentType = this.xtype;
4498             this.menu.triggerEl = this.el;
4499             this.menu = this.addxtype(Roo.apply({}, this.menu));
4500         }
4501         
4502         this.el.select('a',true).on('click', this.onClick, this);
4503         
4504         if(this.tagtype == 'span'){
4505             this.el.select('span',true).on('click', this.onClick, this);
4506         }
4507        
4508         // at this point parent should be available..
4509         this.parent().register(this);
4510     },
4511     
4512     onClick : function(e)
4513     {
4514         if (e.getTarget('.dropdown-menu-item')) {
4515             // did you click on a menu itemm.... - then don't trigger onclick..
4516             return;
4517         }
4518         
4519         if(
4520                 this.preventDefault || 
4521                 this.href == '#' 
4522         ){
4523             Roo.log("NavItem - prevent Default?");
4524             e.preventDefault();
4525         }
4526         
4527         if (this.disabled) {
4528             return;
4529         }
4530         
4531         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4532         if (tg && tg.transition) {
4533             Roo.log("waiting for the transitionend");
4534             return;
4535         }
4536         
4537         
4538         
4539         //Roo.log("fire event clicked");
4540         if(this.fireEvent('click', this, e) === false){
4541             return;
4542         };
4543         
4544         if(this.tagtype == 'span'){
4545             return;
4546         }
4547         
4548         //Roo.log(this.href);
4549         var ael = this.el.select('a',true).first();
4550         //Roo.log(ael);
4551         
4552         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4553             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4554             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4555                 return; // ignore... - it's a 'hash' to another page.
4556             }
4557             Roo.log("NavItem - prevent Default?");
4558             e.preventDefault();
4559             this.scrollToElement(e);
4560         }
4561         
4562         
4563         var p =  this.parent();
4564    
4565         if (['tabs','pills'].indexOf(p.type)!==-1) {
4566             if (typeof(p.setActiveItem) !== 'undefined') {
4567                 p.setActiveItem(this);
4568             }
4569         }
4570         
4571         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4572         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4573             // remove the collapsed menu expand...
4574             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4575         }
4576     },
4577     
4578     isActive: function () {
4579         return this.active
4580     },
4581     setActive : function(state, fire, is_was_active)
4582     {
4583         if (this.active && !state && this.navId) {
4584             this.was_active = true;
4585             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4586             if (nv) {
4587                 nv.clearWasActive(this);
4588             }
4589             
4590         }
4591         this.active = state;
4592         
4593         if (!state ) {
4594             this.el.removeClass('active');
4595         } else if (!this.el.hasClass('active')) {
4596             this.el.addClass('active');
4597         }
4598         if (fire) {
4599             this.fireEvent('changed', this, state);
4600         }
4601         
4602         // show a panel if it's registered and related..
4603         
4604         if (!this.navId || !this.tabId || !state || is_was_active) {
4605             return;
4606         }
4607         
4608         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4609         if (!tg) {
4610             return;
4611         }
4612         var pan = tg.getPanelByName(this.tabId);
4613         if (!pan) {
4614             return;
4615         }
4616         // if we can not flip to new panel - go back to old nav highlight..
4617         if (false == tg.showPanel(pan)) {
4618             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4619             if (nv) {
4620                 var onav = nv.getWasActive();
4621                 if (onav) {
4622                     onav.setActive(true, false, true);
4623                 }
4624             }
4625             
4626         }
4627         
4628         
4629         
4630     },
4631      // this should not be here...
4632     setDisabled : function(state)
4633     {
4634         this.disabled = state;
4635         if (!state ) {
4636             this.el.removeClass('disabled');
4637         } else if (!this.el.hasClass('disabled')) {
4638             this.el.addClass('disabled');
4639         }
4640         
4641     },
4642     
4643     /**
4644      * Fetch the element to display the tooltip on.
4645      * @return {Roo.Element} defaults to this.el
4646      */
4647     tooltipEl : function()
4648     {
4649         return this.el.select('' + this.tagtype + '', true).first();
4650     },
4651     
4652     scrollToElement : function(e)
4653     {
4654         var c = document.body;
4655         
4656         /*
4657          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4658          */
4659         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4660             c = document.documentElement;
4661         }
4662         
4663         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4664         
4665         if(!target){
4666             return;
4667         }
4668
4669         var o = target.calcOffsetsTo(c);
4670         
4671         var options = {
4672             target : target,
4673             value : o[1]
4674         };
4675         
4676         this.fireEvent('scrollto', this, options, e);
4677         
4678         Roo.get(c).scrollTo('top', options.value, true);
4679         
4680         return;
4681     }
4682 });
4683  
4684
4685  /*
4686  * - LGPL
4687  *
4688  * sidebar item
4689  *
4690  *  li
4691  *    <span> icon </span>
4692  *    <span> text </span>
4693  *    <span>badge </span>
4694  */
4695
4696 /**
4697  * @class Roo.bootstrap.NavSidebarItem
4698  * @extends Roo.bootstrap.NavItem
4699  * Bootstrap Navbar.NavSidebarItem class
4700  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4701  * {Boolean} open is the menu open
4702  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4703  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4704  * {String} buttonSize (sm|md|lg)the extra classes for the button
4705  * {Boolean} showArrow show arrow next to the text (default true)
4706  * @constructor
4707  * Create a new Navbar Button
4708  * @param {Object} config The config object
4709  */
4710 Roo.bootstrap.NavSidebarItem = function(config){
4711     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4712     this.addEvents({
4713         // raw events
4714         /**
4715          * @event click
4716          * The raw click event for the entire grid.
4717          * @param {Roo.EventObject} e
4718          */
4719         "click" : true,
4720          /**
4721             * @event changed
4722             * Fires when the active item active state changes
4723             * @param {Roo.bootstrap.NavSidebarItem} this
4724             * @param {boolean} state the new state
4725              
4726          */
4727         'changed': true
4728     });
4729    
4730 };
4731
4732 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4733     
4734     badgeWeight : 'default',
4735     
4736     open: false,
4737     
4738     buttonView : false,
4739     
4740     buttonWeight : 'default',
4741     
4742     buttonSize : 'md',
4743     
4744     showArrow : true,
4745     
4746     getAutoCreate : function(){
4747         
4748         
4749         var a = {
4750                 tag: 'a',
4751                 href : this.href || '#',
4752                 cls: '',
4753                 html : '',
4754                 cn : []
4755         };
4756         
4757         if(this.buttonView){
4758             a = {
4759                 tag: 'button',
4760                 href : this.href || '#',
4761                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4762                 html : this.html,
4763                 cn : []
4764             };
4765         }
4766         
4767         var cfg = {
4768             tag: 'li',
4769             cls: '',
4770             cn: [ a ]
4771         };
4772         
4773         if (this.active) {
4774             cfg.cls += ' active';
4775         }
4776         
4777         if (this.disabled) {
4778             cfg.cls += ' disabled';
4779         }
4780         if (this.open) {
4781             cfg.cls += ' open x-open';
4782         }
4783         // left icon..
4784         if (this.glyphicon || this.icon) {
4785             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4786             a.cn.push({ tag : 'i', cls : c }) ;
4787         }
4788         
4789         if(!this.buttonView){
4790             var span = {
4791                 tag: 'span',
4792                 html : this.html || ''
4793             };
4794
4795             a.cn.push(span);
4796             
4797         }
4798         
4799         if (this.badge !== '') {
4800             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4801         }
4802         
4803         if (this.menu) {
4804             
4805             if(this.showArrow){
4806                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4807             }
4808             
4809             a.cls += ' dropdown-toggle treeview' ;
4810         }
4811         
4812         return cfg;
4813     },
4814     
4815     initEvents : function()
4816     { 
4817         if (typeof (this.menu) != 'undefined') {
4818             this.menu.parentType = this.xtype;
4819             this.menu.triggerEl = this.el;
4820             this.menu = this.addxtype(Roo.apply({}, this.menu));
4821         }
4822         
4823         this.el.on('click', this.onClick, this);
4824         
4825         if(this.badge !== ''){
4826             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4827         }
4828         
4829     },
4830     
4831     onClick : function(e)
4832     {
4833         if(this.disabled){
4834             e.preventDefault();
4835             return;
4836         }
4837         
4838         if(this.preventDefault){
4839             e.preventDefault();
4840         }
4841         
4842         this.fireEvent('click', this);
4843     },
4844     
4845     disable : function()
4846     {
4847         this.setDisabled(true);
4848     },
4849     
4850     enable : function()
4851     {
4852         this.setDisabled(false);
4853     },
4854     
4855     setDisabled : function(state)
4856     {
4857         if(this.disabled == state){
4858             return;
4859         }
4860         
4861         this.disabled = state;
4862         
4863         if (state) {
4864             this.el.addClass('disabled');
4865             return;
4866         }
4867         
4868         this.el.removeClass('disabled');
4869         
4870         return;
4871     },
4872     
4873     setActive : function(state)
4874     {
4875         if(this.active == state){
4876             return;
4877         }
4878         
4879         this.active = state;
4880         
4881         if (state) {
4882             this.el.addClass('active');
4883             return;
4884         }
4885         
4886         this.el.removeClass('active');
4887         
4888         return;
4889     },
4890     
4891     isActive: function () 
4892     {
4893         return this.active;
4894     },
4895     
4896     setBadge : function(str)
4897     {
4898         if(!this.badgeEl){
4899             return;
4900         }
4901         
4902         this.badgeEl.dom.innerHTML = str;
4903     }
4904     
4905    
4906      
4907  
4908 });
4909  
4910
4911  /*
4912  * - LGPL
4913  *
4914  * row
4915  * 
4916  */
4917
4918 /**
4919  * @class Roo.bootstrap.Row
4920  * @extends Roo.bootstrap.Component
4921  * Bootstrap Row class (contains columns...)
4922  * 
4923  * @constructor
4924  * Create a new Row
4925  * @param {Object} config The config object
4926  */
4927
4928 Roo.bootstrap.Row = function(config){
4929     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4930 };
4931
4932 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4933     
4934     getAutoCreate : function(){
4935        return {
4936             cls: 'row clearfix'
4937        };
4938     }
4939     
4940     
4941 });
4942
4943  
4944
4945  /*
4946  * - LGPL
4947  *
4948  * element
4949  * 
4950  */
4951
4952 /**
4953  * @class Roo.bootstrap.Element
4954  * @extends Roo.bootstrap.Component
4955  * Bootstrap Element class
4956  * @cfg {String} html contents of the element
4957  * @cfg {String} tag tag of the element
4958  * @cfg {String} cls class of the element
4959  * @cfg {Boolean} preventDefault (true|false) default false
4960  * @cfg {Boolean} clickable (true|false) default false
4961  * 
4962  * @constructor
4963  * Create a new Element
4964  * @param {Object} config The config object
4965  */
4966
4967 Roo.bootstrap.Element = function(config){
4968     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4969     
4970     this.addEvents({
4971         // raw events
4972         /**
4973          * @event click
4974          * When a element is chick
4975          * @param {Roo.bootstrap.Element} this
4976          * @param {Roo.EventObject} e
4977          */
4978         "click" : true
4979     });
4980 };
4981
4982 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4983     
4984     tag: 'div',
4985     cls: '',
4986     html: '',
4987     preventDefault: false, 
4988     clickable: false,
4989     
4990     getAutoCreate : function(){
4991         
4992         var cfg = {
4993             tag: this.tag,
4994             // cls: this.cls, double assign in parent class Component.js :: onRender
4995             html: this.html
4996         };
4997         
4998         return cfg;
4999     },
5000     
5001     initEvents: function() 
5002     {
5003         Roo.bootstrap.Element.superclass.initEvents.call(this);
5004         
5005         if(this.clickable){
5006             this.el.on('click', this.onClick, this);
5007         }
5008         
5009     },
5010     
5011     onClick : function(e)
5012     {
5013         if(this.preventDefault){
5014             e.preventDefault();
5015         }
5016         
5017         this.fireEvent('click', this, e);
5018     },
5019     
5020     getValue : function()
5021     {
5022         return this.el.dom.innerHTML;
5023     },
5024     
5025     setValue : function(value)
5026     {
5027         this.el.dom.innerHTML = value;
5028     }
5029    
5030 });
5031
5032  
5033
5034  /*
5035  * - LGPL
5036  *
5037  * pagination
5038  * 
5039  */
5040
5041 /**
5042  * @class Roo.bootstrap.Pagination
5043  * @extends Roo.bootstrap.Component
5044  * Bootstrap Pagination class
5045  * @cfg {String} size xs | sm | md | lg
5046  * @cfg {Boolean} inverse false | true
5047  * 
5048  * @constructor
5049  * Create a new Pagination
5050  * @param {Object} config The config object
5051  */
5052
5053 Roo.bootstrap.Pagination = function(config){
5054     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5055 };
5056
5057 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5058     
5059     cls: false,
5060     size: false,
5061     inverse: false,
5062     
5063     getAutoCreate : function(){
5064         var cfg = {
5065             tag: 'ul',
5066                 cls: 'pagination'
5067         };
5068         if (this.inverse) {
5069             cfg.cls += ' inverse';
5070         }
5071         if (this.html) {
5072             cfg.html=this.html;
5073         }
5074         if (this.cls) {
5075             cfg.cls += " " + this.cls;
5076         }
5077         return cfg;
5078     }
5079    
5080 });
5081
5082  
5083
5084  /*
5085  * - LGPL
5086  *
5087  * Pagination item
5088  * 
5089  */
5090
5091
5092 /**
5093  * @class Roo.bootstrap.PaginationItem
5094  * @extends Roo.bootstrap.Component
5095  * Bootstrap PaginationItem class
5096  * @cfg {String} html text
5097  * @cfg {String} href the link
5098  * @cfg {Boolean} preventDefault (true | false) default true
5099  * @cfg {Boolean} active (true | false) default false
5100  * @cfg {Boolean} disabled default false
5101  * 
5102  * 
5103  * @constructor
5104  * Create a new PaginationItem
5105  * @param {Object} config The config object
5106  */
5107
5108
5109 Roo.bootstrap.PaginationItem = function(config){
5110     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5111     this.addEvents({
5112         // raw events
5113         /**
5114          * @event click
5115          * The raw click event for the entire grid.
5116          * @param {Roo.EventObject} e
5117          */
5118         "click" : true
5119     });
5120 };
5121
5122 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5123     
5124     href : false,
5125     html : false,
5126     preventDefault: true,
5127     active : false,
5128     cls : false,
5129     disabled: false,
5130     
5131     getAutoCreate : function(){
5132         var cfg= {
5133             tag: 'li',
5134             cn: [
5135                 {
5136                     tag : 'a',
5137                     href : this.href ? this.href : '#',
5138                     html : this.html ? this.html : ''
5139                 }
5140             ]
5141         };
5142         
5143         if(this.cls){
5144             cfg.cls = this.cls;
5145         }
5146         
5147         if(this.disabled){
5148             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5149         }
5150         
5151         if(this.active){
5152             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5153         }
5154         
5155         return cfg;
5156     },
5157     
5158     initEvents: function() {
5159         
5160         this.el.on('click', this.onClick, this);
5161         
5162     },
5163     onClick : function(e)
5164     {
5165         Roo.log('PaginationItem on click ');
5166         if(this.preventDefault){
5167             e.preventDefault();
5168         }
5169         
5170         if(this.disabled){
5171             return;
5172         }
5173         
5174         this.fireEvent('click', this, e);
5175     }
5176    
5177 });
5178
5179  
5180
5181  /*
5182  * - LGPL
5183  *
5184  * slider
5185  * 
5186  */
5187
5188
5189 /**
5190  * @class Roo.bootstrap.Slider
5191  * @extends Roo.bootstrap.Component
5192  * Bootstrap Slider class
5193  *    
5194  * @constructor
5195  * Create a new Slider
5196  * @param {Object} config The config object
5197  */
5198
5199 Roo.bootstrap.Slider = function(config){
5200     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5201 };
5202
5203 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5204     
5205     getAutoCreate : function(){
5206         
5207         var cfg = {
5208             tag: 'div',
5209             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5210             cn: [
5211                 {
5212                     tag: 'a',
5213                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5214                 }
5215             ]
5216         };
5217         
5218         return cfg;
5219     }
5220    
5221 });
5222
5223  /*
5224  * Based on:
5225  * Ext JS Library 1.1.1
5226  * Copyright(c) 2006-2007, Ext JS, LLC.
5227  *
5228  * Originally Released Under LGPL - original licence link has changed is not relivant.
5229  *
5230  * Fork - LGPL
5231  * <script type="text/javascript">
5232  */
5233  
5234
5235 /**
5236  * @class Roo.grid.ColumnModel
5237  * @extends Roo.util.Observable
5238  * This is the default implementation of a ColumnModel used by the Grid. It defines
5239  * the columns in the grid.
5240  * <br>Usage:<br>
5241  <pre><code>
5242  var colModel = new Roo.grid.ColumnModel([
5243         {header: "Ticker", width: 60, sortable: true, locked: true},
5244         {header: "Company Name", width: 150, sortable: true},
5245         {header: "Market Cap.", width: 100, sortable: true},
5246         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5247         {header: "Employees", width: 100, sortable: true, resizable: false}
5248  ]);
5249  </code></pre>
5250  * <p>
5251  
5252  * The config options listed for this class are options which may appear in each
5253  * individual column definition.
5254  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5255  * @constructor
5256  * @param {Object} config An Array of column config objects. See this class's
5257  * config objects for details.
5258 */
5259 Roo.grid.ColumnModel = function(config){
5260         /**
5261      * The config passed into the constructor
5262      */
5263     this.config = config;
5264     this.lookup = {};
5265
5266     // if no id, create one
5267     // if the column does not have a dataIndex mapping,
5268     // map it to the order it is in the config
5269     for(var i = 0, len = config.length; i < len; i++){
5270         var c = config[i];
5271         if(typeof c.dataIndex == "undefined"){
5272             c.dataIndex = i;
5273         }
5274         if(typeof c.renderer == "string"){
5275             c.renderer = Roo.util.Format[c.renderer];
5276         }
5277         if(typeof c.id == "undefined"){
5278             c.id = Roo.id();
5279         }
5280         if(c.editor && c.editor.xtype){
5281             c.editor  = Roo.factory(c.editor, Roo.grid);
5282         }
5283         if(c.editor && c.editor.isFormField){
5284             c.editor = new Roo.grid.GridEditor(c.editor);
5285         }
5286         this.lookup[c.id] = c;
5287     }
5288
5289     /**
5290      * The width of columns which have no width specified (defaults to 100)
5291      * @type Number
5292      */
5293     this.defaultWidth = 100;
5294
5295     /**
5296      * Default sortable of columns which have no sortable specified (defaults to false)
5297      * @type Boolean
5298      */
5299     this.defaultSortable = false;
5300
5301     this.addEvents({
5302         /**
5303              * @event widthchange
5304              * Fires when the width of a column changes.
5305              * @param {ColumnModel} this
5306              * @param {Number} columnIndex The column index
5307              * @param {Number} newWidth The new width
5308              */
5309             "widthchange": true,
5310         /**
5311              * @event headerchange
5312              * Fires when the text of a header changes.
5313              * @param {ColumnModel} this
5314              * @param {Number} columnIndex The column index
5315              * @param {Number} newText The new header text
5316              */
5317             "headerchange": true,
5318         /**
5319              * @event hiddenchange
5320              * Fires when a column is hidden or "unhidden".
5321              * @param {ColumnModel} this
5322              * @param {Number} columnIndex The column index
5323              * @param {Boolean} hidden true if hidden, false otherwise
5324              */
5325             "hiddenchange": true,
5326             /**
5327          * @event columnmoved
5328          * Fires when a column is moved.
5329          * @param {ColumnModel} this
5330          * @param {Number} oldIndex
5331          * @param {Number} newIndex
5332          */
5333         "columnmoved" : true,
5334         /**
5335          * @event columlockchange
5336          * Fires when a column's locked state is changed
5337          * @param {ColumnModel} this
5338          * @param {Number} colIndex
5339          * @param {Boolean} locked true if locked
5340          */
5341         "columnlockchange" : true
5342     });
5343     Roo.grid.ColumnModel.superclass.constructor.call(this);
5344 };
5345 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5346     /**
5347      * @cfg {String} header The header text to display in the Grid view.
5348      */
5349     /**
5350      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5351      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5352      * specified, the column's index is used as an index into the Record's data Array.
5353      */
5354     /**
5355      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5356      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5357      */
5358     /**
5359      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5360      * Defaults to the value of the {@link #defaultSortable} property.
5361      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5362      */
5363     /**
5364      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5365      */
5366     /**
5367      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5368      */
5369     /**
5370      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5371      */
5372     /**
5373      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5374      */
5375     /**
5376      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5377      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5378      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5379      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5380      */
5381        /**
5382      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5383      */
5384     /**
5385      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5386      */
5387     /**
5388      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5389      */
5390     /**
5391      * @cfg {String} cursor (Optional)
5392      */
5393     /**
5394      * @cfg {String} tooltip (Optional)
5395      */
5396     /**
5397      * @cfg {Number} xs (Optional)
5398      */
5399     /**
5400      * @cfg {Number} sm (Optional)
5401      */
5402     /**
5403      * @cfg {Number} md (Optional)
5404      */
5405     /**
5406      * @cfg {Number} lg (Optional)
5407      */
5408     /**
5409      * Returns the id of the column at the specified index.
5410      * @param {Number} index The column index
5411      * @return {String} the id
5412      */
5413     getColumnId : function(index){
5414         return this.config[index].id;
5415     },
5416
5417     /**
5418      * Returns the column for a specified id.
5419      * @param {String} id The column id
5420      * @return {Object} the column
5421      */
5422     getColumnById : function(id){
5423         return this.lookup[id];
5424     },
5425
5426     
5427     /**
5428      * Returns the column for a specified dataIndex.
5429      * @param {String} dataIndex The column dataIndex
5430      * @return {Object|Boolean} the column or false if not found
5431      */
5432     getColumnByDataIndex: function(dataIndex){
5433         var index = this.findColumnIndex(dataIndex);
5434         return index > -1 ? this.config[index] : false;
5435     },
5436     
5437     /**
5438      * Returns the index for a specified column id.
5439      * @param {String} id The column id
5440      * @return {Number} the index, or -1 if not found
5441      */
5442     getIndexById : function(id){
5443         for(var i = 0, len = this.config.length; i < len; i++){
5444             if(this.config[i].id == id){
5445                 return i;
5446             }
5447         }
5448         return -1;
5449     },
5450     
5451     /**
5452      * Returns the index for a specified column dataIndex.
5453      * @param {String} dataIndex The column dataIndex
5454      * @return {Number} the index, or -1 if not found
5455      */
5456     
5457     findColumnIndex : function(dataIndex){
5458         for(var i = 0, len = this.config.length; i < len; i++){
5459             if(this.config[i].dataIndex == dataIndex){
5460                 return i;
5461             }
5462         }
5463         return -1;
5464     },
5465     
5466     
5467     moveColumn : function(oldIndex, newIndex){
5468         var c = this.config[oldIndex];
5469         this.config.splice(oldIndex, 1);
5470         this.config.splice(newIndex, 0, c);
5471         this.dataMap = null;
5472         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5473     },
5474
5475     isLocked : function(colIndex){
5476         return this.config[colIndex].locked === true;
5477     },
5478
5479     setLocked : function(colIndex, value, suppressEvent){
5480         if(this.isLocked(colIndex) == value){
5481             return;
5482         }
5483         this.config[colIndex].locked = value;
5484         if(!suppressEvent){
5485             this.fireEvent("columnlockchange", this, colIndex, value);
5486         }
5487     },
5488
5489     getTotalLockedWidth : function(){
5490         var totalWidth = 0;
5491         for(var i = 0; i < this.config.length; i++){
5492             if(this.isLocked(i) && !this.isHidden(i)){
5493                 this.totalWidth += this.getColumnWidth(i);
5494             }
5495         }
5496         return totalWidth;
5497     },
5498
5499     getLockedCount : function(){
5500         for(var i = 0, len = this.config.length; i < len; i++){
5501             if(!this.isLocked(i)){
5502                 return i;
5503             }
5504         }
5505         
5506         return this.config.length;
5507     },
5508
5509     /**
5510      * Returns the number of columns.
5511      * @return {Number}
5512      */
5513     getColumnCount : function(visibleOnly){
5514         if(visibleOnly === true){
5515             var c = 0;
5516             for(var i = 0, len = this.config.length; i < len; i++){
5517                 if(!this.isHidden(i)){
5518                     c++;
5519                 }
5520             }
5521             return c;
5522         }
5523         return this.config.length;
5524     },
5525
5526     /**
5527      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5528      * @param {Function} fn
5529      * @param {Object} scope (optional)
5530      * @return {Array} result
5531      */
5532     getColumnsBy : function(fn, scope){
5533         var r = [];
5534         for(var i = 0, len = this.config.length; i < len; i++){
5535             var c = this.config[i];
5536             if(fn.call(scope||this, c, i) === true){
5537                 r[r.length] = c;
5538             }
5539         }
5540         return r;
5541     },
5542
5543     /**
5544      * Returns true if the specified column is sortable.
5545      * @param {Number} col The column index
5546      * @return {Boolean}
5547      */
5548     isSortable : function(col){
5549         if(typeof this.config[col].sortable == "undefined"){
5550             return this.defaultSortable;
5551         }
5552         return this.config[col].sortable;
5553     },
5554
5555     /**
5556      * Returns the rendering (formatting) function defined for the column.
5557      * @param {Number} col The column index.
5558      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5559      */
5560     getRenderer : function(col){
5561         if(!this.config[col].renderer){
5562             return Roo.grid.ColumnModel.defaultRenderer;
5563         }
5564         return this.config[col].renderer;
5565     },
5566
5567     /**
5568      * Sets the rendering (formatting) function for a column.
5569      * @param {Number} col The column index
5570      * @param {Function} fn The function to use to process the cell's raw data
5571      * to return HTML markup for the grid view. The render function is called with
5572      * the following parameters:<ul>
5573      * <li>Data value.</li>
5574      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5575      * <li>css A CSS style string to apply to the table cell.</li>
5576      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5577      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5578      * <li>Row index</li>
5579      * <li>Column index</li>
5580      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5581      */
5582     setRenderer : function(col, fn){
5583         this.config[col].renderer = fn;
5584     },
5585
5586     /**
5587      * Returns the width for the specified column.
5588      * @param {Number} col The column index
5589      * @return {Number}
5590      */
5591     getColumnWidth : function(col){
5592         return this.config[col].width * 1 || this.defaultWidth;
5593     },
5594
5595     /**
5596      * Sets the width for a column.
5597      * @param {Number} col The column index
5598      * @param {Number} width The new width
5599      */
5600     setColumnWidth : function(col, width, suppressEvent){
5601         this.config[col].width = width;
5602         this.totalWidth = null;
5603         if(!suppressEvent){
5604              this.fireEvent("widthchange", this, col, width);
5605         }
5606     },
5607
5608     /**
5609      * Returns the total width of all columns.
5610      * @param {Boolean} includeHidden True to include hidden column widths
5611      * @return {Number}
5612      */
5613     getTotalWidth : function(includeHidden){
5614         if(!this.totalWidth){
5615             this.totalWidth = 0;
5616             for(var i = 0, len = this.config.length; i < len; i++){
5617                 if(includeHidden || !this.isHidden(i)){
5618                     this.totalWidth += this.getColumnWidth(i);
5619                 }
5620             }
5621         }
5622         return this.totalWidth;
5623     },
5624
5625     /**
5626      * Returns the header for the specified column.
5627      * @param {Number} col The column index
5628      * @return {String}
5629      */
5630     getColumnHeader : function(col){
5631         return this.config[col].header;
5632     },
5633
5634     /**
5635      * Sets the header for a column.
5636      * @param {Number} col The column index
5637      * @param {String} header The new header
5638      */
5639     setColumnHeader : function(col, header){
5640         this.config[col].header = header;
5641         this.fireEvent("headerchange", this, col, header);
5642     },
5643
5644     /**
5645      * Returns the tooltip for the specified column.
5646      * @param {Number} col The column index
5647      * @return {String}
5648      */
5649     getColumnTooltip : function(col){
5650             return this.config[col].tooltip;
5651     },
5652     /**
5653      * Sets the tooltip for a column.
5654      * @param {Number} col The column index
5655      * @param {String} tooltip The new tooltip
5656      */
5657     setColumnTooltip : function(col, tooltip){
5658             this.config[col].tooltip = tooltip;
5659     },
5660
5661     /**
5662      * Returns the dataIndex for the specified column.
5663      * @param {Number} col The column index
5664      * @return {Number}
5665      */
5666     getDataIndex : function(col){
5667         return this.config[col].dataIndex;
5668     },
5669
5670     /**
5671      * Sets the dataIndex for a column.
5672      * @param {Number} col The column index
5673      * @param {Number} dataIndex The new dataIndex
5674      */
5675     setDataIndex : function(col, dataIndex){
5676         this.config[col].dataIndex = dataIndex;
5677     },
5678
5679     
5680     
5681     /**
5682      * Returns true if the cell is editable.
5683      * @param {Number} colIndex The column index
5684      * @param {Number} rowIndex The row index - this is nto actually used..?
5685      * @return {Boolean}
5686      */
5687     isCellEditable : function(colIndex, rowIndex){
5688         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5689     },
5690
5691     /**
5692      * Returns the editor defined for the cell/column.
5693      * return false or null to disable editing.
5694      * @param {Number} colIndex The column index
5695      * @param {Number} rowIndex The row index
5696      * @return {Object}
5697      */
5698     getCellEditor : function(colIndex, rowIndex){
5699         return this.config[colIndex].editor;
5700     },
5701
5702     /**
5703      * Sets if a column is editable.
5704      * @param {Number} col The column index
5705      * @param {Boolean} editable True if the column is editable
5706      */
5707     setEditable : function(col, editable){
5708         this.config[col].editable = editable;
5709     },
5710
5711
5712     /**
5713      * Returns true if the column is hidden.
5714      * @param {Number} colIndex The column index
5715      * @return {Boolean}
5716      */
5717     isHidden : function(colIndex){
5718         return this.config[colIndex].hidden;
5719     },
5720
5721
5722     /**
5723      * Returns true if the column width cannot be changed
5724      */
5725     isFixed : function(colIndex){
5726         return this.config[colIndex].fixed;
5727     },
5728
5729     /**
5730      * Returns true if the column can be resized
5731      * @return {Boolean}
5732      */
5733     isResizable : function(colIndex){
5734         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5735     },
5736     /**
5737      * Sets if a column is hidden.
5738      * @param {Number} colIndex The column index
5739      * @param {Boolean} hidden True if the column is hidden
5740      */
5741     setHidden : function(colIndex, hidden){
5742         this.config[colIndex].hidden = hidden;
5743         this.totalWidth = null;
5744         this.fireEvent("hiddenchange", this, colIndex, hidden);
5745     },
5746
5747     /**
5748      * Sets the editor for a column.
5749      * @param {Number} col The column index
5750      * @param {Object} editor The editor object
5751      */
5752     setEditor : function(col, editor){
5753         this.config[col].editor = editor;
5754     }
5755 });
5756
5757 Roo.grid.ColumnModel.defaultRenderer = function(value)
5758 {
5759     if(typeof value == "object") {
5760         return value;
5761     }
5762         if(typeof value == "string" && value.length < 1){
5763             return "&#160;";
5764         }
5765     
5766         return String.format("{0}", value);
5767 };
5768
5769 // Alias for backwards compatibility
5770 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5771 /*
5772  * Based on:
5773  * Ext JS Library 1.1.1
5774  * Copyright(c) 2006-2007, Ext JS, LLC.
5775  *
5776  * Originally Released Under LGPL - original licence link has changed is not relivant.
5777  *
5778  * Fork - LGPL
5779  * <script type="text/javascript">
5780  */
5781  
5782 /**
5783  * @class Roo.LoadMask
5784  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5785  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5786  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5787  * element's UpdateManager load indicator and will be destroyed after the initial load.
5788  * @constructor
5789  * Create a new LoadMask
5790  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5791  * @param {Object} config The config object
5792  */
5793 Roo.LoadMask = function(el, config){
5794     this.el = Roo.get(el);
5795     Roo.apply(this, config);
5796     if(this.store){
5797         this.store.on('beforeload', this.onBeforeLoad, this);
5798         this.store.on('load', this.onLoad, this);
5799         this.store.on('loadexception', this.onLoadException, this);
5800         this.removeMask = false;
5801     }else{
5802         var um = this.el.getUpdateManager();
5803         um.showLoadIndicator = false; // disable the default indicator
5804         um.on('beforeupdate', this.onBeforeLoad, this);
5805         um.on('update', this.onLoad, this);
5806         um.on('failure', this.onLoad, this);
5807         this.removeMask = true;
5808     }
5809 };
5810
5811 Roo.LoadMask.prototype = {
5812     /**
5813      * @cfg {Boolean} removeMask
5814      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5815      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5816      */
5817     /**
5818      * @cfg {String} msg
5819      * The text to display in a centered loading message box (defaults to 'Loading...')
5820      */
5821     msg : 'Loading...',
5822     /**
5823      * @cfg {String} msgCls
5824      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5825      */
5826     msgCls : 'x-mask-loading',
5827
5828     /**
5829      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5830      * @type Boolean
5831      */
5832     disabled: false,
5833
5834     /**
5835      * Disables the mask to prevent it from being displayed
5836      */
5837     disable : function(){
5838        this.disabled = true;
5839     },
5840
5841     /**
5842      * Enables the mask so that it can be displayed
5843      */
5844     enable : function(){
5845         this.disabled = false;
5846     },
5847     
5848     onLoadException : function()
5849     {
5850         Roo.log(arguments);
5851         
5852         if (typeof(arguments[3]) != 'undefined') {
5853             Roo.MessageBox.alert("Error loading",arguments[3]);
5854         } 
5855         /*
5856         try {
5857             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5858                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5859             }   
5860         } catch(e) {
5861             
5862         }
5863         */
5864     
5865         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5866     },
5867     // private
5868     onLoad : function()
5869     {
5870         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5871     },
5872
5873     // private
5874     onBeforeLoad : function(){
5875         if(!this.disabled){
5876             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5877         }
5878     },
5879
5880     // private
5881     destroy : function(){
5882         if(this.store){
5883             this.store.un('beforeload', this.onBeforeLoad, this);
5884             this.store.un('load', this.onLoad, this);
5885             this.store.un('loadexception', this.onLoadException, this);
5886         }else{
5887             var um = this.el.getUpdateManager();
5888             um.un('beforeupdate', this.onBeforeLoad, this);
5889             um.un('update', this.onLoad, this);
5890             um.un('failure', this.onLoad, this);
5891         }
5892     }
5893 };/*
5894  * - LGPL
5895  *
5896  * table
5897  * 
5898  */
5899
5900 /**
5901  * @class Roo.bootstrap.Table
5902  * @extends Roo.bootstrap.Component
5903  * Bootstrap Table class
5904  * @cfg {String} cls table class
5905  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5906  * @cfg {String} bgcolor Specifies the background color for a table
5907  * @cfg {Number} border Specifies whether the table cells should have borders or not
5908  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5909  * @cfg {Number} cellspacing Specifies the space between cells
5910  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5911  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5912  * @cfg {String} sortable Specifies that the table should be sortable
5913  * @cfg {String} summary Specifies a summary of the content of a table
5914  * @cfg {Number} width Specifies the width of a table
5915  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5916  * 
5917  * @cfg {boolean} striped Should the rows be alternative striped
5918  * @cfg {boolean} bordered Add borders to the table
5919  * @cfg {boolean} hover Add hover highlighting
5920  * @cfg {boolean} condensed Format condensed
5921  * @cfg {boolean} responsive Format condensed
5922  * @cfg {Boolean} loadMask (true|false) default false
5923  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5924  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5925  * @cfg {Boolean} rowSelection (true|false) default false
5926  * @cfg {Boolean} cellSelection (true|false) default false
5927  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5928  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5929  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5930  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
5931  
5932  * 
5933  * @constructor
5934  * Create a new Table
5935  * @param {Object} config The config object
5936  */
5937
5938 Roo.bootstrap.Table = function(config){
5939     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5940     
5941   
5942     
5943     // BC...
5944     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5945     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5946     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5947     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5948     
5949     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5950     if (this.sm) {
5951         this.sm.grid = this;
5952         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5953         this.sm = this.selModel;
5954         this.sm.xmodule = this.xmodule || false;
5955     }
5956     
5957     if (this.cm && typeof(this.cm.config) == 'undefined') {
5958         this.colModel = new Roo.grid.ColumnModel(this.cm);
5959         this.cm = this.colModel;
5960         this.cm.xmodule = this.xmodule || false;
5961     }
5962     if (this.store) {
5963         this.store= Roo.factory(this.store, Roo.data);
5964         this.ds = this.store;
5965         this.ds.xmodule = this.xmodule || false;
5966          
5967     }
5968     if (this.footer && this.store) {
5969         this.footer.dataSource = this.ds;
5970         this.footer = Roo.factory(this.footer);
5971     }
5972     
5973     /** @private */
5974     this.addEvents({
5975         /**
5976          * @event cellclick
5977          * Fires when a cell is clicked
5978          * @param {Roo.bootstrap.Table} this
5979          * @param {Roo.Element} el
5980          * @param {Number} rowIndex
5981          * @param {Number} columnIndex
5982          * @param {Roo.EventObject} e
5983          */
5984         "cellclick" : true,
5985         /**
5986          * @event celldblclick
5987          * Fires when a cell is double clicked
5988          * @param {Roo.bootstrap.Table} this
5989          * @param {Roo.Element} el
5990          * @param {Number} rowIndex
5991          * @param {Number} columnIndex
5992          * @param {Roo.EventObject} e
5993          */
5994         "celldblclick" : true,
5995         /**
5996          * @event rowclick
5997          * Fires when a row is clicked
5998          * @param {Roo.bootstrap.Table} this
5999          * @param {Roo.Element} el
6000          * @param {Number} rowIndex
6001          * @param {Roo.EventObject} e
6002          */
6003         "rowclick" : true,
6004         /**
6005          * @event rowdblclick
6006          * Fires when a row is double clicked
6007          * @param {Roo.bootstrap.Table} this
6008          * @param {Roo.Element} el
6009          * @param {Number} rowIndex
6010          * @param {Roo.EventObject} e
6011          */
6012         "rowdblclick" : true,
6013         /**
6014          * @event mouseover
6015          * Fires when a mouseover occur
6016          * @param {Roo.bootstrap.Table} this
6017          * @param {Roo.Element} el
6018          * @param {Number} rowIndex
6019          * @param {Number} columnIndex
6020          * @param {Roo.EventObject} e
6021          */
6022         "mouseover" : true,
6023         /**
6024          * @event mouseout
6025          * Fires when a mouseout occur
6026          * @param {Roo.bootstrap.Table} this
6027          * @param {Roo.Element} el
6028          * @param {Number} rowIndex
6029          * @param {Number} columnIndex
6030          * @param {Roo.EventObject} e
6031          */
6032         "mouseout" : true,
6033         /**
6034          * @event rowclass
6035          * Fires when a row is rendered, so you can change add a style to it.
6036          * @param {Roo.bootstrap.Table} this
6037          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6038          */
6039         'rowclass' : true,
6040           /**
6041          * @event rowsrendered
6042          * Fires when all the  rows have been rendered
6043          * @param {Roo.bootstrap.Table} this
6044          */
6045         'rowsrendered' : true,
6046         /**
6047          * @event contextmenu
6048          * The raw contextmenu event for the entire grid.
6049          * @param {Roo.EventObject} e
6050          */
6051         "contextmenu" : true,
6052         /**
6053          * @event rowcontextmenu
6054          * Fires when a row is right clicked
6055          * @param {Roo.bootstrap.Table} this
6056          * @param {Number} rowIndex
6057          * @param {Roo.EventObject} e
6058          */
6059         "rowcontextmenu" : true,
6060         /**
6061          * @event cellcontextmenu
6062          * Fires when a cell is right clicked
6063          * @param {Roo.bootstrap.Table} this
6064          * @param {Number} rowIndex
6065          * @param {Number} cellIndex
6066          * @param {Roo.EventObject} e
6067          */
6068          "cellcontextmenu" : true,
6069          /**
6070          * @event headercontextmenu
6071          * Fires when a header is right clicked
6072          * @param {Roo.bootstrap.Table} this
6073          * @param {Number} columnIndex
6074          * @param {Roo.EventObject} e
6075          */
6076         "headercontextmenu" : true
6077     });
6078 };
6079
6080 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6081     
6082     cls: false,
6083     align: false,
6084     bgcolor: false,
6085     border: false,
6086     cellpadding: false,
6087     cellspacing: false,
6088     frame: false,
6089     rules: false,
6090     sortable: false,
6091     summary: false,
6092     width: false,
6093     striped : false,
6094     scrollBody : false,
6095     bordered: false,
6096     hover:  false,
6097     condensed : false,
6098     responsive : false,
6099     sm : false,
6100     cm : false,
6101     store : false,
6102     loadMask : false,
6103     footerShow : true,
6104     headerShow : true,
6105   
6106     rowSelection : false,
6107     cellSelection : false,
6108     layout : false,
6109     
6110     // Roo.Element - the tbody
6111     mainBody: false,
6112     // Roo.Element - thead element
6113     mainHead: false,
6114     
6115     container: false, // used by gridpanel...
6116     
6117     lazyLoad : false,
6118     
6119     CSS : Roo.util.CSS,
6120     
6121     auto_hide_footer : false,
6122     
6123     getAutoCreate : function()
6124     {
6125         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6126         
6127         cfg = {
6128             tag: 'table',
6129             cls : 'table',
6130             cn : []
6131         };
6132         if (this.scrollBody) {
6133             cfg.cls += ' table-body-fixed';
6134         }    
6135         if (this.striped) {
6136             cfg.cls += ' table-striped';
6137         }
6138         
6139         if (this.hover) {
6140             cfg.cls += ' table-hover';
6141         }
6142         if (this.bordered) {
6143             cfg.cls += ' table-bordered';
6144         }
6145         if (this.condensed) {
6146             cfg.cls += ' table-condensed';
6147         }
6148         if (this.responsive) {
6149             cfg.cls += ' table-responsive';
6150         }
6151         
6152         if (this.cls) {
6153             cfg.cls+=  ' ' +this.cls;
6154         }
6155         
6156         // this lot should be simplifed...
6157         var _t = this;
6158         var cp = [
6159             'align',
6160             'bgcolor',
6161             'border',
6162             'cellpadding',
6163             'cellspacing',
6164             'frame',
6165             'rules',
6166             'sortable',
6167             'summary',
6168             'width'
6169         ].forEach(function(k) {
6170             if (_t[k]) {
6171                 cfg[k] = _t[k];
6172             }
6173         });
6174         
6175         
6176         if (this.layout) {
6177             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6178         }
6179         
6180         if(this.store || this.cm){
6181             if(this.headerShow){
6182                 cfg.cn.push(this.renderHeader());
6183             }
6184             
6185             cfg.cn.push(this.renderBody());
6186             
6187             if(this.footerShow){
6188                 cfg.cn.push(this.renderFooter());
6189             }
6190             // where does this come from?
6191             //cfg.cls+=  ' TableGrid';
6192         }
6193         
6194         return { cn : [ cfg ] };
6195     },
6196     
6197     initEvents : function()
6198     {   
6199         if(!this.store || !this.cm){
6200             return;
6201         }
6202         if (this.selModel) {
6203             this.selModel.initEvents();
6204         }
6205         
6206         
6207         //Roo.log('initEvents with ds!!!!');
6208         
6209         this.mainBody = this.el.select('tbody', true).first();
6210         this.mainHead = this.el.select('thead', true).first();
6211         this.mainFoot = this.el.select('tfoot', true).first();
6212         
6213         
6214         
6215         var _this = this;
6216         
6217         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6218             e.on('click', _this.sort, _this);
6219         });
6220         
6221         this.mainBody.on("click", this.onClick, this);
6222         this.mainBody.on("dblclick", this.onDblClick, this);
6223         
6224         // why is this done????? = it breaks dialogs??
6225         //this.parent().el.setStyle('position', 'relative');
6226         
6227         
6228         if (this.footer) {
6229             this.footer.parentId = this.id;
6230             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6231             
6232             if(this.lazyLoad){
6233                 this.el.select('tfoot tr td').first().addClass('hide');
6234             }
6235         } 
6236         
6237         if(this.loadMask) {
6238             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6239         }
6240         
6241         this.store.on('load', this.onLoad, this);
6242         this.store.on('beforeload', this.onBeforeLoad, this);
6243         this.store.on('update', this.onUpdate, this);
6244         this.store.on('add', this.onAdd, this);
6245         this.store.on("clear", this.clear, this);
6246         
6247         this.el.on("contextmenu", this.onContextMenu, this);
6248         
6249         this.mainBody.on('scroll', this.onBodyScroll, this);
6250         
6251         this.cm.on("headerchange", this.onHeaderChange, this);
6252         
6253         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6254         
6255     },
6256     
6257     onContextMenu : function(e, t)
6258     {
6259         this.processEvent("contextmenu", e);
6260     },
6261     
6262     processEvent : function(name, e)
6263     {
6264         if (name != 'touchstart' ) {
6265             this.fireEvent(name, e);    
6266         }
6267         
6268         var t = e.getTarget();
6269         
6270         var cell = Roo.get(t);
6271         
6272         if(!cell){
6273             return;
6274         }
6275         
6276         if(cell.findParent('tfoot', false, true)){
6277             return;
6278         }
6279         
6280         if(cell.findParent('thead', false, true)){
6281             
6282             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6283                 cell = Roo.get(t).findParent('th', false, true);
6284                 if (!cell) {
6285                     Roo.log("failed to find th in thead?");
6286                     Roo.log(e.getTarget());
6287                     return;
6288                 }
6289             }
6290             
6291             var cellIndex = cell.dom.cellIndex;
6292             
6293             var ename = name == 'touchstart' ? 'click' : name;
6294             this.fireEvent("header" + ename, this, cellIndex, e);
6295             
6296             return;
6297         }
6298         
6299         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6300             cell = Roo.get(t).findParent('td', false, true);
6301             if (!cell) {
6302                 Roo.log("failed to find th in tbody?");
6303                 Roo.log(e.getTarget());
6304                 return;
6305             }
6306         }
6307         
6308         var row = cell.findParent('tr', false, true);
6309         var cellIndex = cell.dom.cellIndex;
6310         var rowIndex = row.dom.rowIndex - 1;
6311         
6312         if(row !== false){
6313             
6314             this.fireEvent("row" + name, this, rowIndex, e);
6315             
6316             if(cell !== false){
6317             
6318                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6319             }
6320         }
6321         
6322     },
6323     
6324     onMouseover : function(e, el)
6325     {
6326         var cell = Roo.get(el);
6327         
6328         if(!cell){
6329             return;
6330         }
6331         
6332         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6333             cell = cell.findParent('td', false, true);
6334         }
6335         
6336         var row = cell.findParent('tr', false, true);
6337         var cellIndex = cell.dom.cellIndex;
6338         var rowIndex = row.dom.rowIndex - 1; // start from 0
6339         
6340         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6341         
6342     },
6343     
6344     onMouseout : function(e, el)
6345     {
6346         var cell = Roo.get(el);
6347         
6348         if(!cell){
6349             return;
6350         }
6351         
6352         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6353             cell = cell.findParent('td', false, true);
6354         }
6355         
6356         var row = cell.findParent('tr', false, true);
6357         var cellIndex = cell.dom.cellIndex;
6358         var rowIndex = row.dom.rowIndex - 1; // start from 0
6359         
6360         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6361         
6362     },
6363     
6364     onClick : function(e, el)
6365     {
6366         var cell = Roo.get(el);
6367         
6368         if(!cell || (!this.cellSelection && !this.rowSelection)){
6369             return;
6370         }
6371         
6372         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6373             cell = cell.findParent('td', false, true);
6374         }
6375         
6376         if(!cell || typeof(cell) == 'undefined'){
6377             return;
6378         }
6379         
6380         var row = cell.findParent('tr', false, true);
6381         
6382         if(!row || typeof(row) == 'undefined'){
6383             return;
6384         }
6385         
6386         var cellIndex = cell.dom.cellIndex;
6387         var rowIndex = this.getRowIndex(row);
6388         
6389         // why??? - should these not be based on SelectionModel?
6390         if(this.cellSelection){
6391             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6392         }
6393         
6394         if(this.rowSelection){
6395             this.fireEvent('rowclick', this, row, rowIndex, e);
6396         }
6397         
6398         
6399     },
6400         
6401     onDblClick : function(e,el)
6402     {
6403         var cell = Roo.get(el);
6404         
6405         if(!cell || (!this.cellSelection && !this.rowSelection)){
6406             return;
6407         }
6408         
6409         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6410             cell = cell.findParent('td', false, true);
6411         }
6412         
6413         if(!cell || typeof(cell) == 'undefined'){
6414             return;
6415         }
6416         
6417         var row = cell.findParent('tr', false, true);
6418         
6419         if(!row || typeof(row) == 'undefined'){
6420             return;
6421         }
6422         
6423         var cellIndex = cell.dom.cellIndex;
6424         var rowIndex = this.getRowIndex(row);
6425         
6426         if(this.cellSelection){
6427             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6428         }
6429         
6430         if(this.rowSelection){
6431             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6432         }
6433     },
6434     
6435     sort : function(e,el)
6436     {
6437         var col = Roo.get(el);
6438         
6439         if(!col.hasClass('sortable')){
6440             return;
6441         }
6442         
6443         var sort = col.attr('sort');
6444         var dir = 'ASC';
6445         
6446         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6447             dir = 'DESC';
6448         }
6449         
6450         this.store.sortInfo = {field : sort, direction : dir};
6451         
6452         if (this.footer) {
6453             Roo.log("calling footer first");
6454             this.footer.onClick('first');
6455         } else {
6456         
6457             this.store.load({ params : { start : 0 } });
6458         }
6459     },
6460     
6461     renderHeader : function()
6462     {
6463         var header = {
6464             tag: 'thead',
6465             cn : []
6466         };
6467         
6468         var cm = this.cm;
6469         this.totalWidth = 0;
6470         
6471         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6472             
6473             var config = cm.config[i];
6474             
6475             var c = {
6476                 tag: 'th',
6477                 cls : 'x-hcol-' + i,
6478                 style : '',
6479                 html: cm.getColumnHeader(i)
6480             };
6481             
6482             var hh = '';
6483             
6484             if(typeof(config.sortable) != 'undefined' && config.sortable){
6485                 c.cls = 'sortable';
6486                 c.html = '<i class="glyphicon"></i>' + c.html;
6487             }
6488             
6489             if(typeof(config.lgHeader) != 'undefined'){
6490                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6491             }
6492             
6493             if(typeof(config.mdHeader) != 'undefined'){
6494                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6495             }
6496             
6497             if(typeof(config.smHeader) != 'undefined'){
6498                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6499             }
6500             
6501             if(typeof(config.xsHeader) != 'undefined'){
6502                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6503             }
6504             
6505             if(hh.length){
6506                 c.html = hh;
6507             }
6508             
6509             if(typeof(config.tooltip) != 'undefined'){
6510                 c.tooltip = config.tooltip;
6511             }
6512             
6513             if(typeof(config.colspan) != 'undefined'){
6514                 c.colspan = config.colspan;
6515             }
6516             
6517             if(typeof(config.hidden) != 'undefined' && config.hidden){
6518                 c.style += ' display:none;';
6519             }
6520             
6521             if(typeof(config.dataIndex) != 'undefined'){
6522                 c.sort = config.dataIndex;
6523             }
6524             
6525            
6526             
6527             if(typeof(config.align) != 'undefined' && config.align.length){
6528                 c.style += ' text-align:' + config.align + ';';
6529             }
6530             
6531             if(typeof(config.width) != 'undefined'){
6532                 c.style += ' width:' + config.width + 'px;';
6533                 this.totalWidth += config.width;
6534             } else {
6535                 this.totalWidth += 100; // assume minimum of 100 per column?
6536             }
6537             
6538             if(typeof(config.cls) != 'undefined'){
6539                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6540             }
6541             
6542             ['xs','sm','md','lg'].map(function(size){
6543                 
6544                 if(typeof(config[size]) == 'undefined'){
6545                     return;
6546                 }
6547                 
6548                 if (!config[size]) { // 0 = hidden
6549                     c.cls += ' hidden-' + size;
6550                     return;
6551                 }
6552                 
6553                 c.cls += ' col-' + size + '-' + config[size];
6554
6555             });
6556             
6557             header.cn.push(c)
6558         }
6559         
6560         return header;
6561     },
6562     
6563     renderBody : function()
6564     {
6565         var body = {
6566             tag: 'tbody',
6567             cn : [
6568                 {
6569                     tag: 'tr',
6570                     cn : [
6571                         {
6572                             tag : 'td',
6573                             colspan :  this.cm.getColumnCount()
6574                         }
6575                     ]
6576                 }
6577             ]
6578         };
6579         
6580         return body;
6581     },
6582     
6583     renderFooter : function()
6584     {
6585         var footer = {
6586             tag: 'tfoot',
6587             cn : [
6588                 {
6589                     tag: 'tr',
6590                     cn : [
6591                         {
6592                             tag : 'td',
6593                             colspan :  this.cm.getColumnCount()
6594                         }
6595                     ]
6596                 }
6597             ]
6598         };
6599         
6600         return footer;
6601     },
6602     
6603     
6604     
6605     onLoad : function()
6606     {
6607 //        Roo.log('ds onload');
6608         this.clear();
6609         
6610         var _this = this;
6611         var cm = this.cm;
6612         var ds = this.store;
6613         
6614         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6615             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6616             if (_this.store.sortInfo) {
6617                     
6618                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6619                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6620                 }
6621                 
6622                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6623                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6624                 }
6625             }
6626         });
6627         
6628         var tbody =  this.mainBody;
6629               
6630         if(ds.getCount() > 0){
6631             ds.data.each(function(d,rowIndex){
6632                 var row =  this.renderRow(cm, ds, rowIndex);
6633                 
6634                 tbody.createChild(row);
6635                 
6636                 var _this = this;
6637                 
6638                 if(row.cellObjects.length){
6639                     Roo.each(row.cellObjects, function(r){
6640                         _this.renderCellObject(r);
6641                     })
6642                 }
6643                 
6644             }, this);
6645         }
6646         
6647         var tfoot = this.el.select('tfoot', true).first();
6648         
6649         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6650             
6651             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6652             
6653             var total = this.ds.getTotalCount();
6654             
6655             if(this.footer.pageSize < total){
6656                 this.mainFoot.show();
6657             }
6658         }
6659         
6660         Roo.each(this.el.select('tbody td', true).elements, function(e){
6661             e.on('mouseover', _this.onMouseover, _this);
6662         });
6663         
6664         Roo.each(this.el.select('tbody td', true).elements, function(e){
6665             e.on('mouseout', _this.onMouseout, _this);
6666         });
6667         this.fireEvent('rowsrendered', this);
6668         
6669         this.autoSize();
6670     },
6671     
6672     
6673     onUpdate : function(ds,record)
6674     {
6675         this.refreshRow(record);
6676         this.autoSize();
6677     },
6678     
6679     onRemove : function(ds, record, index, isUpdate){
6680         if(isUpdate !== true){
6681             this.fireEvent("beforerowremoved", this, index, record);
6682         }
6683         var bt = this.mainBody.dom;
6684         
6685         var rows = this.el.select('tbody > tr', true).elements;
6686         
6687         if(typeof(rows[index]) != 'undefined'){
6688             bt.removeChild(rows[index].dom);
6689         }
6690         
6691 //        if(bt.rows[index]){
6692 //            bt.removeChild(bt.rows[index]);
6693 //        }
6694         
6695         if(isUpdate !== true){
6696             //this.stripeRows(index);
6697             //this.syncRowHeights(index, index);
6698             //this.layout();
6699             this.fireEvent("rowremoved", this, index, record);
6700         }
6701     },
6702     
6703     onAdd : function(ds, records, rowIndex)
6704     {
6705         //Roo.log('on Add called');
6706         // - note this does not handle multiple adding very well..
6707         var bt = this.mainBody.dom;
6708         for (var i =0 ; i < records.length;i++) {
6709             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6710             //Roo.log(records[i]);
6711             //Roo.log(this.store.getAt(rowIndex+i));
6712             this.insertRow(this.store, rowIndex + i, false);
6713             return;
6714         }
6715         
6716     },
6717     
6718     
6719     refreshRow : function(record){
6720         var ds = this.store, index;
6721         if(typeof record == 'number'){
6722             index = record;
6723             record = ds.getAt(index);
6724         }else{
6725             index = ds.indexOf(record);
6726         }
6727         this.insertRow(ds, index, true);
6728         this.autoSize();
6729         this.onRemove(ds, record, index+1, true);
6730         this.autoSize();
6731         //this.syncRowHeights(index, index);
6732         //this.layout();
6733         this.fireEvent("rowupdated", this, index, record);
6734     },
6735     
6736     insertRow : function(dm, rowIndex, isUpdate){
6737         
6738         if(!isUpdate){
6739             this.fireEvent("beforerowsinserted", this, rowIndex);
6740         }
6741             //var s = this.getScrollState();
6742         var row = this.renderRow(this.cm, this.store, rowIndex);
6743         // insert before rowIndex..
6744         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6745         
6746         var _this = this;
6747                 
6748         if(row.cellObjects.length){
6749             Roo.each(row.cellObjects, function(r){
6750                 _this.renderCellObject(r);
6751             })
6752         }
6753             
6754         if(!isUpdate){
6755             this.fireEvent("rowsinserted", this, rowIndex);
6756             //this.syncRowHeights(firstRow, lastRow);
6757             //this.stripeRows(firstRow);
6758             //this.layout();
6759         }
6760         
6761     },
6762     
6763     
6764     getRowDom : function(rowIndex)
6765     {
6766         var rows = this.el.select('tbody > tr', true).elements;
6767         
6768         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6769         
6770     },
6771     // returns the object tree for a tr..
6772   
6773     
6774     renderRow : function(cm, ds, rowIndex) 
6775     {
6776         var d = ds.getAt(rowIndex);
6777         
6778         var row = {
6779             tag : 'tr',
6780             cls : 'x-row-' + rowIndex,
6781             cn : []
6782         };
6783             
6784         var cellObjects = [];
6785         
6786         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6787             var config = cm.config[i];
6788             
6789             var renderer = cm.getRenderer(i);
6790             var value = '';
6791             var id = false;
6792             
6793             if(typeof(renderer) !== 'undefined'){
6794                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6795             }
6796             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6797             // and are rendered into the cells after the row is rendered - using the id for the element.
6798             
6799             if(typeof(value) === 'object'){
6800                 id = Roo.id();
6801                 cellObjects.push({
6802                     container : id,
6803                     cfg : value 
6804                 })
6805             }
6806             
6807             var rowcfg = {
6808                 record: d,
6809                 rowIndex : rowIndex,
6810                 colIndex : i,
6811                 rowClass : ''
6812             };
6813
6814             this.fireEvent('rowclass', this, rowcfg);
6815             
6816             var td = {
6817                 tag: 'td',
6818                 cls : rowcfg.rowClass + ' x-col-' + i,
6819                 style: '',
6820                 html: (typeof(value) === 'object') ? '' : value
6821             };
6822             
6823             if (id) {
6824                 td.id = id;
6825             }
6826             
6827             if(typeof(config.colspan) != 'undefined'){
6828                 td.colspan = config.colspan;
6829             }
6830             
6831             if(typeof(config.hidden) != 'undefined' && config.hidden){
6832                 td.style += ' display:none;';
6833             }
6834             
6835             if(typeof(config.align) != 'undefined' && config.align.length){
6836                 td.style += ' text-align:' + config.align + ';';
6837             }
6838             if(typeof(config.valign) != 'undefined' && config.valign.length){
6839                 td.style += ' vertical-align:' + config.valign + ';';
6840             }
6841             
6842             if(typeof(config.width) != 'undefined'){
6843                 td.style += ' width:' +  config.width + 'px;';
6844             }
6845             
6846             if(typeof(config.cursor) != 'undefined'){
6847                 td.style += ' cursor:' +  config.cursor + ';';
6848             }
6849             
6850             if(typeof(config.cls) != 'undefined'){
6851                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6852             }
6853             
6854             ['xs','sm','md','lg'].map(function(size){
6855                 
6856                 if(typeof(config[size]) == 'undefined'){
6857                     return;
6858                 }
6859                 
6860                 if (!config[size]) { // 0 = hidden
6861                     td.cls += ' hidden-' + size;
6862                     return;
6863                 }
6864                 
6865                 td.cls += ' col-' + size + '-' + config[size];
6866
6867             });
6868             
6869             row.cn.push(td);
6870            
6871         }
6872         
6873         row.cellObjects = cellObjects;
6874         
6875         return row;
6876           
6877     },
6878     
6879     
6880     
6881     onBeforeLoad : function()
6882     {
6883         
6884     },
6885      /**
6886      * Remove all rows
6887      */
6888     clear : function()
6889     {
6890         this.el.select('tbody', true).first().dom.innerHTML = '';
6891     },
6892     /**
6893      * Show or hide a row.
6894      * @param {Number} rowIndex to show or hide
6895      * @param {Boolean} state hide
6896      */
6897     setRowVisibility : function(rowIndex, state)
6898     {
6899         var bt = this.mainBody.dom;
6900         
6901         var rows = this.el.select('tbody > tr', true).elements;
6902         
6903         if(typeof(rows[rowIndex]) == 'undefined'){
6904             return;
6905         }
6906         rows[rowIndex].dom.style.display = state ? '' : 'none';
6907     },
6908     
6909     
6910     getSelectionModel : function(){
6911         if(!this.selModel){
6912             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6913         }
6914         return this.selModel;
6915     },
6916     /*
6917      * Render the Roo.bootstrap object from renderder
6918      */
6919     renderCellObject : function(r)
6920     {
6921         var _this = this;
6922         
6923         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6924         
6925         var t = r.cfg.render(r.container);
6926         
6927         if(r.cfg.cn){
6928             Roo.each(r.cfg.cn, function(c){
6929                 var child = {
6930                     container: t.getChildContainer(),
6931                     cfg: c
6932                 };
6933                 _this.renderCellObject(child);
6934             })
6935         }
6936     },
6937     
6938     getRowIndex : function(row)
6939     {
6940         var rowIndex = -1;
6941         
6942         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6943             if(el != row){
6944                 return;
6945             }
6946             
6947             rowIndex = index;
6948         });
6949         
6950         return rowIndex;
6951     },
6952      /**
6953      * Returns the grid's underlying element = used by panel.Grid
6954      * @return {Element} The element
6955      */
6956     getGridEl : function(){
6957         return this.el;
6958     },
6959      /**
6960      * Forces a resize - used by panel.Grid
6961      * @return {Element} The element
6962      */
6963     autoSize : function()
6964     {
6965         //var ctr = Roo.get(this.container.dom.parentElement);
6966         var ctr = Roo.get(this.el.dom);
6967         
6968         var thd = this.getGridEl().select('thead',true).first();
6969         var tbd = this.getGridEl().select('tbody', true).first();
6970         var tfd = this.getGridEl().select('tfoot', true).first();
6971         
6972         var cw = ctr.getWidth();
6973         
6974         if (tbd) {
6975             
6976             tbd.setSize(ctr.getWidth(),
6977                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6978             );
6979             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6980             cw -= barsize;
6981         }
6982         cw = Math.max(cw, this.totalWidth);
6983         this.getGridEl().select('tr',true).setWidth(cw);
6984         // resize 'expandable coloumn?
6985         
6986         return; // we doe not have a view in this design..
6987         
6988     },
6989     onBodyScroll: function()
6990     {
6991         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6992         if(this.mainHead){
6993             this.mainHead.setStyle({
6994                 'position' : 'relative',
6995                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6996             });
6997         }
6998         
6999         if(this.lazyLoad){
7000             
7001             var scrollHeight = this.mainBody.dom.scrollHeight;
7002             
7003             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7004             
7005             var height = this.mainBody.getHeight();
7006             
7007             if(scrollHeight - height == scrollTop) {
7008                 
7009                 var total = this.ds.getTotalCount();
7010                 
7011                 if(this.footer.cursor + this.footer.pageSize < total){
7012                     
7013                     this.footer.ds.load({
7014                         params : {
7015                             start : this.footer.cursor + this.footer.pageSize,
7016                             limit : this.footer.pageSize
7017                         },
7018                         add : true
7019                     });
7020                 }
7021             }
7022             
7023         }
7024     },
7025     
7026     onHeaderChange : function()
7027     {
7028         var header = this.renderHeader();
7029         var table = this.el.select('table', true).first();
7030         
7031         this.mainHead.remove();
7032         this.mainHead = table.createChild(header, this.mainBody, false);
7033     },
7034     
7035     onHiddenChange : function(colModel, colIndex, hidden)
7036     {
7037         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7038         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7039         
7040         this.CSS.updateRule(thSelector, "display", "");
7041         this.CSS.updateRule(tdSelector, "display", "");
7042         
7043         if(hidden){
7044             this.CSS.updateRule(thSelector, "display", "none");
7045             this.CSS.updateRule(tdSelector, "display", "none");
7046         }
7047         
7048         this.onHeaderChange();
7049         this.onLoad();
7050         
7051     }
7052     
7053 });
7054
7055  
7056
7057  /*
7058  * - LGPL
7059  *
7060  * table cell
7061  * 
7062  */
7063
7064 /**
7065  * @class Roo.bootstrap.TableCell
7066  * @extends Roo.bootstrap.Component
7067  * Bootstrap TableCell class
7068  * @cfg {String} html cell contain text
7069  * @cfg {String} cls cell class
7070  * @cfg {String} tag cell tag (td|th) default td
7071  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7072  * @cfg {String} align Aligns the content in a cell
7073  * @cfg {String} axis Categorizes cells
7074  * @cfg {String} bgcolor Specifies the background color of a cell
7075  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7076  * @cfg {Number} colspan Specifies the number of columns a cell should span
7077  * @cfg {String} headers Specifies one or more header cells a cell is related to
7078  * @cfg {Number} height Sets the height of a cell
7079  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7080  * @cfg {Number} rowspan Sets the number of rows a cell should span
7081  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7082  * @cfg {String} valign Vertical aligns the content in a cell
7083  * @cfg {Number} width Specifies the width of a cell
7084  * 
7085  * @constructor
7086  * Create a new TableCell
7087  * @param {Object} config The config object
7088  */
7089
7090 Roo.bootstrap.TableCell = function(config){
7091     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7092 };
7093
7094 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7095     
7096     html: false,
7097     cls: false,
7098     tag: false,
7099     abbr: false,
7100     align: false,
7101     axis: false,
7102     bgcolor: false,
7103     charoff: false,
7104     colspan: false,
7105     headers: false,
7106     height: false,
7107     nowrap: false,
7108     rowspan: false,
7109     scope: false,
7110     valign: false,
7111     width: false,
7112     
7113     
7114     getAutoCreate : function(){
7115         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7116         
7117         cfg = {
7118             tag: 'td'
7119         };
7120         
7121         if(this.tag){
7122             cfg.tag = this.tag;
7123         }
7124         
7125         if (this.html) {
7126             cfg.html=this.html
7127         }
7128         if (this.cls) {
7129             cfg.cls=this.cls
7130         }
7131         if (this.abbr) {
7132             cfg.abbr=this.abbr
7133         }
7134         if (this.align) {
7135             cfg.align=this.align
7136         }
7137         if (this.axis) {
7138             cfg.axis=this.axis
7139         }
7140         if (this.bgcolor) {
7141             cfg.bgcolor=this.bgcolor
7142         }
7143         if (this.charoff) {
7144             cfg.charoff=this.charoff
7145         }
7146         if (this.colspan) {
7147             cfg.colspan=this.colspan
7148         }
7149         if (this.headers) {
7150             cfg.headers=this.headers
7151         }
7152         if (this.height) {
7153             cfg.height=this.height
7154         }
7155         if (this.nowrap) {
7156             cfg.nowrap=this.nowrap
7157         }
7158         if (this.rowspan) {
7159             cfg.rowspan=this.rowspan
7160         }
7161         if (this.scope) {
7162             cfg.scope=this.scope
7163         }
7164         if (this.valign) {
7165             cfg.valign=this.valign
7166         }
7167         if (this.width) {
7168             cfg.width=this.width
7169         }
7170         
7171         
7172         return cfg;
7173     }
7174    
7175 });
7176
7177  
7178
7179  /*
7180  * - LGPL
7181  *
7182  * table row
7183  * 
7184  */
7185
7186 /**
7187  * @class Roo.bootstrap.TableRow
7188  * @extends Roo.bootstrap.Component
7189  * Bootstrap TableRow class
7190  * @cfg {String} cls row class
7191  * @cfg {String} align Aligns the content in a table row
7192  * @cfg {String} bgcolor Specifies a background color for a table row
7193  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7194  * @cfg {String} valign Vertical aligns the content in a table row
7195  * 
7196  * @constructor
7197  * Create a new TableRow
7198  * @param {Object} config The config object
7199  */
7200
7201 Roo.bootstrap.TableRow = function(config){
7202     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7203 };
7204
7205 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7206     
7207     cls: false,
7208     align: false,
7209     bgcolor: false,
7210     charoff: false,
7211     valign: false,
7212     
7213     getAutoCreate : function(){
7214         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7215         
7216         cfg = {
7217             tag: 'tr'
7218         };
7219             
7220         if(this.cls){
7221             cfg.cls = this.cls;
7222         }
7223         if(this.align){
7224             cfg.align = this.align;
7225         }
7226         if(this.bgcolor){
7227             cfg.bgcolor = this.bgcolor;
7228         }
7229         if(this.charoff){
7230             cfg.charoff = this.charoff;
7231         }
7232         if(this.valign){
7233             cfg.valign = this.valign;
7234         }
7235         
7236         return cfg;
7237     }
7238    
7239 });
7240
7241  
7242
7243  /*
7244  * - LGPL
7245  *
7246  * table body
7247  * 
7248  */
7249
7250 /**
7251  * @class Roo.bootstrap.TableBody
7252  * @extends Roo.bootstrap.Component
7253  * Bootstrap TableBody class
7254  * @cfg {String} cls element class
7255  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7256  * @cfg {String} align Aligns the content inside the element
7257  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7258  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7259  * 
7260  * @constructor
7261  * Create a new TableBody
7262  * @param {Object} config The config object
7263  */
7264
7265 Roo.bootstrap.TableBody = function(config){
7266     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7267 };
7268
7269 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7270     
7271     cls: false,
7272     tag: false,
7273     align: false,
7274     charoff: false,
7275     valign: false,
7276     
7277     getAutoCreate : function(){
7278         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7279         
7280         cfg = {
7281             tag: 'tbody'
7282         };
7283             
7284         if (this.cls) {
7285             cfg.cls=this.cls
7286         }
7287         if(this.tag){
7288             cfg.tag = this.tag;
7289         }
7290         
7291         if(this.align){
7292             cfg.align = this.align;
7293         }
7294         if(this.charoff){
7295             cfg.charoff = this.charoff;
7296         }
7297         if(this.valign){
7298             cfg.valign = this.valign;
7299         }
7300         
7301         return cfg;
7302     }
7303     
7304     
7305 //    initEvents : function()
7306 //    {
7307 //        
7308 //        if(!this.store){
7309 //            return;
7310 //        }
7311 //        
7312 //        this.store = Roo.factory(this.store, Roo.data);
7313 //        this.store.on('load', this.onLoad, this);
7314 //        
7315 //        this.store.load();
7316 //        
7317 //    },
7318 //    
7319 //    onLoad: function () 
7320 //    {   
7321 //        this.fireEvent('load', this);
7322 //    }
7323 //    
7324 //   
7325 });
7326
7327  
7328
7329  /*
7330  * Based on:
7331  * Ext JS Library 1.1.1
7332  * Copyright(c) 2006-2007, Ext JS, LLC.
7333  *
7334  * Originally Released Under LGPL - original licence link has changed is not relivant.
7335  *
7336  * Fork - LGPL
7337  * <script type="text/javascript">
7338  */
7339
7340 // as we use this in bootstrap.
7341 Roo.namespace('Roo.form');
7342  /**
7343  * @class Roo.form.Action
7344  * Internal Class used to handle form actions
7345  * @constructor
7346  * @param {Roo.form.BasicForm} el The form element or its id
7347  * @param {Object} config Configuration options
7348  */
7349
7350  
7351  
7352 // define the action interface
7353 Roo.form.Action = function(form, options){
7354     this.form = form;
7355     this.options = options || {};
7356 };
7357 /**
7358  * Client Validation Failed
7359  * @const 
7360  */
7361 Roo.form.Action.CLIENT_INVALID = 'client';
7362 /**
7363  * Server Validation Failed
7364  * @const 
7365  */
7366 Roo.form.Action.SERVER_INVALID = 'server';
7367  /**
7368  * Connect to Server Failed
7369  * @const 
7370  */
7371 Roo.form.Action.CONNECT_FAILURE = 'connect';
7372 /**
7373  * Reading Data from Server Failed
7374  * @const 
7375  */
7376 Roo.form.Action.LOAD_FAILURE = 'load';
7377
7378 Roo.form.Action.prototype = {
7379     type : 'default',
7380     failureType : undefined,
7381     response : undefined,
7382     result : undefined,
7383
7384     // interface method
7385     run : function(options){
7386
7387     },
7388
7389     // interface method
7390     success : function(response){
7391
7392     },
7393
7394     // interface method
7395     handleResponse : function(response){
7396
7397     },
7398
7399     // default connection failure
7400     failure : function(response){
7401         
7402         this.response = response;
7403         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7404         this.form.afterAction(this, false);
7405     },
7406
7407     processResponse : function(response){
7408         this.response = response;
7409         if(!response.responseText){
7410             return true;
7411         }
7412         this.result = this.handleResponse(response);
7413         return this.result;
7414     },
7415
7416     // utility functions used internally
7417     getUrl : function(appendParams){
7418         var url = this.options.url || this.form.url || this.form.el.dom.action;
7419         if(appendParams){
7420             var p = this.getParams();
7421             if(p){
7422                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7423             }
7424         }
7425         return url;
7426     },
7427
7428     getMethod : function(){
7429         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7430     },
7431
7432     getParams : function(){
7433         var bp = this.form.baseParams;
7434         var p = this.options.params;
7435         if(p){
7436             if(typeof p == "object"){
7437                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7438             }else if(typeof p == 'string' && bp){
7439                 p += '&' + Roo.urlEncode(bp);
7440             }
7441         }else if(bp){
7442             p = Roo.urlEncode(bp);
7443         }
7444         return p;
7445     },
7446
7447     createCallback : function(){
7448         return {
7449             success: this.success,
7450             failure: this.failure,
7451             scope: this,
7452             timeout: (this.form.timeout*1000),
7453             upload: this.form.fileUpload ? this.success : undefined
7454         };
7455     }
7456 };
7457
7458 Roo.form.Action.Submit = function(form, options){
7459     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7460 };
7461
7462 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7463     type : 'submit',
7464
7465     haveProgress : false,
7466     uploadComplete : false,
7467     
7468     // uploadProgress indicator.
7469     uploadProgress : function()
7470     {
7471         if (!this.form.progressUrl) {
7472             return;
7473         }
7474         
7475         if (!this.haveProgress) {
7476             Roo.MessageBox.progress("Uploading", "Uploading");
7477         }
7478         if (this.uploadComplete) {
7479            Roo.MessageBox.hide();
7480            return;
7481         }
7482         
7483         this.haveProgress = true;
7484    
7485         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7486         
7487         var c = new Roo.data.Connection();
7488         c.request({
7489             url : this.form.progressUrl,
7490             params: {
7491                 id : uid
7492             },
7493             method: 'GET',
7494             success : function(req){
7495                //console.log(data);
7496                 var rdata = false;
7497                 var edata;
7498                 try  {
7499                    rdata = Roo.decode(req.responseText)
7500                 } catch (e) {
7501                     Roo.log("Invalid data from server..");
7502                     Roo.log(edata);
7503                     return;
7504                 }
7505                 if (!rdata || !rdata.success) {
7506                     Roo.log(rdata);
7507                     Roo.MessageBox.alert(Roo.encode(rdata));
7508                     return;
7509                 }
7510                 var data = rdata.data;
7511                 
7512                 if (this.uploadComplete) {
7513                    Roo.MessageBox.hide();
7514                    return;
7515                 }
7516                    
7517                 if (data){
7518                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7519                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7520                     );
7521                 }
7522                 this.uploadProgress.defer(2000,this);
7523             },
7524        
7525             failure: function(data) {
7526                 Roo.log('progress url failed ');
7527                 Roo.log(data);
7528             },
7529             scope : this
7530         });
7531            
7532     },
7533     
7534     
7535     run : function()
7536     {
7537         // run get Values on the form, so it syncs any secondary forms.
7538         this.form.getValues();
7539         
7540         var o = this.options;
7541         var method = this.getMethod();
7542         var isPost = method == 'POST';
7543         if(o.clientValidation === false || this.form.isValid()){
7544             
7545             if (this.form.progressUrl) {
7546                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7547                     (new Date() * 1) + '' + Math.random());
7548                     
7549             } 
7550             
7551             
7552             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7553                 form:this.form.el.dom,
7554                 url:this.getUrl(!isPost),
7555                 method: method,
7556                 params:isPost ? this.getParams() : null,
7557                 isUpload: this.form.fileUpload
7558             }));
7559             
7560             this.uploadProgress();
7561
7562         }else if (o.clientValidation !== false){ // client validation failed
7563             this.failureType = Roo.form.Action.CLIENT_INVALID;
7564             this.form.afterAction(this, false);
7565         }
7566     },
7567
7568     success : function(response)
7569     {
7570         this.uploadComplete= true;
7571         if (this.haveProgress) {
7572             Roo.MessageBox.hide();
7573         }
7574         
7575         
7576         var result = this.processResponse(response);
7577         if(result === true || result.success){
7578             this.form.afterAction(this, true);
7579             return;
7580         }
7581         if(result.errors){
7582             this.form.markInvalid(result.errors);
7583             this.failureType = Roo.form.Action.SERVER_INVALID;
7584         }
7585         this.form.afterAction(this, false);
7586     },
7587     failure : function(response)
7588     {
7589         this.uploadComplete= true;
7590         if (this.haveProgress) {
7591             Roo.MessageBox.hide();
7592         }
7593         
7594         this.response = response;
7595         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7596         this.form.afterAction(this, false);
7597     },
7598     
7599     handleResponse : function(response){
7600         if(this.form.errorReader){
7601             var rs = this.form.errorReader.read(response);
7602             var errors = [];
7603             if(rs.records){
7604                 for(var i = 0, len = rs.records.length; i < len; i++) {
7605                     var r = rs.records[i];
7606                     errors[i] = r.data;
7607                 }
7608             }
7609             if(errors.length < 1){
7610                 errors = null;
7611             }
7612             return {
7613                 success : rs.success,
7614                 errors : errors
7615             };
7616         }
7617         var ret = false;
7618         try {
7619             ret = Roo.decode(response.responseText);
7620         } catch (e) {
7621             ret = {
7622                 success: false,
7623                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7624                 errors : []
7625             };
7626         }
7627         return ret;
7628         
7629     }
7630 });
7631
7632
7633 Roo.form.Action.Load = function(form, options){
7634     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7635     this.reader = this.form.reader;
7636 };
7637
7638 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7639     type : 'load',
7640
7641     run : function(){
7642         
7643         Roo.Ajax.request(Roo.apply(
7644                 this.createCallback(), {
7645                     method:this.getMethod(),
7646                     url:this.getUrl(false),
7647                     params:this.getParams()
7648         }));
7649     },
7650
7651     success : function(response){
7652         
7653         var result = this.processResponse(response);
7654         if(result === true || !result.success || !result.data){
7655             this.failureType = Roo.form.Action.LOAD_FAILURE;
7656             this.form.afterAction(this, false);
7657             return;
7658         }
7659         this.form.clearInvalid();
7660         this.form.setValues(result.data);
7661         this.form.afterAction(this, true);
7662     },
7663
7664     handleResponse : function(response){
7665         if(this.form.reader){
7666             var rs = this.form.reader.read(response);
7667             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7668             return {
7669                 success : rs.success,
7670                 data : data
7671             };
7672         }
7673         return Roo.decode(response.responseText);
7674     }
7675 });
7676
7677 Roo.form.Action.ACTION_TYPES = {
7678     'load' : Roo.form.Action.Load,
7679     'submit' : Roo.form.Action.Submit
7680 };/*
7681  * - LGPL
7682  *
7683  * form
7684  *
7685  */
7686
7687 /**
7688  * @class Roo.bootstrap.Form
7689  * @extends Roo.bootstrap.Component
7690  * Bootstrap Form class
7691  * @cfg {String} method  GET | POST (default POST)
7692  * @cfg {String} labelAlign top | left (default top)
7693  * @cfg {String} align left  | right - for navbars
7694  * @cfg {Boolean} loadMask load mask when submit (default true)
7695
7696  *
7697  * @constructor
7698  * Create a new Form
7699  * @param {Object} config The config object
7700  */
7701
7702
7703 Roo.bootstrap.Form = function(config){
7704     
7705     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7706     
7707     Roo.bootstrap.Form.popover.apply();
7708     
7709     this.addEvents({
7710         /**
7711          * @event clientvalidation
7712          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7713          * @param {Form} this
7714          * @param {Boolean} valid true if the form has passed client-side validation
7715          */
7716         clientvalidation: true,
7717         /**
7718          * @event beforeaction
7719          * Fires before any action is performed. Return false to cancel the action.
7720          * @param {Form} this
7721          * @param {Action} action The action to be performed
7722          */
7723         beforeaction: true,
7724         /**
7725          * @event actionfailed
7726          * Fires when an action fails.
7727          * @param {Form} this
7728          * @param {Action} action The action that failed
7729          */
7730         actionfailed : true,
7731         /**
7732          * @event actioncomplete
7733          * Fires when an action is completed.
7734          * @param {Form} this
7735          * @param {Action} action The action that completed
7736          */
7737         actioncomplete : true
7738     });
7739 };
7740
7741 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7742
7743      /**
7744      * @cfg {String} method
7745      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7746      */
7747     method : 'POST',
7748     /**
7749      * @cfg {String} url
7750      * The URL to use for form actions if one isn't supplied in the action options.
7751      */
7752     /**
7753      * @cfg {Boolean} fileUpload
7754      * Set to true if this form is a file upload.
7755      */
7756
7757     /**
7758      * @cfg {Object} baseParams
7759      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7760      */
7761
7762     /**
7763      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7764      */
7765     timeout: 30,
7766     /**
7767      * @cfg {Sting} align (left|right) for navbar forms
7768      */
7769     align : 'left',
7770
7771     // private
7772     activeAction : null,
7773
7774     /**
7775      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7776      * element by passing it or its id or mask the form itself by passing in true.
7777      * @type Mixed
7778      */
7779     waitMsgTarget : false,
7780
7781     loadMask : true,
7782     
7783     /**
7784      * @cfg {Boolean} errorMask (true|false) default false
7785      */
7786     errorMask : false,
7787     
7788     /**
7789      * @cfg {Number} maskOffset Default 100
7790      */
7791     maskOffset : 100,
7792     
7793     /**
7794      * @cfg {Boolean} maskBody
7795      */
7796     maskBody : false,
7797
7798     getAutoCreate : function(){
7799
7800         var cfg = {
7801             tag: 'form',
7802             method : this.method || 'POST',
7803             id : this.id || Roo.id(),
7804             cls : ''
7805         };
7806         if (this.parent().xtype.match(/^Nav/)) {
7807             cfg.cls = 'navbar-form navbar-' + this.align;
7808
7809         }
7810
7811         if (this.labelAlign == 'left' ) {
7812             cfg.cls += ' form-horizontal';
7813         }
7814
7815
7816         return cfg;
7817     },
7818     initEvents : function()
7819     {
7820         this.el.on('submit', this.onSubmit, this);
7821         // this was added as random key presses on the form where triggering form submit.
7822         this.el.on('keypress', function(e) {
7823             if (e.getCharCode() != 13) {
7824                 return true;
7825             }
7826             // we might need to allow it for textareas.. and some other items.
7827             // check e.getTarget().
7828
7829             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7830                 return true;
7831             }
7832
7833             Roo.log("keypress blocked");
7834
7835             e.preventDefault();
7836             return false;
7837         });
7838         
7839     },
7840     // private
7841     onSubmit : function(e){
7842         e.stopEvent();
7843     },
7844
7845      /**
7846      * Returns true if client-side validation on the form is successful.
7847      * @return Boolean
7848      */
7849     isValid : function(){
7850         var items = this.getItems();
7851         var valid = true;
7852         var target = false;
7853         
7854         items.each(function(f){
7855             
7856             if(f.validate()){
7857                 return;
7858             }
7859             
7860             Roo.log('invalid field: ' + f.name);
7861             
7862             valid = false;
7863
7864             if(!target && f.el.isVisible(true)){
7865                 target = f;
7866             }
7867            
7868         });
7869         
7870         if(this.errorMask && !valid){
7871             Roo.bootstrap.Form.popover.mask(this, target);
7872         }
7873         
7874         return valid;
7875     },
7876     
7877     /**
7878      * Returns true if any fields in this form have changed since their original load.
7879      * @return Boolean
7880      */
7881     isDirty : function(){
7882         var dirty = false;
7883         var items = this.getItems();
7884         items.each(function(f){
7885            if(f.isDirty()){
7886                dirty = true;
7887                return false;
7888            }
7889            return true;
7890         });
7891         return dirty;
7892     },
7893      /**
7894      * Performs a predefined action (submit or load) or custom actions you define on this form.
7895      * @param {String} actionName The name of the action type
7896      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7897      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7898      * accept other config options):
7899      * <pre>
7900 Property          Type             Description
7901 ----------------  ---------------  ----------------------------------------------------------------------------------
7902 url               String           The url for the action (defaults to the form's url)
7903 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7904 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7905 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7906                                    validate the form on the client (defaults to false)
7907      * </pre>
7908      * @return {BasicForm} this
7909      */
7910     doAction : function(action, options){
7911         if(typeof action == 'string'){
7912             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7913         }
7914         if(this.fireEvent('beforeaction', this, action) !== false){
7915             this.beforeAction(action);
7916             action.run.defer(100, action);
7917         }
7918         return this;
7919     },
7920
7921     // private
7922     beforeAction : function(action){
7923         var o = action.options;
7924         
7925         if(this.loadMask){
7926             
7927             if(this.maskBody){
7928                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7929             } else {
7930                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7931             }
7932         }
7933         // not really supported yet.. ??
7934
7935         //if(this.waitMsgTarget === true){
7936         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7937         //}else if(this.waitMsgTarget){
7938         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7939         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7940         //}else {
7941         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7942        // }
7943
7944     },
7945
7946     // private
7947     afterAction : function(action, success){
7948         this.activeAction = null;
7949         var o = action.options;
7950
7951         if(this.loadMask){
7952             
7953             if(this.maskBody){
7954                 Roo.get(document.body).unmask();
7955             } else {
7956                 this.el.unmask();
7957             }
7958         }
7959         
7960         //if(this.waitMsgTarget === true){
7961 //            this.el.unmask();
7962         //}else if(this.waitMsgTarget){
7963         //    this.waitMsgTarget.unmask();
7964         //}else{
7965         //    Roo.MessageBox.updateProgress(1);
7966         //    Roo.MessageBox.hide();
7967        // }
7968         //
7969         if(success){
7970             if(o.reset){
7971                 this.reset();
7972             }
7973             Roo.callback(o.success, o.scope, [this, action]);
7974             this.fireEvent('actioncomplete', this, action);
7975
7976         }else{
7977
7978             // failure condition..
7979             // we have a scenario where updates need confirming.
7980             // eg. if a locking scenario exists..
7981             // we look for { errors : { needs_confirm : true }} in the response.
7982             if (
7983                 (typeof(action.result) != 'undefined')  &&
7984                 (typeof(action.result.errors) != 'undefined')  &&
7985                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7986            ){
7987                 var _t = this;
7988                 Roo.log("not supported yet");
7989                  /*
7990
7991                 Roo.MessageBox.confirm(
7992                     "Change requires confirmation",
7993                     action.result.errorMsg,
7994                     function(r) {
7995                         if (r != 'yes') {
7996                             return;
7997                         }
7998                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7999                     }
8000
8001                 );
8002                 */
8003
8004
8005                 return;
8006             }
8007
8008             Roo.callback(o.failure, o.scope, [this, action]);
8009             // show an error message if no failed handler is set..
8010             if (!this.hasListener('actionfailed')) {
8011                 Roo.log("need to add dialog support");
8012                 /*
8013                 Roo.MessageBox.alert("Error",
8014                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8015                         action.result.errorMsg :
8016                         "Saving Failed, please check your entries or try again"
8017                 );
8018                 */
8019             }
8020
8021             this.fireEvent('actionfailed', this, action);
8022         }
8023
8024     },
8025     /**
8026      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8027      * @param {String} id The value to search for
8028      * @return Field
8029      */
8030     findField : function(id){
8031         var items = this.getItems();
8032         var field = items.get(id);
8033         if(!field){
8034              items.each(function(f){
8035                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8036                     field = f;
8037                     return false;
8038                 }
8039                 return true;
8040             });
8041         }
8042         return field || null;
8043     },
8044      /**
8045      * Mark fields in this form invalid in bulk.
8046      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8047      * @return {BasicForm} this
8048      */
8049     markInvalid : function(errors){
8050         if(errors instanceof Array){
8051             for(var i = 0, len = errors.length; i < len; i++){
8052                 var fieldError = errors[i];
8053                 var f = this.findField(fieldError.id);
8054                 if(f){
8055                     f.markInvalid(fieldError.msg);
8056                 }
8057             }
8058         }else{
8059             var field, id;
8060             for(id in errors){
8061                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8062                     field.markInvalid(errors[id]);
8063                 }
8064             }
8065         }
8066         //Roo.each(this.childForms || [], function (f) {
8067         //    f.markInvalid(errors);
8068         //});
8069
8070         return this;
8071     },
8072
8073     /**
8074      * Set values for fields in this form in bulk.
8075      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8076      * @return {BasicForm} this
8077      */
8078     setValues : function(values){
8079         if(values instanceof Array){ // array of objects
8080             for(var i = 0, len = values.length; i < len; i++){
8081                 var v = values[i];
8082                 var f = this.findField(v.id);
8083                 if(f){
8084                     f.setValue(v.value);
8085                     if(this.trackResetOnLoad){
8086                         f.originalValue = f.getValue();
8087                     }
8088                 }
8089             }
8090         }else{ // object hash
8091             var field, id;
8092             for(id in values){
8093                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8094
8095                     if (field.setFromData &&
8096                         field.valueField &&
8097                         field.displayField &&
8098                         // combos' with local stores can
8099                         // be queried via setValue()
8100                         // to set their value..
8101                         (field.store && !field.store.isLocal)
8102                         ) {
8103                         // it's a combo
8104                         var sd = { };
8105                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8106                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8107                         field.setFromData(sd);
8108
8109                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8110                         
8111                         field.setFromData(values);
8112                         
8113                     } else {
8114                         field.setValue(values[id]);
8115                     }
8116
8117
8118                     if(this.trackResetOnLoad){
8119                         field.originalValue = field.getValue();
8120                     }
8121                 }
8122             }
8123         }
8124
8125         //Roo.each(this.childForms || [], function (f) {
8126         //    f.setValues(values);
8127         //});
8128
8129         return this;
8130     },
8131
8132     /**
8133      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8134      * they are returned as an array.
8135      * @param {Boolean} asString
8136      * @return {Object}
8137      */
8138     getValues : function(asString){
8139         //if (this.childForms) {
8140             // copy values from the child forms
8141         //    Roo.each(this.childForms, function (f) {
8142         //        this.setValues(f.getValues());
8143         //    }, this);
8144         //}
8145
8146
8147
8148         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8149         if(asString === true){
8150             return fs;
8151         }
8152         return Roo.urlDecode(fs);
8153     },
8154
8155     /**
8156      * Returns the fields in this form as an object with key/value pairs.
8157      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8158      * @return {Object}
8159      */
8160     getFieldValues : function(with_hidden)
8161     {
8162         var items = this.getItems();
8163         var ret = {};
8164         items.each(function(f){
8165             
8166             if (!f.getName()) {
8167                 return;
8168             }
8169             
8170             var v = f.getValue();
8171             
8172             if (f.inputType =='radio') {
8173                 if (typeof(ret[f.getName()]) == 'undefined') {
8174                     ret[f.getName()] = ''; // empty..
8175                 }
8176
8177                 if (!f.el.dom.checked) {
8178                     return;
8179
8180                 }
8181                 v = f.el.dom.value;
8182
8183             }
8184             
8185             if(f.xtype == 'MoneyField'){
8186                 ret[f.currencyName] = f.getCurrency();
8187             }
8188
8189             // not sure if this supported any more..
8190             if ((typeof(v) == 'object') && f.getRawValue) {
8191                 v = f.getRawValue() ; // dates..
8192             }
8193             // combo boxes where name != hiddenName...
8194             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8195                 ret[f.name] = f.getRawValue();
8196             }
8197             ret[f.getName()] = v;
8198         });
8199
8200         return ret;
8201     },
8202
8203     /**
8204      * Clears all invalid messages in this form.
8205      * @return {BasicForm} this
8206      */
8207     clearInvalid : function(){
8208         var items = this.getItems();
8209
8210         items.each(function(f){
8211            f.clearInvalid();
8212         });
8213
8214         return this;
8215     },
8216
8217     /**
8218      * Resets this form.
8219      * @return {BasicForm} this
8220      */
8221     reset : function(){
8222         var items = this.getItems();
8223         items.each(function(f){
8224             f.reset();
8225         });
8226
8227         Roo.each(this.childForms || [], function (f) {
8228             f.reset();
8229         });
8230
8231
8232         return this;
8233     },
8234     
8235     getItems : function()
8236     {
8237         var r=new Roo.util.MixedCollection(false, function(o){
8238             return o.id || (o.id = Roo.id());
8239         });
8240         var iter = function(el) {
8241             if (el.inputEl) {
8242                 r.add(el);
8243             }
8244             if (!el.items) {
8245                 return;
8246             }
8247             Roo.each(el.items,function(e) {
8248                 iter(e);
8249             });
8250         };
8251
8252         iter(this);
8253         return r;
8254     },
8255     
8256     hideFields : function(items)
8257     {
8258         Roo.each(items, function(i){
8259             
8260             var f = this.findField(i);
8261             
8262             if(!f){
8263                 return;
8264             }
8265             
8266             if(f.xtype == 'DateField'){
8267                 f.setVisible(false);
8268                 return;
8269             }
8270             
8271             f.hide();
8272             
8273         }, this);
8274     },
8275     
8276     showFields : function(items)
8277     {
8278         Roo.each(items, function(i){
8279             
8280             var f = this.findField(i);
8281             
8282             if(!f){
8283                 return;
8284             }
8285             
8286             if(f.xtype == 'DateField'){
8287                 f.setVisible(true);
8288                 return;
8289             }
8290             
8291             f.show();
8292             
8293         }, this);
8294     }
8295
8296 });
8297
8298 Roo.apply(Roo.bootstrap.Form, {
8299     
8300     popover : {
8301         
8302         padding : 5,
8303         
8304         isApplied : false,
8305         
8306         isMasked : false,
8307         
8308         form : false,
8309         
8310         target : false,
8311         
8312         toolTip : false,
8313         
8314         intervalID : false,
8315         
8316         maskEl : false,
8317         
8318         apply : function()
8319         {
8320             if(this.isApplied){
8321                 return;
8322             }
8323             
8324             this.maskEl = {
8325                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8326                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8327                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8328                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8329             };
8330             
8331             this.maskEl.top.enableDisplayMode("block");
8332             this.maskEl.left.enableDisplayMode("block");
8333             this.maskEl.bottom.enableDisplayMode("block");
8334             this.maskEl.right.enableDisplayMode("block");
8335             
8336             this.toolTip = new Roo.bootstrap.Tooltip({
8337                 cls : 'roo-form-error-popover',
8338                 alignment : {
8339                     'left' : ['r-l', [-2,0], 'right'],
8340                     'right' : ['l-r', [2,0], 'left'],
8341                     'bottom' : ['tl-bl', [0,2], 'top'],
8342                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8343                 }
8344             });
8345             
8346             this.toolTip.render(Roo.get(document.body));
8347
8348             this.toolTip.el.enableDisplayMode("block");
8349             
8350             Roo.get(document.body).on('click', function(){
8351                 this.unmask();
8352             }, this);
8353             
8354             Roo.get(document.body).on('touchstart', function(){
8355                 this.unmask();
8356             }, this);
8357             
8358             this.isApplied = true
8359         },
8360         
8361         mask : function(form, target)
8362         {
8363             this.form = form;
8364             
8365             this.target = target;
8366             
8367             if(!this.form.errorMask || !target.el){
8368                 return;
8369             }
8370             
8371             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8372             
8373             Roo.log(scrollable);
8374             
8375             var ot = this.target.el.calcOffsetsTo(scrollable);
8376             
8377             var scrollTo = ot[1] - this.form.maskOffset;
8378             
8379             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8380             
8381             scrollable.scrollTo('top', scrollTo);
8382             
8383             var box = this.target.el.getBox();
8384             Roo.log(box);
8385             var zIndex = Roo.bootstrap.Modal.zIndex++;
8386
8387             
8388             this.maskEl.top.setStyle('position', 'absolute');
8389             this.maskEl.top.setStyle('z-index', zIndex);
8390             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8391             this.maskEl.top.setLeft(0);
8392             this.maskEl.top.setTop(0);
8393             this.maskEl.top.show();
8394             
8395             this.maskEl.left.setStyle('position', 'absolute');
8396             this.maskEl.left.setStyle('z-index', zIndex);
8397             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8398             this.maskEl.left.setLeft(0);
8399             this.maskEl.left.setTop(box.y - this.padding);
8400             this.maskEl.left.show();
8401
8402             this.maskEl.bottom.setStyle('position', 'absolute');
8403             this.maskEl.bottom.setStyle('z-index', zIndex);
8404             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8405             this.maskEl.bottom.setLeft(0);
8406             this.maskEl.bottom.setTop(box.bottom + this.padding);
8407             this.maskEl.bottom.show();
8408
8409             this.maskEl.right.setStyle('position', 'absolute');
8410             this.maskEl.right.setStyle('z-index', zIndex);
8411             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8412             this.maskEl.right.setLeft(box.right + this.padding);
8413             this.maskEl.right.setTop(box.y - this.padding);
8414             this.maskEl.right.show();
8415
8416             this.toolTip.bindEl = this.target.el;
8417
8418             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8419
8420             var tip = this.target.blankText;
8421
8422             if(this.target.getValue() !== '' ) {
8423                 
8424                 if (this.target.invalidText.length) {
8425                     tip = this.target.invalidText;
8426                 } else if (this.target.regexText.length){
8427                     tip = this.target.regexText;
8428                 }
8429             }
8430
8431             this.toolTip.show(tip);
8432
8433             this.intervalID = window.setInterval(function() {
8434                 Roo.bootstrap.Form.popover.unmask();
8435             }, 10000);
8436
8437             window.onwheel = function(){ return false;};
8438             
8439             (function(){ this.isMasked = true; }).defer(500, this);
8440             
8441         },
8442         
8443         unmask : function()
8444         {
8445             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8446                 return;
8447             }
8448             
8449             this.maskEl.top.setStyle('position', 'absolute');
8450             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8451             this.maskEl.top.hide();
8452
8453             this.maskEl.left.setStyle('position', 'absolute');
8454             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8455             this.maskEl.left.hide();
8456
8457             this.maskEl.bottom.setStyle('position', 'absolute');
8458             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8459             this.maskEl.bottom.hide();
8460
8461             this.maskEl.right.setStyle('position', 'absolute');
8462             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8463             this.maskEl.right.hide();
8464             
8465             this.toolTip.hide();
8466             
8467             this.toolTip.el.hide();
8468             
8469             window.onwheel = function(){ return true;};
8470             
8471             if(this.intervalID){
8472                 window.clearInterval(this.intervalID);
8473                 this.intervalID = false;
8474             }
8475             
8476             this.isMasked = false;
8477             
8478         }
8479         
8480     }
8481     
8482 });
8483
8484 /*
8485  * Based on:
8486  * Ext JS Library 1.1.1
8487  * Copyright(c) 2006-2007, Ext JS, LLC.
8488  *
8489  * Originally Released Under LGPL - original licence link has changed is not relivant.
8490  *
8491  * Fork - LGPL
8492  * <script type="text/javascript">
8493  */
8494 /**
8495  * @class Roo.form.VTypes
8496  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8497  * @singleton
8498  */
8499 Roo.form.VTypes = function(){
8500     // closure these in so they are only created once.
8501     var alpha = /^[a-zA-Z_]+$/;
8502     var alphanum = /^[a-zA-Z0-9_]+$/;
8503     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8504     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8505
8506     // All these messages and functions are configurable
8507     return {
8508         /**
8509          * The function used to validate email addresses
8510          * @param {String} value The email address
8511          */
8512         'email' : function(v){
8513             return email.test(v);
8514         },
8515         /**
8516          * The error text to display when the email validation function returns false
8517          * @type String
8518          */
8519         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8520         /**
8521          * The keystroke filter mask to be applied on email input
8522          * @type RegExp
8523          */
8524         'emailMask' : /[a-z0-9_\.\-@]/i,
8525
8526         /**
8527          * The function used to validate URLs
8528          * @param {String} value The URL
8529          */
8530         'url' : function(v){
8531             return url.test(v);
8532         },
8533         /**
8534          * The error text to display when the url validation function returns false
8535          * @type String
8536          */
8537         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8538         
8539         /**
8540          * The function used to validate alpha values
8541          * @param {String} value The value
8542          */
8543         'alpha' : function(v){
8544             return alpha.test(v);
8545         },
8546         /**
8547          * The error text to display when the alpha validation function returns false
8548          * @type String
8549          */
8550         'alphaText' : 'This field should only contain letters and _',
8551         /**
8552          * The keystroke filter mask to be applied on alpha input
8553          * @type RegExp
8554          */
8555         'alphaMask' : /[a-z_]/i,
8556
8557         /**
8558          * The function used to validate alphanumeric values
8559          * @param {String} value The value
8560          */
8561         'alphanum' : function(v){
8562             return alphanum.test(v);
8563         },
8564         /**
8565          * The error text to display when the alphanumeric validation function returns false
8566          * @type String
8567          */
8568         'alphanumText' : 'This field should only contain letters, numbers and _',
8569         /**
8570          * The keystroke filter mask to be applied on alphanumeric input
8571          * @type RegExp
8572          */
8573         'alphanumMask' : /[a-z0-9_]/i
8574     };
8575 }();/*
8576  * - LGPL
8577  *
8578  * Input
8579  * 
8580  */
8581
8582 /**
8583  * @class Roo.bootstrap.Input
8584  * @extends Roo.bootstrap.Component
8585  * Bootstrap Input class
8586  * @cfg {Boolean} disabled is it disabled
8587  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8588  * @cfg {String} name name of the input
8589  * @cfg {string} fieldLabel - the label associated
8590  * @cfg {string} placeholder - placeholder to put in text.
8591  * @cfg {string}  before - input group add on before
8592  * @cfg {string} after - input group add on after
8593  * @cfg {string} size - (lg|sm) or leave empty..
8594  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8595  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8596  * @cfg {Number} md colspan out of 12 for computer-sized screens
8597  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8598  * @cfg {string} value default value of the input
8599  * @cfg {Number} labelWidth set the width of label 
8600  * @cfg {Number} labellg set the width of label (1-12)
8601  * @cfg {Number} labelmd set the width of label (1-12)
8602  * @cfg {Number} labelsm set the width of label (1-12)
8603  * @cfg {Number} labelxs set the width of label (1-12)
8604  * @cfg {String} labelAlign (top|left)
8605  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8606  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8607  * @cfg {String} indicatorpos (left|right) default left
8608  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8609  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8610
8611  * @cfg {String} align (left|center|right) Default left
8612  * @cfg {Boolean} forceFeedback (true|false) Default false
8613  * 
8614  * @constructor
8615  * Create a new Input
8616  * @param {Object} config The config object
8617  */
8618
8619 Roo.bootstrap.Input = function(config){
8620     
8621     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8622     
8623     this.addEvents({
8624         /**
8625          * @event focus
8626          * Fires when this field receives input focus.
8627          * @param {Roo.form.Field} this
8628          */
8629         focus : true,
8630         /**
8631          * @event blur
8632          * Fires when this field loses input focus.
8633          * @param {Roo.form.Field} this
8634          */
8635         blur : true,
8636         /**
8637          * @event specialkey
8638          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8639          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8640          * @param {Roo.form.Field} this
8641          * @param {Roo.EventObject} e The event object
8642          */
8643         specialkey : true,
8644         /**
8645          * @event change
8646          * Fires just before the field blurs if the field value has changed.
8647          * @param {Roo.form.Field} this
8648          * @param {Mixed} newValue The new value
8649          * @param {Mixed} oldValue The original value
8650          */
8651         change : true,
8652         /**
8653          * @event invalid
8654          * Fires after the field has been marked as invalid.
8655          * @param {Roo.form.Field} this
8656          * @param {String} msg The validation message
8657          */
8658         invalid : true,
8659         /**
8660          * @event valid
8661          * Fires after the field has been validated with no errors.
8662          * @param {Roo.form.Field} this
8663          */
8664         valid : true,
8665          /**
8666          * @event keyup
8667          * Fires after the key up
8668          * @param {Roo.form.Field} this
8669          * @param {Roo.EventObject}  e The event Object
8670          */
8671         keyup : true
8672     });
8673 };
8674
8675 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8676      /**
8677      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8678       automatic validation (defaults to "keyup").
8679      */
8680     validationEvent : "keyup",
8681      /**
8682      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8683      */
8684     validateOnBlur : true,
8685     /**
8686      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8687      */
8688     validationDelay : 250,
8689      /**
8690      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8691      */
8692     focusClass : "x-form-focus",  // not needed???
8693     
8694        
8695     /**
8696      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8697      */
8698     invalidClass : "has-warning",
8699     
8700     /**
8701      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8702      */
8703     validClass : "has-success",
8704     
8705     /**
8706      * @cfg {Boolean} hasFeedback (true|false) default true
8707      */
8708     hasFeedback : true,
8709     
8710     /**
8711      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8712      */
8713     invalidFeedbackClass : "glyphicon-warning-sign",
8714     
8715     /**
8716      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8717      */
8718     validFeedbackClass : "glyphicon-ok",
8719     
8720     /**
8721      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8722      */
8723     selectOnFocus : false,
8724     
8725      /**
8726      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8727      */
8728     maskRe : null,
8729        /**
8730      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8731      */
8732     vtype : null,
8733     
8734       /**
8735      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8736      */
8737     disableKeyFilter : false,
8738     
8739        /**
8740      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8741      */
8742     disabled : false,
8743      /**
8744      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8745      */
8746     allowBlank : true,
8747     /**
8748      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8749      */
8750     blankText : "Please complete this mandatory field",
8751     
8752      /**
8753      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8754      */
8755     minLength : 0,
8756     /**
8757      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8758      */
8759     maxLength : Number.MAX_VALUE,
8760     /**
8761      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8762      */
8763     minLengthText : "The minimum length for this field is {0}",
8764     /**
8765      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8766      */
8767     maxLengthText : "The maximum length for this field is {0}",
8768   
8769     
8770     /**
8771      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8772      * If available, this function will be called only after the basic validators all return true, and will be passed the
8773      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8774      */
8775     validator : null,
8776     /**
8777      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8778      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8779      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8780      */
8781     regex : null,
8782     /**
8783      * @cfg {String} regexText -- Depricated - use Invalid Text
8784      */
8785     regexText : "",
8786     
8787     /**
8788      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8789      */
8790     invalidText : "",
8791     
8792     
8793     
8794     autocomplete: false,
8795     
8796     
8797     fieldLabel : '',
8798     inputType : 'text',
8799     
8800     name : false,
8801     placeholder: false,
8802     before : false,
8803     after : false,
8804     size : false,
8805     hasFocus : false,
8806     preventMark: false,
8807     isFormField : true,
8808     value : '',
8809     labelWidth : 2,
8810     labelAlign : false,
8811     readOnly : false,
8812     align : false,
8813     formatedValue : false,
8814     forceFeedback : false,
8815     
8816     indicatorpos : 'left',
8817     
8818     labellg : 0,
8819     labelmd : 0,
8820     labelsm : 0,
8821     labelxs : 0,
8822     
8823     capture : '',
8824     accept : '',
8825     
8826     parentLabelAlign : function()
8827     {
8828         var parent = this;
8829         while (parent.parent()) {
8830             parent = parent.parent();
8831             if (typeof(parent.labelAlign) !='undefined') {
8832                 return parent.labelAlign;
8833             }
8834         }
8835         return 'left';
8836         
8837     },
8838     
8839     getAutoCreate : function()
8840     {
8841         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8842         
8843         var id = Roo.id();
8844         
8845         var cfg = {};
8846         
8847         if(this.inputType != 'hidden'){
8848             cfg.cls = 'form-group' //input-group
8849         }
8850         
8851         var input =  {
8852             tag: 'input',
8853             id : id,
8854             type : this.inputType,
8855             value : this.value,
8856             cls : 'form-control',
8857             placeholder : this.placeholder || '',
8858             autocomplete : this.autocomplete || 'new-password'
8859         };
8860         
8861         if(this.capture.length){
8862             input.capture = this.capture;
8863         }
8864         
8865         if(this.accept.length){
8866             input.accept = this.accept + "/*";
8867         }
8868         
8869         if(this.align){
8870             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8871         }
8872         
8873         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8874             input.maxLength = this.maxLength;
8875         }
8876         
8877         if (this.disabled) {
8878             input.disabled=true;
8879         }
8880         
8881         if (this.readOnly) {
8882             input.readonly=true;
8883         }
8884         
8885         if (this.name) {
8886             input.name = this.name;
8887         }
8888         
8889         if (this.size) {
8890             input.cls += ' input-' + this.size;
8891         }
8892         
8893         var settings=this;
8894         ['xs','sm','md','lg'].map(function(size){
8895             if (settings[size]) {
8896                 cfg.cls += ' col-' + size + '-' + settings[size];
8897             }
8898         });
8899         
8900         var inputblock = input;
8901         
8902         var feedback = {
8903             tag: 'span',
8904             cls: 'glyphicon form-control-feedback'
8905         };
8906             
8907         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8908             
8909             inputblock = {
8910                 cls : 'has-feedback',
8911                 cn :  [
8912                     input,
8913                     feedback
8914                 ] 
8915             };  
8916         }
8917         
8918         if (this.before || this.after) {
8919             
8920             inputblock = {
8921                 cls : 'input-group',
8922                 cn :  [] 
8923             };
8924             
8925             if (this.before && typeof(this.before) == 'string') {
8926                 
8927                 inputblock.cn.push({
8928                     tag :'span',
8929                     cls : 'roo-input-before input-group-addon',
8930                     html : this.before
8931                 });
8932             }
8933             if (this.before && typeof(this.before) == 'object') {
8934                 this.before = Roo.factory(this.before);
8935                 
8936                 inputblock.cn.push({
8937                     tag :'span',
8938                     cls : 'roo-input-before input-group-' +
8939                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8940                 });
8941             }
8942             
8943             inputblock.cn.push(input);
8944             
8945             if (this.after && typeof(this.after) == 'string') {
8946                 inputblock.cn.push({
8947                     tag :'span',
8948                     cls : 'roo-input-after input-group-addon',
8949                     html : this.after
8950                 });
8951             }
8952             if (this.after && typeof(this.after) == 'object') {
8953                 this.after = Roo.factory(this.after);
8954                 
8955                 inputblock.cn.push({
8956                     tag :'span',
8957                     cls : 'roo-input-after input-group-' +
8958                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8959                 });
8960             }
8961             
8962             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8963                 inputblock.cls += ' has-feedback';
8964                 inputblock.cn.push(feedback);
8965             }
8966         };
8967         
8968         if (align ==='left' && this.fieldLabel.length) {
8969             
8970             cfg.cls += ' roo-form-group-label-left';
8971             
8972             cfg.cn = [
8973                 {
8974                     tag : 'i',
8975                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8976                     tooltip : 'This field is required'
8977                 },
8978                 {
8979                     tag: 'label',
8980                     'for' :  id,
8981                     cls : 'control-label',
8982                     html : this.fieldLabel
8983
8984                 },
8985                 {
8986                     cls : "", 
8987                     cn: [
8988                         inputblock
8989                     ]
8990                 }
8991             ];
8992             
8993             var labelCfg = cfg.cn[1];
8994             var contentCfg = cfg.cn[2];
8995             
8996             if(this.indicatorpos == 'right'){
8997                 cfg.cn = [
8998                     {
8999                         tag: 'label',
9000                         'for' :  id,
9001                         cls : 'control-label',
9002                         cn : [
9003                             {
9004                                 tag : 'span',
9005                                 html : this.fieldLabel
9006                             },
9007                             {
9008                                 tag : 'i',
9009                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9010                                 tooltip : 'This field is required'
9011                             }
9012                         ]
9013                     },
9014                     {
9015                         cls : "",
9016                         cn: [
9017                             inputblock
9018                         ]
9019                     }
9020
9021                 ];
9022                 
9023                 labelCfg = cfg.cn[0];
9024                 contentCfg = cfg.cn[1];
9025             
9026             }
9027             
9028             if(this.labelWidth > 12){
9029                 labelCfg.style = "width: " + this.labelWidth + 'px';
9030             }
9031             
9032             if(this.labelWidth < 13 && this.labelmd == 0){
9033                 this.labelmd = this.labelWidth;
9034             }
9035             
9036             if(this.labellg > 0){
9037                 labelCfg.cls += ' col-lg-' + this.labellg;
9038                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9039             }
9040             
9041             if(this.labelmd > 0){
9042                 labelCfg.cls += ' col-md-' + this.labelmd;
9043                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9044             }
9045             
9046             if(this.labelsm > 0){
9047                 labelCfg.cls += ' col-sm-' + this.labelsm;
9048                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9049             }
9050             
9051             if(this.labelxs > 0){
9052                 labelCfg.cls += ' col-xs-' + this.labelxs;
9053                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9054             }
9055             
9056             
9057         } else if ( this.fieldLabel.length) {
9058                 
9059             cfg.cn = [
9060                 {
9061                     tag : 'i',
9062                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9063                     tooltip : 'This field is required'
9064                 },
9065                 {
9066                     tag: 'label',
9067                    //cls : 'input-group-addon',
9068                     html : this.fieldLabel
9069
9070                 },
9071
9072                inputblock
9073
9074            ];
9075            
9076            if(this.indicatorpos == 'right'){
9077                 
9078                 cfg.cn = [
9079                     {
9080                         tag: 'label',
9081                        //cls : 'input-group-addon',
9082                         html : this.fieldLabel
9083
9084                     },
9085                     {
9086                         tag : 'i',
9087                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9088                         tooltip : 'This field is required'
9089                     },
9090
9091                    inputblock
9092
9093                ];
9094
9095             }
9096
9097         } else {
9098             
9099             cfg.cn = [
9100
9101                     inputblock
9102
9103             ];
9104                 
9105                 
9106         };
9107         
9108         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9109            cfg.cls += ' navbar-form';
9110         }
9111         
9112         if (this.parentType === 'NavGroup') {
9113            cfg.cls += ' navbar-form';
9114            cfg.tag = 'li';
9115         }
9116         
9117         return cfg;
9118         
9119     },
9120     /**
9121      * return the real input element.
9122      */
9123     inputEl: function ()
9124     {
9125         return this.el.select('input.form-control',true).first();
9126     },
9127     
9128     tooltipEl : function()
9129     {
9130         return this.inputEl();
9131     },
9132     
9133     indicatorEl : function()
9134     {
9135         var indicator = this.el.select('i.roo-required-indicator',true).first();
9136         
9137         if(!indicator){
9138             return false;
9139         }
9140         
9141         return indicator;
9142         
9143     },
9144     
9145     setDisabled : function(v)
9146     {
9147         var i  = this.inputEl().dom;
9148         if (!v) {
9149             i.removeAttribute('disabled');
9150             return;
9151             
9152         }
9153         i.setAttribute('disabled','true');
9154     },
9155     initEvents : function()
9156     {
9157           
9158         this.inputEl().on("keydown" , this.fireKey,  this);
9159         this.inputEl().on("focus", this.onFocus,  this);
9160         this.inputEl().on("blur", this.onBlur,  this);
9161         
9162         this.inputEl().relayEvent('keyup', this);
9163         
9164         this.indicator = this.indicatorEl();
9165         
9166         if(this.indicator){
9167             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9168         }
9169  
9170         // reference to original value for reset
9171         this.originalValue = this.getValue();
9172         //Roo.form.TextField.superclass.initEvents.call(this);
9173         if(this.validationEvent == 'keyup'){
9174             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9175             this.inputEl().on('keyup', this.filterValidation, this);
9176         }
9177         else if(this.validationEvent !== false){
9178             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9179         }
9180         
9181         if(this.selectOnFocus){
9182             this.on("focus", this.preFocus, this);
9183             
9184         }
9185         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9186             this.inputEl().on("keypress", this.filterKeys, this);
9187         } else {
9188             this.inputEl().relayEvent('keypress', this);
9189         }
9190        /* if(this.grow){
9191             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9192             this.el.on("click", this.autoSize,  this);
9193         }
9194         */
9195         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9196             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9197         }
9198         
9199         if (typeof(this.before) == 'object') {
9200             this.before.render(this.el.select('.roo-input-before',true).first());
9201         }
9202         if (typeof(this.after) == 'object') {
9203             this.after.render(this.el.select('.roo-input-after',true).first());
9204         }
9205         
9206         this.inputEl().on('change', this.onChange, this);
9207         
9208     },
9209     filterValidation : function(e){
9210         if(!e.isNavKeyPress()){
9211             this.validationTask.delay(this.validationDelay);
9212         }
9213     },
9214      /**
9215      * Validates the field value
9216      * @return {Boolean} True if the value is valid, else false
9217      */
9218     validate : function(){
9219         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9220         if(this.disabled || this.validateValue(this.getRawValue())){
9221             this.markValid();
9222             return true;
9223         }
9224         
9225         this.markInvalid();
9226         return false;
9227     },
9228     
9229     
9230     /**
9231      * Validates a value according to the field's validation rules and marks the field as invalid
9232      * if the validation fails
9233      * @param {Mixed} value The value to validate
9234      * @return {Boolean} True if the value is valid, else false
9235      */
9236     validateValue : function(value)
9237     {
9238         if(this.getVisibilityEl().hasClass('hidden')){
9239             return true;
9240         }
9241         
9242         if(value.length < 1)  { // if it's blank
9243             if(this.allowBlank){
9244                 return true;
9245             }
9246             return false;
9247         }
9248         
9249         if(value.length < this.minLength){
9250             return false;
9251         }
9252         if(value.length > this.maxLength){
9253             return false;
9254         }
9255         if(this.vtype){
9256             var vt = Roo.form.VTypes;
9257             if(!vt[this.vtype](value, this)){
9258                 return false;
9259             }
9260         }
9261         if(typeof this.validator == "function"){
9262             var msg = this.validator(value);
9263             if(msg !== true){
9264                 return false;
9265             }
9266             if (typeof(msg) == 'string') {
9267                 this.invalidText = msg;
9268             }
9269         }
9270         
9271         if(this.regex && !this.regex.test(value)){
9272             return false;
9273         }
9274         
9275         return true;
9276     },
9277     
9278      // private
9279     fireKey : function(e){
9280         //Roo.log('field ' + e.getKey());
9281         if(e.isNavKeyPress()){
9282             this.fireEvent("specialkey", this, e);
9283         }
9284     },
9285     focus : function (selectText){
9286         if(this.rendered){
9287             this.inputEl().focus();
9288             if(selectText === true){
9289                 this.inputEl().dom.select();
9290             }
9291         }
9292         return this;
9293     } ,
9294     
9295     onFocus : function(){
9296         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9297            // this.el.addClass(this.focusClass);
9298         }
9299         if(!this.hasFocus){
9300             this.hasFocus = true;
9301             this.startValue = this.getValue();
9302             this.fireEvent("focus", this);
9303         }
9304     },
9305     
9306     beforeBlur : Roo.emptyFn,
9307
9308     
9309     // private
9310     onBlur : function(){
9311         this.beforeBlur();
9312         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9313             //this.el.removeClass(this.focusClass);
9314         }
9315         this.hasFocus = false;
9316         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9317             this.validate();
9318         }
9319         var v = this.getValue();
9320         if(String(v) !== String(this.startValue)){
9321             this.fireEvent('change', this, v, this.startValue);
9322         }
9323         this.fireEvent("blur", this);
9324     },
9325     
9326     onChange : function(e)
9327     {
9328         var v = this.getValue();
9329         if(String(v) !== String(this.startValue)){
9330             this.fireEvent('change', this, v, this.startValue);
9331         }
9332         
9333     },
9334     
9335     /**
9336      * Resets the current field value to the originally loaded value and clears any validation messages
9337      */
9338     reset : function(){
9339         this.setValue(this.originalValue);
9340         this.validate();
9341     },
9342      /**
9343      * Returns the name of the field
9344      * @return {Mixed} name The name field
9345      */
9346     getName: function(){
9347         return this.name;
9348     },
9349      /**
9350      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9351      * @return {Mixed} value The field value
9352      */
9353     getValue : function(){
9354         
9355         var v = this.inputEl().getValue();
9356         
9357         return v;
9358     },
9359     /**
9360      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9361      * @return {Mixed} value The field value
9362      */
9363     getRawValue : function(){
9364         var v = this.inputEl().getValue();
9365         
9366         return v;
9367     },
9368     
9369     /**
9370      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9371      * @param {Mixed} value The value to set
9372      */
9373     setRawValue : function(v){
9374         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9375     },
9376     
9377     selectText : function(start, end){
9378         var v = this.getRawValue();
9379         if(v.length > 0){
9380             start = start === undefined ? 0 : start;
9381             end = end === undefined ? v.length : end;
9382             var d = this.inputEl().dom;
9383             if(d.setSelectionRange){
9384                 d.setSelectionRange(start, end);
9385             }else if(d.createTextRange){
9386                 var range = d.createTextRange();
9387                 range.moveStart("character", start);
9388                 range.moveEnd("character", v.length-end);
9389                 range.select();
9390             }
9391         }
9392     },
9393     
9394     /**
9395      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9396      * @param {Mixed} value The value to set
9397      */
9398     setValue : function(v){
9399         this.value = v;
9400         if(this.rendered){
9401             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9402             this.validate();
9403         }
9404     },
9405     
9406     /*
9407     processValue : function(value){
9408         if(this.stripCharsRe){
9409             var newValue = value.replace(this.stripCharsRe, '');
9410             if(newValue !== value){
9411                 this.setRawValue(newValue);
9412                 return newValue;
9413             }
9414         }
9415         return value;
9416     },
9417   */
9418     preFocus : function(){
9419         
9420         if(this.selectOnFocus){
9421             this.inputEl().dom.select();
9422         }
9423     },
9424     filterKeys : function(e){
9425         var k = e.getKey();
9426         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9427             return;
9428         }
9429         var c = e.getCharCode(), cc = String.fromCharCode(c);
9430         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9431             return;
9432         }
9433         if(!this.maskRe.test(cc)){
9434             e.stopEvent();
9435         }
9436     },
9437      /**
9438      * Clear any invalid styles/messages for this field
9439      */
9440     clearInvalid : function(){
9441         
9442         if(!this.el || this.preventMark){ // not rendered
9443             return;
9444         }
9445         
9446      
9447         this.el.removeClass(this.invalidClass);
9448         
9449         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9450             
9451             var feedback = this.el.select('.form-control-feedback', true).first();
9452             
9453             if(feedback){
9454                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9455             }
9456             
9457         }
9458         
9459         if(this.indicator){
9460             this.indicator.removeClass('visible');
9461             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9462         }
9463         
9464         this.fireEvent('valid', this);
9465     },
9466     
9467      /**
9468      * Mark this field as valid
9469      */
9470     markValid : function()
9471     {
9472         if(!this.el  || this.preventMark){ // not rendered...
9473             return;
9474         }
9475         
9476         this.el.removeClass([this.invalidClass, this.validClass]);
9477         
9478         var feedback = this.el.select('.form-control-feedback', true).first();
9479             
9480         if(feedback){
9481             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9482         }
9483         
9484         if(this.indicator){
9485             this.indicator.removeClass('visible');
9486             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9487         }
9488         
9489         if(this.disabled){
9490             return;
9491         }
9492         
9493         if(this.allowBlank && !this.getRawValue().length){
9494             return;
9495         }
9496         
9497         this.el.addClass(this.validClass);
9498         
9499         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9500             
9501             var feedback = this.el.select('.form-control-feedback', true).first();
9502             
9503             if(feedback){
9504                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9505                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9506             }
9507             
9508         }
9509         
9510         this.fireEvent('valid', this);
9511     },
9512     
9513      /**
9514      * Mark this field as invalid
9515      * @param {String} msg The validation message
9516      */
9517     markInvalid : function(msg)
9518     {
9519         if(!this.el  || this.preventMark){ // not rendered
9520             return;
9521         }
9522         
9523         this.el.removeClass([this.invalidClass, this.validClass]);
9524         
9525         var feedback = this.el.select('.form-control-feedback', true).first();
9526             
9527         if(feedback){
9528             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9529         }
9530
9531         if(this.disabled){
9532             return;
9533         }
9534         
9535         if(this.allowBlank && !this.getRawValue().length){
9536             return;
9537         }
9538         
9539         if(this.indicator){
9540             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9541             this.indicator.addClass('visible');
9542         }
9543         
9544         this.el.addClass(this.invalidClass);
9545         
9546         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9547             
9548             var feedback = this.el.select('.form-control-feedback', true).first();
9549             
9550             if(feedback){
9551                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9552                 
9553                 if(this.getValue().length || this.forceFeedback){
9554                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9555                 }
9556                 
9557             }
9558             
9559         }
9560         
9561         this.fireEvent('invalid', this, msg);
9562     },
9563     // private
9564     SafariOnKeyDown : function(event)
9565     {
9566         // this is a workaround for a password hang bug on chrome/ webkit.
9567         if (this.inputEl().dom.type != 'password') {
9568             return;
9569         }
9570         
9571         var isSelectAll = false;
9572         
9573         if(this.inputEl().dom.selectionEnd > 0){
9574             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9575         }
9576         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9577             event.preventDefault();
9578             this.setValue('');
9579             return;
9580         }
9581         
9582         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9583             
9584             event.preventDefault();
9585             // this is very hacky as keydown always get's upper case.
9586             //
9587             var cc = String.fromCharCode(event.getCharCode());
9588             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9589             
9590         }
9591     },
9592     adjustWidth : function(tag, w){
9593         tag = tag.toLowerCase();
9594         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9595             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9596                 if(tag == 'input'){
9597                     return w + 2;
9598                 }
9599                 if(tag == 'textarea'){
9600                     return w-2;
9601                 }
9602             }else if(Roo.isOpera){
9603                 if(tag == 'input'){
9604                     return w + 2;
9605                 }
9606                 if(tag == 'textarea'){
9607                     return w-2;
9608                 }
9609             }
9610         }
9611         return w;
9612     },
9613     
9614     setFieldLabel : function(v)
9615     {
9616         if(!this.rendered){
9617             return;
9618         }
9619         
9620         if(this.indicator){
9621             var ar = this.el.select('label > span',true);
9622             
9623             if (ar.elements.length) {
9624                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9625                 this.fieldLabel = v;
9626                 return;
9627             }
9628             
9629             var br = this.el.select('label',true);
9630             
9631             if(br.elements.length) {
9632                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9633                 this.fieldLabel = v;
9634                 return;
9635             }
9636             
9637             Roo.log('Cannot Found any of label > span || label in input');
9638             return;
9639         }
9640         
9641         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9642         this.fieldLabel = v;
9643         
9644         
9645     }
9646 });
9647
9648  
9649 /*
9650  * - LGPL
9651  *
9652  * Input
9653  * 
9654  */
9655
9656 /**
9657  * @class Roo.bootstrap.TextArea
9658  * @extends Roo.bootstrap.Input
9659  * Bootstrap TextArea class
9660  * @cfg {Number} cols Specifies the visible width of a text area
9661  * @cfg {Number} rows Specifies the visible number of lines in a text area
9662  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9663  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9664  * @cfg {string} html text
9665  * 
9666  * @constructor
9667  * Create a new TextArea
9668  * @param {Object} config The config object
9669  */
9670
9671 Roo.bootstrap.TextArea = function(config){
9672     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9673    
9674 };
9675
9676 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9677      
9678     cols : false,
9679     rows : 5,
9680     readOnly : false,
9681     warp : 'soft',
9682     resize : false,
9683     value: false,
9684     html: false,
9685     
9686     getAutoCreate : function(){
9687         
9688         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9689         
9690         var id = Roo.id();
9691         
9692         var cfg = {};
9693         
9694         if(this.inputType != 'hidden'){
9695             cfg.cls = 'form-group' //input-group
9696         }
9697         
9698         var input =  {
9699             tag: 'textarea',
9700             id : id,
9701             warp : this.warp,
9702             rows : this.rows,
9703             value : this.value || '',
9704             html: this.html || '',
9705             cls : 'form-control',
9706             placeholder : this.placeholder || '' 
9707             
9708         };
9709         
9710         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9711             input.maxLength = this.maxLength;
9712         }
9713         
9714         if(this.resize){
9715             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9716         }
9717         
9718         if(this.cols){
9719             input.cols = this.cols;
9720         }
9721         
9722         if (this.readOnly) {
9723             input.readonly = true;
9724         }
9725         
9726         if (this.name) {
9727             input.name = this.name;
9728         }
9729         
9730         if (this.size) {
9731             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9732         }
9733         
9734         var settings=this;
9735         ['xs','sm','md','lg'].map(function(size){
9736             if (settings[size]) {
9737                 cfg.cls += ' col-' + size + '-' + settings[size];
9738             }
9739         });
9740         
9741         var inputblock = input;
9742         
9743         if(this.hasFeedback && !this.allowBlank){
9744             
9745             var feedback = {
9746                 tag: 'span',
9747                 cls: 'glyphicon form-control-feedback'
9748             };
9749
9750             inputblock = {
9751                 cls : 'has-feedback',
9752                 cn :  [
9753                     input,
9754                     feedback
9755                 ] 
9756             };  
9757         }
9758         
9759         
9760         if (this.before || this.after) {
9761             
9762             inputblock = {
9763                 cls : 'input-group',
9764                 cn :  [] 
9765             };
9766             if (this.before) {
9767                 inputblock.cn.push({
9768                     tag :'span',
9769                     cls : 'input-group-addon',
9770                     html : this.before
9771                 });
9772             }
9773             
9774             inputblock.cn.push(input);
9775             
9776             if(this.hasFeedback && !this.allowBlank){
9777                 inputblock.cls += ' has-feedback';
9778                 inputblock.cn.push(feedback);
9779             }
9780             
9781             if (this.after) {
9782                 inputblock.cn.push({
9783                     tag :'span',
9784                     cls : 'input-group-addon',
9785                     html : this.after
9786                 });
9787             }
9788             
9789         }
9790         
9791         if (align ==='left' && this.fieldLabel.length) {
9792             cfg.cn = [
9793                 {
9794                     tag: 'label',
9795                     'for' :  id,
9796                     cls : 'control-label',
9797                     html : this.fieldLabel
9798                 },
9799                 {
9800                     cls : "",
9801                     cn: [
9802                         inputblock
9803                     ]
9804                 }
9805
9806             ];
9807             
9808             if(this.labelWidth > 12){
9809                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9810             }
9811
9812             if(this.labelWidth < 13 && this.labelmd == 0){
9813                 this.labelmd = this.labelWidth;
9814             }
9815
9816             if(this.labellg > 0){
9817                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9818                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9819             }
9820
9821             if(this.labelmd > 0){
9822                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9823                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9824             }
9825
9826             if(this.labelsm > 0){
9827                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9828                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9829             }
9830
9831             if(this.labelxs > 0){
9832                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9833                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9834             }
9835             
9836         } else if ( this.fieldLabel.length) {
9837             cfg.cn = [
9838
9839                {
9840                    tag: 'label',
9841                    //cls : 'input-group-addon',
9842                    html : this.fieldLabel
9843
9844                },
9845
9846                inputblock
9847
9848            ];
9849
9850         } else {
9851
9852             cfg.cn = [
9853
9854                 inputblock
9855
9856             ];
9857                 
9858         }
9859         
9860         if (this.disabled) {
9861             input.disabled=true;
9862         }
9863         
9864         return cfg;
9865         
9866     },
9867     /**
9868      * return the real textarea element.
9869      */
9870     inputEl: function ()
9871     {
9872         return this.el.select('textarea.form-control',true).first();
9873     },
9874     
9875     /**
9876      * Clear any invalid styles/messages for this field
9877      */
9878     clearInvalid : function()
9879     {
9880         
9881         if(!this.el || this.preventMark){ // not rendered
9882             return;
9883         }
9884         
9885         var label = this.el.select('label', true).first();
9886         var icon = this.el.select('i.fa-star', true).first();
9887         
9888         if(label && icon){
9889             icon.remove();
9890         }
9891         
9892         this.el.removeClass(this.invalidClass);
9893         
9894         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9895             
9896             var feedback = this.el.select('.form-control-feedback', true).first();
9897             
9898             if(feedback){
9899                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9900             }
9901             
9902         }
9903         
9904         this.fireEvent('valid', this);
9905     },
9906     
9907      /**
9908      * Mark this field as valid
9909      */
9910     markValid : function()
9911     {
9912         if(!this.el  || this.preventMark){ // not rendered
9913             return;
9914         }
9915         
9916         this.el.removeClass([this.invalidClass, this.validClass]);
9917         
9918         var feedback = this.el.select('.form-control-feedback', true).first();
9919             
9920         if(feedback){
9921             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9922         }
9923
9924         if(this.disabled || this.allowBlank){
9925             return;
9926         }
9927         
9928         var label = this.el.select('label', true).first();
9929         var icon = this.el.select('i.fa-star', true).first();
9930         
9931         if(label && icon){
9932             icon.remove();
9933         }
9934         
9935         this.el.addClass(this.validClass);
9936         
9937         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9938             
9939             var feedback = this.el.select('.form-control-feedback', true).first();
9940             
9941             if(feedback){
9942                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9943                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9944             }
9945             
9946         }
9947         
9948         this.fireEvent('valid', this);
9949     },
9950     
9951      /**
9952      * Mark this field as invalid
9953      * @param {String} msg The validation message
9954      */
9955     markInvalid : function(msg)
9956     {
9957         if(!this.el  || this.preventMark){ // not rendered
9958             return;
9959         }
9960         
9961         this.el.removeClass([this.invalidClass, this.validClass]);
9962         
9963         var feedback = this.el.select('.form-control-feedback', true).first();
9964             
9965         if(feedback){
9966             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9967         }
9968
9969         if(this.disabled || this.allowBlank){
9970             return;
9971         }
9972         
9973         var label = this.el.select('label', true).first();
9974         var icon = this.el.select('i.fa-star', true).first();
9975         
9976         if(!this.getValue().length && label && !icon){
9977             this.el.createChild({
9978                 tag : 'i',
9979                 cls : 'text-danger fa fa-lg fa-star',
9980                 tooltip : 'This field is required',
9981                 style : 'margin-right:5px;'
9982             }, label, true);
9983         }
9984
9985         this.el.addClass(this.invalidClass);
9986         
9987         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9988             
9989             var feedback = this.el.select('.form-control-feedback', true).first();
9990             
9991             if(feedback){
9992                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9993                 
9994                 if(this.getValue().length || this.forceFeedback){
9995                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9996                 }
9997                 
9998             }
9999             
10000         }
10001         
10002         this.fireEvent('invalid', this, msg);
10003     }
10004 });
10005
10006  
10007 /*
10008  * - LGPL
10009  *
10010  * trigger field - base class for combo..
10011  * 
10012  */
10013  
10014 /**
10015  * @class Roo.bootstrap.TriggerField
10016  * @extends Roo.bootstrap.Input
10017  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10018  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10019  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10020  * for which you can provide a custom implementation.  For example:
10021  * <pre><code>
10022 var trigger = new Roo.bootstrap.TriggerField();
10023 trigger.onTriggerClick = myTriggerFn;
10024 trigger.applyTo('my-field');
10025 </code></pre>
10026  *
10027  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10028  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10029  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10030  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10031  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10032
10033  * @constructor
10034  * Create a new TriggerField.
10035  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10036  * to the base TextField)
10037  */
10038 Roo.bootstrap.TriggerField = function(config){
10039     this.mimicing = false;
10040     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10041 };
10042
10043 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10044     /**
10045      * @cfg {String} triggerClass A CSS class to apply to the trigger
10046      */
10047      /**
10048      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10049      */
10050     hideTrigger:false,
10051
10052     /**
10053      * @cfg {Boolean} removable (true|false) special filter default false
10054      */
10055     removable : false,
10056     
10057     /** @cfg {Boolean} grow @hide */
10058     /** @cfg {Number} growMin @hide */
10059     /** @cfg {Number} growMax @hide */
10060
10061     /**
10062      * @hide 
10063      * @method
10064      */
10065     autoSize: Roo.emptyFn,
10066     // private
10067     monitorTab : true,
10068     // private
10069     deferHeight : true,
10070
10071     
10072     actionMode : 'wrap',
10073     
10074     caret : false,
10075     
10076     
10077     getAutoCreate : function(){
10078        
10079         var align = this.labelAlign || this.parentLabelAlign();
10080         
10081         var id = Roo.id();
10082         
10083         var cfg = {
10084             cls: 'form-group' //input-group
10085         };
10086         
10087         
10088         var input =  {
10089             tag: 'input',
10090             id : id,
10091             type : this.inputType,
10092             cls : 'form-control',
10093             autocomplete: 'new-password',
10094             placeholder : this.placeholder || '' 
10095             
10096         };
10097         if (this.name) {
10098             input.name = this.name;
10099         }
10100         if (this.size) {
10101             input.cls += ' input-' + this.size;
10102         }
10103         
10104         if (this.disabled) {
10105             input.disabled=true;
10106         }
10107         
10108         var inputblock = input;
10109         
10110         if(this.hasFeedback && !this.allowBlank){
10111             
10112             var feedback = {
10113                 tag: 'span',
10114                 cls: 'glyphicon form-control-feedback'
10115             };
10116             
10117             if(this.removable && !this.editable && !this.tickable){
10118                 inputblock = {
10119                     cls : 'has-feedback',
10120                     cn :  [
10121                         inputblock,
10122                         {
10123                             tag: 'button',
10124                             html : 'x',
10125                             cls : 'roo-combo-removable-btn close'
10126                         },
10127                         feedback
10128                     ] 
10129                 };
10130             } else {
10131                 inputblock = {
10132                     cls : 'has-feedback',
10133                     cn :  [
10134                         inputblock,
10135                         feedback
10136                     ] 
10137                 };
10138             }
10139
10140         } else {
10141             if(this.removable && !this.editable && !this.tickable){
10142                 inputblock = {
10143                     cls : 'roo-removable',
10144                     cn :  [
10145                         inputblock,
10146                         {
10147                             tag: 'button',
10148                             html : 'x',
10149                             cls : 'roo-combo-removable-btn close'
10150                         }
10151                     ] 
10152                 };
10153             }
10154         }
10155         
10156         if (this.before || this.after) {
10157             
10158             inputblock = {
10159                 cls : 'input-group',
10160                 cn :  [] 
10161             };
10162             if (this.before) {
10163                 inputblock.cn.push({
10164                     tag :'span',
10165                     cls : 'input-group-addon',
10166                     html : this.before
10167                 });
10168             }
10169             
10170             inputblock.cn.push(input);
10171             
10172             if(this.hasFeedback && !this.allowBlank){
10173                 inputblock.cls += ' has-feedback';
10174                 inputblock.cn.push(feedback);
10175             }
10176             
10177             if (this.after) {
10178                 inputblock.cn.push({
10179                     tag :'span',
10180                     cls : 'input-group-addon',
10181                     html : this.after
10182                 });
10183             }
10184             
10185         };
10186         
10187         var box = {
10188             tag: 'div',
10189             cn: [
10190                 {
10191                     tag: 'input',
10192                     type : 'hidden',
10193                     cls: 'form-hidden-field'
10194                 },
10195                 inputblock
10196             ]
10197             
10198         };
10199         
10200         if(this.multiple){
10201             box = {
10202                 tag: 'div',
10203                 cn: [
10204                     {
10205                         tag: 'input',
10206                         type : 'hidden',
10207                         cls: 'form-hidden-field'
10208                     },
10209                     {
10210                         tag: 'ul',
10211                         cls: 'roo-select2-choices',
10212                         cn:[
10213                             {
10214                                 tag: 'li',
10215                                 cls: 'roo-select2-search-field',
10216                                 cn: [
10217
10218                                     inputblock
10219                                 ]
10220                             }
10221                         ]
10222                     }
10223                 ]
10224             }
10225         };
10226         
10227         var combobox = {
10228             cls: 'roo-select2-container input-group',
10229             cn: [
10230                 box
10231 //                {
10232 //                    tag: 'ul',
10233 //                    cls: 'typeahead typeahead-long dropdown-menu',
10234 //                    style: 'display:none'
10235 //                }
10236             ]
10237         };
10238         
10239         if(!this.multiple && this.showToggleBtn){
10240             
10241             var caret = {
10242                         tag: 'span',
10243                         cls: 'caret'
10244              };
10245             if (this.caret != false) {
10246                 caret = {
10247                      tag: 'i',
10248                      cls: 'fa fa-' + this.caret
10249                 };
10250                 
10251             }
10252             
10253             combobox.cn.push({
10254                 tag :'span',
10255                 cls : 'input-group-addon btn dropdown-toggle',
10256                 cn : [
10257                     caret,
10258                     {
10259                         tag: 'span',
10260                         cls: 'combobox-clear',
10261                         cn  : [
10262                             {
10263                                 tag : 'i',
10264                                 cls: 'icon-remove'
10265                             }
10266                         ]
10267                     }
10268                 ]
10269
10270             })
10271         }
10272         
10273         if(this.multiple){
10274             combobox.cls += ' roo-select2-container-multi';
10275         }
10276         
10277         if (align ==='left' && this.fieldLabel.length) {
10278             
10279             cfg.cls += ' roo-form-group-label-left';
10280
10281             cfg.cn = [
10282                 {
10283                     tag : 'i',
10284                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10285                     tooltip : 'This field is required'
10286                 },
10287                 {
10288                     tag: 'label',
10289                     'for' :  id,
10290                     cls : 'control-label',
10291                     html : this.fieldLabel
10292
10293                 },
10294                 {
10295                     cls : "", 
10296                     cn: [
10297                         combobox
10298                     ]
10299                 }
10300
10301             ];
10302             
10303             var labelCfg = cfg.cn[1];
10304             var contentCfg = cfg.cn[2];
10305             
10306             if(this.indicatorpos == 'right'){
10307                 cfg.cn = [
10308                     {
10309                         tag: 'label',
10310                         'for' :  id,
10311                         cls : 'control-label',
10312                         cn : [
10313                             {
10314                                 tag : 'span',
10315                                 html : this.fieldLabel
10316                             },
10317                             {
10318                                 tag : 'i',
10319                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10320                                 tooltip : 'This field is required'
10321                             }
10322                         ]
10323                     },
10324                     {
10325                         cls : "", 
10326                         cn: [
10327                             combobox
10328                         ]
10329                     }
10330
10331                 ];
10332                 
10333                 labelCfg = cfg.cn[0];
10334                 contentCfg = cfg.cn[1];
10335             }
10336             
10337             if(this.labelWidth > 12){
10338                 labelCfg.style = "width: " + this.labelWidth + 'px';
10339             }
10340             
10341             if(this.labelWidth < 13 && this.labelmd == 0){
10342                 this.labelmd = this.labelWidth;
10343             }
10344             
10345             if(this.labellg > 0){
10346                 labelCfg.cls += ' col-lg-' + this.labellg;
10347                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10348             }
10349             
10350             if(this.labelmd > 0){
10351                 labelCfg.cls += ' col-md-' + this.labelmd;
10352                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10353             }
10354             
10355             if(this.labelsm > 0){
10356                 labelCfg.cls += ' col-sm-' + this.labelsm;
10357                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10358             }
10359             
10360             if(this.labelxs > 0){
10361                 labelCfg.cls += ' col-xs-' + this.labelxs;
10362                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10363             }
10364             
10365         } else if ( this.fieldLabel.length) {
10366 //                Roo.log(" label");
10367             cfg.cn = [
10368                 {
10369                    tag : 'i',
10370                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10371                    tooltip : 'This field is required'
10372                },
10373                {
10374                    tag: 'label',
10375                    //cls : 'input-group-addon',
10376                    html : this.fieldLabel
10377
10378                },
10379
10380                combobox
10381
10382             ];
10383             
10384             if(this.indicatorpos == 'right'){
10385                 
10386                 cfg.cn = [
10387                     {
10388                        tag: 'label',
10389                        cn : [
10390                            {
10391                                tag : 'span',
10392                                html : this.fieldLabel
10393                            },
10394                            {
10395                               tag : 'i',
10396                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10397                               tooltip : 'This field is required'
10398                            }
10399                        ]
10400
10401                     },
10402                     combobox
10403
10404                 ];
10405
10406             }
10407
10408         } else {
10409             
10410 //                Roo.log(" no label && no align");
10411                 cfg = combobox
10412                      
10413                 
10414         }
10415         
10416         var settings=this;
10417         ['xs','sm','md','lg'].map(function(size){
10418             if (settings[size]) {
10419                 cfg.cls += ' col-' + size + '-' + settings[size];
10420             }
10421         });
10422         
10423         return cfg;
10424         
10425     },
10426     
10427     
10428     
10429     // private
10430     onResize : function(w, h){
10431 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10432 //        if(typeof w == 'number'){
10433 //            var x = w - this.trigger.getWidth();
10434 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10435 //            this.trigger.setStyle('left', x+'px');
10436 //        }
10437     },
10438
10439     // private
10440     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10441
10442     // private
10443     getResizeEl : function(){
10444         return this.inputEl();
10445     },
10446
10447     // private
10448     getPositionEl : function(){
10449         return this.inputEl();
10450     },
10451
10452     // private
10453     alignErrorIcon : function(){
10454         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10455     },
10456
10457     // private
10458     initEvents : function(){
10459         
10460         this.createList();
10461         
10462         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10463         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10464         if(!this.multiple && this.showToggleBtn){
10465             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10466             if(this.hideTrigger){
10467                 this.trigger.setDisplayed(false);
10468             }
10469             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10470         }
10471         
10472         if(this.multiple){
10473             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10474         }
10475         
10476         if(this.removable && !this.editable && !this.tickable){
10477             var close = this.closeTriggerEl();
10478             
10479             if(close){
10480                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10481                 close.on('click', this.removeBtnClick, this, close);
10482             }
10483         }
10484         
10485         //this.trigger.addClassOnOver('x-form-trigger-over');
10486         //this.trigger.addClassOnClick('x-form-trigger-click');
10487         
10488         //if(!this.width){
10489         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10490         //}
10491     },
10492     
10493     closeTriggerEl : function()
10494     {
10495         var close = this.el.select('.roo-combo-removable-btn', true).first();
10496         return close ? close : false;
10497     },
10498     
10499     removeBtnClick : function(e, h, el)
10500     {
10501         e.preventDefault();
10502         
10503         if(this.fireEvent("remove", this) !== false){
10504             this.reset();
10505             this.fireEvent("afterremove", this)
10506         }
10507     },
10508     
10509     createList : function()
10510     {
10511         this.list = Roo.get(document.body).createChild({
10512             tag: 'ul',
10513             cls: 'typeahead typeahead-long dropdown-menu',
10514             style: 'display:none'
10515         });
10516         
10517         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10518         
10519     },
10520
10521     // private
10522     initTrigger : function(){
10523        
10524     },
10525
10526     // private
10527     onDestroy : function(){
10528         if(this.trigger){
10529             this.trigger.removeAllListeners();
10530           //  this.trigger.remove();
10531         }
10532         //if(this.wrap){
10533         //    this.wrap.remove();
10534         //}
10535         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10536     },
10537
10538     // private
10539     onFocus : function(){
10540         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10541         /*
10542         if(!this.mimicing){
10543             this.wrap.addClass('x-trigger-wrap-focus');
10544             this.mimicing = true;
10545             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10546             if(this.monitorTab){
10547                 this.el.on("keydown", this.checkTab, this);
10548             }
10549         }
10550         */
10551     },
10552
10553     // private
10554     checkTab : function(e){
10555         if(e.getKey() == e.TAB){
10556             this.triggerBlur();
10557         }
10558     },
10559
10560     // private
10561     onBlur : function(){
10562         // do nothing
10563     },
10564
10565     // private
10566     mimicBlur : function(e, t){
10567         /*
10568         if(!this.wrap.contains(t) && this.validateBlur()){
10569             this.triggerBlur();
10570         }
10571         */
10572     },
10573
10574     // private
10575     triggerBlur : function(){
10576         this.mimicing = false;
10577         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10578         if(this.monitorTab){
10579             this.el.un("keydown", this.checkTab, this);
10580         }
10581         //this.wrap.removeClass('x-trigger-wrap-focus');
10582         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10583     },
10584
10585     // private
10586     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10587     validateBlur : function(e, t){
10588         return true;
10589     },
10590
10591     // private
10592     onDisable : function(){
10593         this.inputEl().dom.disabled = true;
10594         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10595         //if(this.wrap){
10596         //    this.wrap.addClass('x-item-disabled');
10597         //}
10598     },
10599
10600     // private
10601     onEnable : function(){
10602         this.inputEl().dom.disabled = false;
10603         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10604         //if(this.wrap){
10605         //    this.el.removeClass('x-item-disabled');
10606         //}
10607     },
10608
10609     // private
10610     onShow : function(){
10611         var ae = this.getActionEl();
10612         
10613         if(ae){
10614             ae.dom.style.display = '';
10615             ae.dom.style.visibility = 'visible';
10616         }
10617     },
10618
10619     // private
10620     
10621     onHide : function(){
10622         var ae = this.getActionEl();
10623         ae.dom.style.display = 'none';
10624     },
10625
10626     /**
10627      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10628      * by an implementing function.
10629      * @method
10630      * @param {EventObject} e
10631      */
10632     onTriggerClick : Roo.emptyFn
10633 });
10634  /*
10635  * Based on:
10636  * Ext JS Library 1.1.1
10637  * Copyright(c) 2006-2007, Ext JS, LLC.
10638  *
10639  * Originally Released Under LGPL - original licence link has changed is not relivant.
10640  *
10641  * Fork - LGPL
10642  * <script type="text/javascript">
10643  */
10644
10645
10646 /**
10647  * @class Roo.data.SortTypes
10648  * @singleton
10649  * Defines the default sorting (casting?) comparison functions used when sorting data.
10650  */
10651 Roo.data.SortTypes = {
10652     /**
10653      * Default sort that does nothing
10654      * @param {Mixed} s The value being converted
10655      * @return {Mixed} The comparison value
10656      */
10657     none : function(s){
10658         return s;
10659     },
10660     
10661     /**
10662      * The regular expression used to strip tags
10663      * @type {RegExp}
10664      * @property
10665      */
10666     stripTagsRE : /<\/?[^>]+>/gi,
10667     
10668     /**
10669      * Strips all HTML tags to sort on text only
10670      * @param {Mixed} s The value being converted
10671      * @return {String} The comparison value
10672      */
10673     asText : function(s){
10674         return String(s).replace(this.stripTagsRE, "");
10675     },
10676     
10677     /**
10678      * Strips all HTML tags to sort on text only - Case insensitive
10679      * @param {Mixed} s The value being converted
10680      * @return {String} The comparison value
10681      */
10682     asUCText : function(s){
10683         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10684     },
10685     
10686     /**
10687      * Case insensitive string
10688      * @param {Mixed} s The value being converted
10689      * @return {String} The comparison value
10690      */
10691     asUCString : function(s) {
10692         return String(s).toUpperCase();
10693     },
10694     
10695     /**
10696      * Date sorting
10697      * @param {Mixed} s The value being converted
10698      * @return {Number} The comparison value
10699      */
10700     asDate : function(s) {
10701         if(!s){
10702             return 0;
10703         }
10704         if(s instanceof Date){
10705             return s.getTime();
10706         }
10707         return Date.parse(String(s));
10708     },
10709     
10710     /**
10711      * Float sorting
10712      * @param {Mixed} s The value being converted
10713      * @return {Float} The comparison value
10714      */
10715     asFloat : function(s) {
10716         var val = parseFloat(String(s).replace(/,/g, ""));
10717         if(isNaN(val)) {
10718             val = 0;
10719         }
10720         return val;
10721     },
10722     
10723     /**
10724      * Integer sorting
10725      * @param {Mixed} s The value being converted
10726      * @return {Number} The comparison value
10727      */
10728     asInt : function(s) {
10729         var val = parseInt(String(s).replace(/,/g, ""));
10730         if(isNaN(val)) {
10731             val = 0;
10732         }
10733         return val;
10734     }
10735 };/*
10736  * Based on:
10737  * Ext JS Library 1.1.1
10738  * Copyright(c) 2006-2007, Ext JS, LLC.
10739  *
10740  * Originally Released Under LGPL - original licence link has changed is not relivant.
10741  *
10742  * Fork - LGPL
10743  * <script type="text/javascript">
10744  */
10745
10746 /**
10747 * @class Roo.data.Record
10748  * Instances of this class encapsulate both record <em>definition</em> information, and record
10749  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10750  * to access Records cached in an {@link Roo.data.Store} object.<br>
10751  * <p>
10752  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10753  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10754  * objects.<br>
10755  * <p>
10756  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10757  * @constructor
10758  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10759  * {@link #create}. The parameters are the same.
10760  * @param {Array} data An associative Array of data values keyed by the field name.
10761  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10762  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10763  * not specified an integer id is generated.
10764  */
10765 Roo.data.Record = function(data, id){
10766     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10767     this.data = data;
10768 };
10769
10770 /**
10771  * Generate a constructor for a specific record layout.
10772  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10773  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10774  * Each field definition object may contain the following properties: <ul>
10775  * <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,
10776  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10777  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10778  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10779  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10780  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10781  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10782  * this may be omitted.</p></li>
10783  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10784  * <ul><li>auto (Default, implies no conversion)</li>
10785  * <li>string</li>
10786  * <li>int</li>
10787  * <li>float</li>
10788  * <li>boolean</li>
10789  * <li>date</li></ul></p></li>
10790  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10791  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10792  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10793  * by the Reader into an object that will be stored in the Record. It is passed the
10794  * following parameters:<ul>
10795  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10796  * </ul></p></li>
10797  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10798  * </ul>
10799  * <br>usage:<br><pre><code>
10800 var TopicRecord = Roo.data.Record.create(
10801     {name: 'title', mapping: 'topic_title'},
10802     {name: 'author', mapping: 'username'},
10803     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10804     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10805     {name: 'lastPoster', mapping: 'user2'},
10806     {name: 'excerpt', mapping: 'post_text'}
10807 );
10808
10809 var myNewRecord = new TopicRecord({
10810     title: 'Do my job please',
10811     author: 'noobie',
10812     totalPosts: 1,
10813     lastPost: new Date(),
10814     lastPoster: 'Animal',
10815     excerpt: 'No way dude!'
10816 });
10817 myStore.add(myNewRecord);
10818 </code></pre>
10819  * @method create
10820  * @static
10821  */
10822 Roo.data.Record.create = function(o){
10823     var f = function(){
10824         f.superclass.constructor.apply(this, arguments);
10825     };
10826     Roo.extend(f, Roo.data.Record);
10827     var p = f.prototype;
10828     p.fields = new Roo.util.MixedCollection(false, function(field){
10829         return field.name;
10830     });
10831     for(var i = 0, len = o.length; i < len; i++){
10832         p.fields.add(new Roo.data.Field(o[i]));
10833     }
10834     f.getField = function(name){
10835         return p.fields.get(name);  
10836     };
10837     return f;
10838 };
10839
10840 Roo.data.Record.AUTO_ID = 1000;
10841 Roo.data.Record.EDIT = 'edit';
10842 Roo.data.Record.REJECT = 'reject';
10843 Roo.data.Record.COMMIT = 'commit';
10844
10845 Roo.data.Record.prototype = {
10846     /**
10847      * Readonly flag - true if this record has been modified.
10848      * @type Boolean
10849      */
10850     dirty : false,
10851     editing : false,
10852     error: null,
10853     modified: null,
10854
10855     // private
10856     join : function(store){
10857         this.store = store;
10858     },
10859
10860     /**
10861      * Set the named field to the specified value.
10862      * @param {String} name The name of the field to set.
10863      * @param {Object} value The value to set the field to.
10864      */
10865     set : function(name, value){
10866         if(this.data[name] == value){
10867             return;
10868         }
10869         this.dirty = true;
10870         if(!this.modified){
10871             this.modified = {};
10872         }
10873         if(typeof this.modified[name] == 'undefined'){
10874             this.modified[name] = this.data[name];
10875         }
10876         this.data[name] = value;
10877         if(!this.editing && this.store){
10878             this.store.afterEdit(this);
10879         }       
10880     },
10881
10882     /**
10883      * Get the value of the named field.
10884      * @param {String} name The name of the field to get the value of.
10885      * @return {Object} The value of the field.
10886      */
10887     get : function(name){
10888         return this.data[name]; 
10889     },
10890
10891     // private
10892     beginEdit : function(){
10893         this.editing = true;
10894         this.modified = {}; 
10895     },
10896
10897     // private
10898     cancelEdit : function(){
10899         this.editing = false;
10900         delete this.modified;
10901     },
10902
10903     // private
10904     endEdit : function(){
10905         this.editing = false;
10906         if(this.dirty && this.store){
10907             this.store.afterEdit(this);
10908         }
10909     },
10910
10911     /**
10912      * Usually called by the {@link Roo.data.Store} which owns the Record.
10913      * Rejects all changes made to the Record since either creation, or the last commit operation.
10914      * Modified fields are reverted to their original values.
10915      * <p>
10916      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10917      * of reject operations.
10918      */
10919     reject : function(){
10920         var m = this.modified;
10921         for(var n in m){
10922             if(typeof m[n] != "function"){
10923                 this.data[n] = m[n];
10924             }
10925         }
10926         this.dirty = false;
10927         delete this.modified;
10928         this.editing = false;
10929         if(this.store){
10930             this.store.afterReject(this);
10931         }
10932     },
10933
10934     /**
10935      * Usually called by the {@link Roo.data.Store} which owns the Record.
10936      * Commits all changes made to the Record since either creation, or the last commit operation.
10937      * <p>
10938      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10939      * of commit operations.
10940      */
10941     commit : function(){
10942         this.dirty = false;
10943         delete this.modified;
10944         this.editing = false;
10945         if(this.store){
10946             this.store.afterCommit(this);
10947         }
10948     },
10949
10950     // private
10951     hasError : function(){
10952         return this.error != null;
10953     },
10954
10955     // private
10956     clearError : function(){
10957         this.error = null;
10958     },
10959
10960     /**
10961      * Creates a copy of this record.
10962      * @param {String} id (optional) A new record id if you don't want to use this record's id
10963      * @return {Record}
10964      */
10965     copy : function(newId) {
10966         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10967     }
10968 };/*
10969  * Based on:
10970  * Ext JS Library 1.1.1
10971  * Copyright(c) 2006-2007, Ext JS, LLC.
10972  *
10973  * Originally Released Under LGPL - original licence link has changed is not relivant.
10974  *
10975  * Fork - LGPL
10976  * <script type="text/javascript">
10977  */
10978
10979
10980
10981 /**
10982  * @class Roo.data.Store
10983  * @extends Roo.util.Observable
10984  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10985  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10986  * <p>
10987  * 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
10988  * has no knowledge of the format of the data returned by the Proxy.<br>
10989  * <p>
10990  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10991  * instances from the data object. These records are cached and made available through accessor functions.
10992  * @constructor
10993  * Creates a new Store.
10994  * @param {Object} config A config object containing the objects needed for the Store to access data,
10995  * and read the data into Records.
10996  */
10997 Roo.data.Store = function(config){
10998     this.data = new Roo.util.MixedCollection(false);
10999     this.data.getKey = function(o){
11000         return o.id;
11001     };
11002     this.baseParams = {};
11003     // private
11004     this.paramNames = {
11005         "start" : "start",
11006         "limit" : "limit",
11007         "sort" : "sort",
11008         "dir" : "dir",
11009         "multisort" : "_multisort"
11010     };
11011
11012     if(config && config.data){
11013         this.inlineData = config.data;
11014         delete config.data;
11015     }
11016
11017     Roo.apply(this, config);
11018     
11019     if(this.reader){ // reader passed
11020         this.reader = Roo.factory(this.reader, Roo.data);
11021         this.reader.xmodule = this.xmodule || false;
11022         if(!this.recordType){
11023             this.recordType = this.reader.recordType;
11024         }
11025         if(this.reader.onMetaChange){
11026             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11027         }
11028     }
11029
11030     if(this.recordType){
11031         this.fields = this.recordType.prototype.fields;
11032     }
11033     this.modified = [];
11034
11035     this.addEvents({
11036         /**
11037          * @event datachanged
11038          * Fires when the data cache has changed, and a widget which is using this Store
11039          * as a Record cache should refresh its view.
11040          * @param {Store} this
11041          */
11042         datachanged : true,
11043         /**
11044          * @event metachange
11045          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11046          * @param {Store} this
11047          * @param {Object} meta The JSON metadata
11048          */
11049         metachange : true,
11050         /**
11051          * @event add
11052          * Fires when Records have been added to the Store
11053          * @param {Store} this
11054          * @param {Roo.data.Record[]} records The array of Records added
11055          * @param {Number} index The index at which the record(s) were added
11056          */
11057         add : true,
11058         /**
11059          * @event remove
11060          * Fires when a Record has been removed from the Store
11061          * @param {Store} this
11062          * @param {Roo.data.Record} record The Record that was removed
11063          * @param {Number} index The index at which the record was removed
11064          */
11065         remove : true,
11066         /**
11067          * @event update
11068          * Fires when a Record has been updated
11069          * @param {Store} this
11070          * @param {Roo.data.Record} record The Record that was updated
11071          * @param {String} operation The update operation being performed.  Value may be one of:
11072          * <pre><code>
11073  Roo.data.Record.EDIT
11074  Roo.data.Record.REJECT
11075  Roo.data.Record.COMMIT
11076          * </code></pre>
11077          */
11078         update : true,
11079         /**
11080          * @event clear
11081          * Fires when the data cache has been cleared.
11082          * @param {Store} this
11083          */
11084         clear : true,
11085         /**
11086          * @event beforeload
11087          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11088          * the load action will be canceled.
11089          * @param {Store} this
11090          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11091          */
11092         beforeload : true,
11093         /**
11094          * @event beforeloadadd
11095          * Fires after a new set of Records has been loaded.
11096          * @param {Store} this
11097          * @param {Roo.data.Record[]} records The Records that were loaded
11098          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11099          */
11100         beforeloadadd : true,
11101         /**
11102          * @event load
11103          * Fires after a new set of Records has been loaded, before they are added to the store.
11104          * @param {Store} this
11105          * @param {Roo.data.Record[]} records The Records that were loaded
11106          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11107          * @params {Object} return from reader
11108          */
11109         load : true,
11110         /**
11111          * @event loadexception
11112          * Fires if an exception occurs in the Proxy during loading.
11113          * Called with the signature of the Proxy's "loadexception" event.
11114          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11115          * 
11116          * @param {Proxy} 
11117          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11118          * @param {Object} load options 
11119          * @param {Object} jsonData from your request (normally this contains the Exception)
11120          */
11121         loadexception : true
11122     });
11123     
11124     if(this.proxy){
11125         this.proxy = Roo.factory(this.proxy, Roo.data);
11126         this.proxy.xmodule = this.xmodule || false;
11127         this.relayEvents(this.proxy,  ["loadexception"]);
11128     }
11129     this.sortToggle = {};
11130     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11131
11132     Roo.data.Store.superclass.constructor.call(this);
11133
11134     if(this.inlineData){
11135         this.loadData(this.inlineData);
11136         delete this.inlineData;
11137     }
11138 };
11139
11140 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11141      /**
11142     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11143     * without a remote query - used by combo/forms at present.
11144     */
11145     
11146     /**
11147     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11148     */
11149     /**
11150     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11151     */
11152     /**
11153     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11154     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11155     */
11156     /**
11157     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11158     * on any HTTP request
11159     */
11160     /**
11161     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11162     */
11163     /**
11164     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11165     */
11166     multiSort: false,
11167     /**
11168     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11169     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11170     */
11171     remoteSort : false,
11172
11173     /**
11174     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11175      * loaded or when a record is removed. (defaults to false).
11176     */
11177     pruneModifiedRecords : false,
11178
11179     // private
11180     lastOptions : null,
11181
11182     /**
11183      * Add Records to the Store and fires the add event.
11184      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11185      */
11186     add : function(records){
11187         records = [].concat(records);
11188         for(var i = 0, len = records.length; i < len; i++){
11189             records[i].join(this);
11190         }
11191         var index = this.data.length;
11192         this.data.addAll(records);
11193         this.fireEvent("add", this, records, index);
11194     },
11195
11196     /**
11197      * Remove a Record from the Store and fires the remove event.
11198      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11199      */
11200     remove : function(record){
11201         var index = this.data.indexOf(record);
11202         this.data.removeAt(index);
11203  
11204         if(this.pruneModifiedRecords){
11205             this.modified.remove(record);
11206         }
11207         this.fireEvent("remove", this, record, index);
11208     },
11209
11210     /**
11211      * Remove all Records from the Store and fires the clear event.
11212      */
11213     removeAll : function(){
11214         this.data.clear();
11215         if(this.pruneModifiedRecords){
11216             this.modified = [];
11217         }
11218         this.fireEvent("clear", this);
11219     },
11220
11221     /**
11222      * Inserts Records to the Store at the given index and fires the add event.
11223      * @param {Number} index The start index at which to insert the passed Records.
11224      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11225      */
11226     insert : function(index, records){
11227         records = [].concat(records);
11228         for(var i = 0, len = records.length; i < len; i++){
11229             this.data.insert(index, records[i]);
11230             records[i].join(this);
11231         }
11232         this.fireEvent("add", this, records, index);
11233     },
11234
11235     /**
11236      * Get the index within the cache of the passed Record.
11237      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11238      * @return {Number} The index of the passed Record. Returns -1 if not found.
11239      */
11240     indexOf : function(record){
11241         return this.data.indexOf(record);
11242     },
11243
11244     /**
11245      * Get the index within the cache of the Record with the passed id.
11246      * @param {String} id The id of the Record to find.
11247      * @return {Number} The index of the Record. Returns -1 if not found.
11248      */
11249     indexOfId : function(id){
11250         return this.data.indexOfKey(id);
11251     },
11252
11253     /**
11254      * Get the Record with the specified id.
11255      * @param {String} id The id of the Record to find.
11256      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11257      */
11258     getById : function(id){
11259         return this.data.key(id);
11260     },
11261
11262     /**
11263      * Get the Record at the specified index.
11264      * @param {Number} index The index of the Record to find.
11265      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11266      */
11267     getAt : function(index){
11268         return this.data.itemAt(index);
11269     },
11270
11271     /**
11272      * Returns a range of Records between specified indices.
11273      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11274      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11275      * @return {Roo.data.Record[]} An array of Records
11276      */
11277     getRange : function(start, end){
11278         return this.data.getRange(start, end);
11279     },
11280
11281     // private
11282     storeOptions : function(o){
11283         o = Roo.apply({}, o);
11284         delete o.callback;
11285         delete o.scope;
11286         this.lastOptions = o;
11287     },
11288
11289     /**
11290      * Loads the Record cache from the configured Proxy using the configured Reader.
11291      * <p>
11292      * If using remote paging, then the first load call must specify the <em>start</em>
11293      * and <em>limit</em> properties in the options.params property to establish the initial
11294      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11295      * <p>
11296      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11297      * and this call will return before the new data has been loaded. Perform any post-processing
11298      * in a callback function, or in a "load" event handler.</strong>
11299      * <p>
11300      * @param {Object} options An object containing properties which control loading options:<ul>
11301      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11302      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11303      * passed the following arguments:<ul>
11304      * <li>r : Roo.data.Record[]</li>
11305      * <li>options: Options object from the load call</li>
11306      * <li>success: Boolean success indicator</li></ul></li>
11307      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11308      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11309      * </ul>
11310      */
11311     load : function(options){
11312         options = options || {};
11313         if(this.fireEvent("beforeload", this, options) !== false){
11314             this.storeOptions(options);
11315             var p = Roo.apply(options.params || {}, this.baseParams);
11316             // if meta was not loaded from remote source.. try requesting it.
11317             if (!this.reader.metaFromRemote) {
11318                 p._requestMeta = 1;
11319             }
11320             if(this.sortInfo && this.remoteSort){
11321                 var pn = this.paramNames;
11322                 p[pn["sort"]] = this.sortInfo.field;
11323                 p[pn["dir"]] = this.sortInfo.direction;
11324             }
11325             if (this.multiSort) {
11326                 var pn = this.paramNames;
11327                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11328             }
11329             
11330             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11331         }
11332     },
11333
11334     /**
11335      * Reloads the Record cache from the configured Proxy using the configured Reader and
11336      * the options from the last load operation performed.
11337      * @param {Object} options (optional) An object containing properties which may override the options
11338      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11339      * the most recently used options are reused).
11340      */
11341     reload : function(options){
11342         this.load(Roo.applyIf(options||{}, this.lastOptions));
11343     },
11344
11345     // private
11346     // Called as a callback by the Reader during a load operation.
11347     loadRecords : function(o, options, success){
11348         if(!o || success === false){
11349             if(success !== false){
11350                 this.fireEvent("load", this, [], options, o);
11351             }
11352             if(options.callback){
11353                 options.callback.call(options.scope || this, [], options, false);
11354             }
11355             return;
11356         }
11357         // if data returned failure - throw an exception.
11358         if (o.success === false) {
11359             // show a message if no listener is registered.
11360             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11361                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11362             }
11363             // loadmask wil be hooked into this..
11364             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11365             return;
11366         }
11367         var r = o.records, t = o.totalRecords || r.length;
11368         
11369         this.fireEvent("beforeloadadd", this, r, options, o);
11370         
11371         if(!options || options.add !== true){
11372             if(this.pruneModifiedRecords){
11373                 this.modified = [];
11374             }
11375             for(var i = 0, len = r.length; i < len; i++){
11376                 r[i].join(this);
11377             }
11378             if(this.snapshot){
11379                 this.data = this.snapshot;
11380                 delete this.snapshot;
11381             }
11382             this.data.clear();
11383             this.data.addAll(r);
11384             this.totalLength = t;
11385             this.applySort();
11386             this.fireEvent("datachanged", this);
11387         }else{
11388             this.totalLength = Math.max(t, this.data.length+r.length);
11389             this.add(r);
11390         }
11391         
11392         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11393                 
11394             var e = new Roo.data.Record({});
11395
11396             e.set(this.parent.displayField, this.parent.emptyTitle);
11397             e.set(this.parent.valueField, '');
11398
11399             this.insert(0, e);
11400         }
11401             
11402         this.fireEvent("load", this, r, options, o);
11403         if(options.callback){
11404             options.callback.call(options.scope || this, r, options, true);
11405         }
11406     },
11407
11408
11409     /**
11410      * Loads data from a passed data block. A Reader which understands the format of the data
11411      * must have been configured in the constructor.
11412      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11413      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11414      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11415      */
11416     loadData : function(o, append){
11417         var r = this.reader.readRecords(o);
11418         this.loadRecords(r, {add: append}, true);
11419     },
11420
11421     /**
11422      * Gets the number of cached records.
11423      * <p>
11424      * <em>If using paging, this may not be the total size of the dataset. If the data object
11425      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11426      * the data set size</em>
11427      */
11428     getCount : function(){
11429         return this.data.length || 0;
11430     },
11431
11432     /**
11433      * Gets the total number of records in the dataset as returned by the server.
11434      * <p>
11435      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11436      * the dataset size</em>
11437      */
11438     getTotalCount : function(){
11439         return this.totalLength || 0;
11440     },
11441
11442     /**
11443      * Returns the sort state of the Store as an object with two properties:
11444      * <pre><code>
11445  field {String} The name of the field by which the Records are sorted
11446  direction {String} The sort order, "ASC" or "DESC"
11447      * </code></pre>
11448      */
11449     getSortState : function(){
11450         return this.sortInfo;
11451     },
11452
11453     // private
11454     applySort : function(){
11455         if(this.sortInfo && !this.remoteSort){
11456             var s = this.sortInfo, f = s.field;
11457             var st = this.fields.get(f).sortType;
11458             var fn = function(r1, r2){
11459                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11460                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11461             };
11462             this.data.sort(s.direction, fn);
11463             if(this.snapshot && this.snapshot != this.data){
11464                 this.snapshot.sort(s.direction, fn);
11465             }
11466         }
11467     },
11468
11469     /**
11470      * Sets the default sort column and order to be used by the next load operation.
11471      * @param {String} fieldName The name of the field to sort by.
11472      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11473      */
11474     setDefaultSort : function(field, dir){
11475         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11476     },
11477
11478     /**
11479      * Sort the Records.
11480      * If remote sorting is used, the sort is performed on the server, and the cache is
11481      * reloaded. If local sorting is used, the cache is sorted internally.
11482      * @param {String} fieldName The name of the field to sort by.
11483      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11484      */
11485     sort : function(fieldName, dir){
11486         var f = this.fields.get(fieldName);
11487         if(!dir){
11488             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11489             
11490             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11491                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11492             }else{
11493                 dir = f.sortDir;
11494             }
11495         }
11496         this.sortToggle[f.name] = dir;
11497         this.sortInfo = {field: f.name, direction: dir};
11498         if(!this.remoteSort){
11499             this.applySort();
11500             this.fireEvent("datachanged", this);
11501         }else{
11502             this.load(this.lastOptions);
11503         }
11504     },
11505
11506     /**
11507      * Calls the specified function for each of the Records in the cache.
11508      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11509      * Returning <em>false</em> aborts and exits the iteration.
11510      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11511      */
11512     each : function(fn, scope){
11513         this.data.each(fn, scope);
11514     },
11515
11516     /**
11517      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11518      * (e.g., during paging).
11519      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11520      */
11521     getModifiedRecords : function(){
11522         return this.modified;
11523     },
11524
11525     // private
11526     createFilterFn : function(property, value, anyMatch){
11527         if(!value.exec){ // not a regex
11528             value = String(value);
11529             if(value.length == 0){
11530                 return false;
11531             }
11532             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11533         }
11534         return function(r){
11535             return value.test(r.data[property]);
11536         };
11537     },
11538
11539     /**
11540      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11541      * @param {String} property A field on your records
11542      * @param {Number} start The record index to start at (defaults to 0)
11543      * @param {Number} end The last record index to include (defaults to length - 1)
11544      * @return {Number} The sum
11545      */
11546     sum : function(property, start, end){
11547         var rs = this.data.items, v = 0;
11548         start = start || 0;
11549         end = (end || end === 0) ? end : rs.length-1;
11550
11551         for(var i = start; i <= end; i++){
11552             v += (rs[i].data[property] || 0);
11553         }
11554         return v;
11555     },
11556
11557     /**
11558      * Filter the records by a specified property.
11559      * @param {String} field A field on your records
11560      * @param {String/RegExp} value Either a string that the field
11561      * should start with or a RegExp to test against the field
11562      * @param {Boolean} anyMatch True to match any part not just the beginning
11563      */
11564     filter : function(property, value, anyMatch){
11565         var fn = this.createFilterFn(property, value, anyMatch);
11566         return fn ? this.filterBy(fn) : this.clearFilter();
11567     },
11568
11569     /**
11570      * Filter by a function. The specified function will be called with each
11571      * record in this data source. If the function returns true the record is included,
11572      * otherwise it is filtered.
11573      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11574      * @param {Object} scope (optional) The scope of the function (defaults to this)
11575      */
11576     filterBy : function(fn, scope){
11577         this.snapshot = this.snapshot || this.data;
11578         this.data = this.queryBy(fn, scope||this);
11579         this.fireEvent("datachanged", this);
11580     },
11581
11582     /**
11583      * Query the records by a specified property.
11584      * @param {String} field A field on your records
11585      * @param {String/RegExp} value Either a string that the field
11586      * should start with or a RegExp to test against the field
11587      * @param {Boolean} anyMatch True to match any part not just the beginning
11588      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11589      */
11590     query : function(property, value, anyMatch){
11591         var fn = this.createFilterFn(property, value, anyMatch);
11592         return fn ? this.queryBy(fn) : this.data.clone();
11593     },
11594
11595     /**
11596      * Query by a function. The specified function will be called with each
11597      * record in this data source. If the function returns true the record is included
11598      * in the results.
11599      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11600      * @param {Object} scope (optional) The scope of the function (defaults to this)
11601       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11602      **/
11603     queryBy : function(fn, scope){
11604         var data = this.snapshot || this.data;
11605         return data.filterBy(fn, scope||this);
11606     },
11607
11608     /**
11609      * Collects unique values for a particular dataIndex from this store.
11610      * @param {String} dataIndex The property to collect
11611      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11612      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11613      * @return {Array} An array of the unique values
11614      **/
11615     collect : function(dataIndex, allowNull, bypassFilter){
11616         var d = (bypassFilter === true && this.snapshot) ?
11617                 this.snapshot.items : this.data.items;
11618         var v, sv, r = [], l = {};
11619         for(var i = 0, len = d.length; i < len; i++){
11620             v = d[i].data[dataIndex];
11621             sv = String(v);
11622             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11623                 l[sv] = true;
11624                 r[r.length] = v;
11625             }
11626         }
11627         return r;
11628     },
11629
11630     /**
11631      * Revert to a view of the Record cache with no filtering applied.
11632      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11633      */
11634     clearFilter : function(suppressEvent){
11635         if(this.snapshot && this.snapshot != this.data){
11636             this.data = this.snapshot;
11637             delete this.snapshot;
11638             if(suppressEvent !== true){
11639                 this.fireEvent("datachanged", this);
11640             }
11641         }
11642     },
11643
11644     // private
11645     afterEdit : function(record){
11646         if(this.modified.indexOf(record) == -1){
11647             this.modified.push(record);
11648         }
11649         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11650     },
11651     
11652     // private
11653     afterReject : function(record){
11654         this.modified.remove(record);
11655         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11656     },
11657
11658     // private
11659     afterCommit : function(record){
11660         this.modified.remove(record);
11661         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11662     },
11663
11664     /**
11665      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11666      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11667      */
11668     commitChanges : function(){
11669         var m = this.modified.slice(0);
11670         this.modified = [];
11671         for(var i = 0, len = m.length; i < len; i++){
11672             m[i].commit();
11673         }
11674     },
11675
11676     /**
11677      * Cancel outstanding changes on all changed records.
11678      */
11679     rejectChanges : function(){
11680         var m = this.modified.slice(0);
11681         this.modified = [];
11682         for(var i = 0, len = m.length; i < len; i++){
11683             m[i].reject();
11684         }
11685     },
11686
11687     onMetaChange : function(meta, rtype, o){
11688         this.recordType = rtype;
11689         this.fields = rtype.prototype.fields;
11690         delete this.snapshot;
11691         this.sortInfo = meta.sortInfo || this.sortInfo;
11692         this.modified = [];
11693         this.fireEvent('metachange', this, this.reader.meta);
11694     },
11695     
11696     moveIndex : function(data, type)
11697     {
11698         var index = this.indexOf(data);
11699         
11700         var newIndex = index + type;
11701         
11702         this.remove(data);
11703         
11704         this.insert(newIndex, data);
11705         
11706     }
11707 });/*
11708  * Based on:
11709  * Ext JS Library 1.1.1
11710  * Copyright(c) 2006-2007, Ext JS, LLC.
11711  *
11712  * Originally Released Under LGPL - original licence link has changed is not relivant.
11713  *
11714  * Fork - LGPL
11715  * <script type="text/javascript">
11716  */
11717
11718 /**
11719  * @class Roo.data.SimpleStore
11720  * @extends Roo.data.Store
11721  * Small helper class to make creating Stores from Array data easier.
11722  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11723  * @cfg {Array} fields An array of field definition objects, or field name strings.
11724  * @cfg {Array} data The multi-dimensional array of data
11725  * @constructor
11726  * @param {Object} config
11727  */
11728 Roo.data.SimpleStore = function(config){
11729     Roo.data.SimpleStore.superclass.constructor.call(this, {
11730         isLocal : true,
11731         reader: new Roo.data.ArrayReader({
11732                 id: config.id
11733             },
11734             Roo.data.Record.create(config.fields)
11735         ),
11736         proxy : new Roo.data.MemoryProxy(config.data)
11737     });
11738     this.load();
11739 };
11740 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11741  * Based on:
11742  * Ext JS Library 1.1.1
11743  * Copyright(c) 2006-2007, Ext JS, LLC.
11744  *
11745  * Originally Released Under LGPL - original licence link has changed is not relivant.
11746  *
11747  * Fork - LGPL
11748  * <script type="text/javascript">
11749  */
11750
11751 /**
11752 /**
11753  * @extends Roo.data.Store
11754  * @class Roo.data.JsonStore
11755  * Small helper class to make creating Stores for JSON data easier. <br/>
11756 <pre><code>
11757 var store = new Roo.data.JsonStore({
11758     url: 'get-images.php',
11759     root: 'images',
11760     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11761 });
11762 </code></pre>
11763  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11764  * JsonReader and HttpProxy (unless inline data is provided).</b>
11765  * @cfg {Array} fields An array of field definition objects, or field name strings.
11766  * @constructor
11767  * @param {Object} config
11768  */
11769 Roo.data.JsonStore = function(c){
11770     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11771         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11772         reader: new Roo.data.JsonReader(c, c.fields)
11773     }));
11774 };
11775 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11776  * Based on:
11777  * Ext JS Library 1.1.1
11778  * Copyright(c) 2006-2007, Ext JS, LLC.
11779  *
11780  * Originally Released Under LGPL - original licence link has changed is not relivant.
11781  *
11782  * Fork - LGPL
11783  * <script type="text/javascript">
11784  */
11785
11786  
11787 Roo.data.Field = function(config){
11788     if(typeof config == "string"){
11789         config = {name: config};
11790     }
11791     Roo.apply(this, config);
11792     
11793     if(!this.type){
11794         this.type = "auto";
11795     }
11796     
11797     var st = Roo.data.SortTypes;
11798     // named sortTypes are supported, here we look them up
11799     if(typeof this.sortType == "string"){
11800         this.sortType = st[this.sortType];
11801     }
11802     
11803     // set default sortType for strings and dates
11804     if(!this.sortType){
11805         switch(this.type){
11806             case "string":
11807                 this.sortType = st.asUCString;
11808                 break;
11809             case "date":
11810                 this.sortType = st.asDate;
11811                 break;
11812             default:
11813                 this.sortType = st.none;
11814         }
11815     }
11816
11817     // define once
11818     var stripRe = /[\$,%]/g;
11819
11820     // prebuilt conversion function for this field, instead of
11821     // switching every time we're reading a value
11822     if(!this.convert){
11823         var cv, dateFormat = this.dateFormat;
11824         switch(this.type){
11825             case "":
11826             case "auto":
11827             case undefined:
11828                 cv = function(v){ return v; };
11829                 break;
11830             case "string":
11831                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11832                 break;
11833             case "int":
11834                 cv = function(v){
11835                     return v !== undefined && v !== null && v !== '' ?
11836                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11837                     };
11838                 break;
11839             case "float":
11840                 cv = function(v){
11841                     return v !== undefined && v !== null && v !== '' ?
11842                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11843                     };
11844                 break;
11845             case "bool":
11846             case "boolean":
11847                 cv = function(v){ return v === true || v === "true" || v == 1; };
11848                 break;
11849             case "date":
11850                 cv = function(v){
11851                     if(!v){
11852                         return '';
11853                     }
11854                     if(v instanceof Date){
11855                         return v;
11856                     }
11857                     if(dateFormat){
11858                         if(dateFormat == "timestamp"){
11859                             return new Date(v*1000);
11860                         }
11861                         return Date.parseDate(v, dateFormat);
11862                     }
11863                     var parsed = Date.parse(v);
11864                     return parsed ? new Date(parsed) : null;
11865                 };
11866              break;
11867             
11868         }
11869         this.convert = cv;
11870     }
11871 };
11872
11873 Roo.data.Field.prototype = {
11874     dateFormat: null,
11875     defaultValue: "",
11876     mapping: null,
11877     sortType : null,
11878     sortDir : "ASC"
11879 };/*
11880  * Based on:
11881  * Ext JS Library 1.1.1
11882  * Copyright(c) 2006-2007, Ext JS, LLC.
11883  *
11884  * Originally Released Under LGPL - original licence link has changed is not relivant.
11885  *
11886  * Fork - LGPL
11887  * <script type="text/javascript">
11888  */
11889  
11890 // Base class for reading structured data from a data source.  This class is intended to be
11891 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11892
11893 /**
11894  * @class Roo.data.DataReader
11895  * Base class for reading structured data from a data source.  This class is intended to be
11896  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11897  */
11898
11899 Roo.data.DataReader = function(meta, recordType){
11900     
11901     this.meta = meta;
11902     
11903     this.recordType = recordType instanceof Array ? 
11904         Roo.data.Record.create(recordType) : recordType;
11905 };
11906
11907 Roo.data.DataReader.prototype = {
11908      /**
11909      * Create an empty record
11910      * @param {Object} data (optional) - overlay some values
11911      * @return {Roo.data.Record} record created.
11912      */
11913     newRow :  function(d) {
11914         var da =  {};
11915         this.recordType.prototype.fields.each(function(c) {
11916             switch( c.type) {
11917                 case 'int' : da[c.name] = 0; break;
11918                 case 'date' : da[c.name] = new Date(); break;
11919                 case 'float' : da[c.name] = 0.0; break;
11920                 case 'boolean' : da[c.name] = false; break;
11921                 default : da[c.name] = ""; break;
11922             }
11923             
11924         });
11925         return new this.recordType(Roo.apply(da, d));
11926     }
11927     
11928 };/*
11929  * Based on:
11930  * Ext JS Library 1.1.1
11931  * Copyright(c) 2006-2007, Ext JS, LLC.
11932  *
11933  * Originally Released Under LGPL - original licence link has changed is not relivant.
11934  *
11935  * Fork - LGPL
11936  * <script type="text/javascript">
11937  */
11938
11939 /**
11940  * @class Roo.data.DataProxy
11941  * @extends Roo.data.Observable
11942  * This class is an abstract base class for implementations which provide retrieval of
11943  * unformatted data objects.<br>
11944  * <p>
11945  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11946  * (of the appropriate type which knows how to parse the data object) to provide a block of
11947  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11948  * <p>
11949  * Custom implementations must implement the load method as described in
11950  * {@link Roo.data.HttpProxy#load}.
11951  */
11952 Roo.data.DataProxy = function(){
11953     this.addEvents({
11954         /**
11955          * @event beforeload
11956          * Fires before a network request is made to retrieve a data object.
11957          * @param {Object} This DataProxy object.
11958          * @param {Object} params The params parameter to the load function.
11959          */
11960         beforeload : true,
11961         /**
11962          * @event load
11963          * Fires before the load method's callback is called.
11964          * @param {Object} This DataProxy object.
11965          * @param {Object} o The data object.
11966          * @param {Object} arg The callback argument object passed to the load function.
11967          */
11968         load : true,
11969         /**
11970          * @event loadexception
11971          * Fires if an Exception occurs during data retrieval.
11972          * @param {Object} This DataProxy object.
11973          * @param {Object} o The data object.
11974          * @param {Object} arg The callback argument object passed to the load function.
11975          * @param {Object} e The Exception.
11976          */
11977         loadexception : true
11978     });
11979     Roo.data.DataProxy.superclass.constructor.call(this);
11980 };
11981
11982 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11983
11984     /**
11985      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11986      */
11987 /*
11988  * Based on:
11989  * Ext JS Library 1.1.1
11990  * Copyright(c) 2006-2007, Ext JS, LLC.
11991  *
11992  * Originally Released Under LGPL - original licence link has changed is not relivant.
11993  *
11994  * Fork - LGPL
11995  * <script type="text/javascript">
11996  */
11997 /**
11998  * @class Roo.data.MemoryProxy
11999  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12000  * to the Reader when its load method is called.
12001  * @constructor
12002  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12003  */
12004 Roo.data.MemoryProxy = function(data){
12005     if (data.data) {
12006         data = data.data;
12007     }
12008     Roo.data.MemoryProxy.superclass.constructor.call(this);
12009     this.data = data;
12010 };
12011
12012 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12013     
12014     /**
12015      * Load data from the requested source (in this case an in-memory
12016      * data object passed to the constructor), read the data object into
12017      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12018      * process that block using the passed callback.
12019      * @param {Object} params This parameter is not used by the MemoryProxy class.
12020      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12021      * object into a block of Roo.data.Records.
12022      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12023      * The function must be passed <ul>
12024      * <li>The Record block object</li>
12025      * <li>The "arg" argument from the load function</li>
12026      * <li>A boolean success indicator</li>
12027      * </ul>
12028      * @param {Object} scope The scope in which to call the callback
12029      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12030      */
12031     load : function(params, reader, callback, scope, arg){
12032         params = params || {};
12033         var result;
12034         try {
12035             result = reader.readRecords(this.data);
12036         }catch(e){
12037             this.fireEvent("loadexception", this, arg, null, e);
12038             callback.call(scope, null, arg, false);
12039             return;
12040         }
12041         callback.call(scope, result, arg, true);
12042     },
12043     
12044     // private
12045     update : function(params, records){
12046         
12047     }
12048 });/*
12049  * Based on:
12050  * Ext JS Library 1.1.1
12051  * Copyright(c) 2006-2007, Ext JS, LLC.
12052  *
12053  * Originally Released Under LGPL - original licence link has changed is not relivant.
12054  *
12055  * Fork - LGPL
12056  * <script type="text/javascript">
12057  */
12058 /**
12059  * @class Roo.data.HttpProxy
12060  * @extends Roo.data.DataProxy
12061  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12062  * configured to reference a certain URL.<br><br>
12063  * <p>
12064  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12065  * from which the running page was served.<br><br>
12066  * <p>
12067  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12068  * <p>
12069  * Be aware that to enable the browser to parse an XML document, the server must set
12070  * the Content-Type header in the HTTP response to "text/xml".
12071  * @constructor
12072  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12073  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12074  * will be used to make the request.
12075  */
12076 Roo.data.HttpProxy = function(conn){
12077     Roo.data.HttpProxy.superclass.constructor.call(this);
12078     // is conn a conn config or a real conn?
12079     this.conn = conn;
12080     this.useAjax = !conn || !conn.events;
12081   
12082 };
12083
12084 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12085     // thse are take from connection...
12086     
12087     /**
12088      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12089      */
12090     /**
12091      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12092      * extra parameters to each request made by this object. (defaults to undefined)
12093      */
12094     /**
12095      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12096      *  to each request made by this object. (defaults to undefined)
12097      */
12098     /**
12099      * @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)
12100      */
12101     /**
12102      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12103      */
12104      /**
12105      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12106      * @type Boolean
12107      */
12108   
12109
12110     /**
12111      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12112      * @type Boolean
12113      */
12114     /**
12115      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12116      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12117      * a finer-grained basis than the DataProxy events.
12118      */
12119     getConnection : function(){
12120         return this.useAjax ? Roo.Ajax : this.conn;
12121     },
12122
12123     /**
12124      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12125      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12126      * process that block using the passed callback.
12127      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12128      * for the request to the remote server.
12129      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12130      * object into a block of Roo.data.Records.
12131      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12132      * The function must be passed <ul>
12133      * <li>The Record block object</li>
12134      * <li>The "arg" argument from the load function</li>
12135      * <li>A boolean success indicator</li>
12136      * </ul>
12137      * @param {Object} scope The scope in which to call the callback
12138      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12139      */
12140     load : function(params, reader, callback, scope, arg){
12141         if(this.fireEvent("beforeload", this, params) !== false){
12142             var  o = {
12143                 params : params || {},
12144                 request: {
12145                     callback : callback,
12146                     scope : scope,
12147                     arg : arg
12148                 },
12149                 reader: reader,
12150                 callback : this.loadResponse,
12151                 scope: this
12152             };
12153             if(this.useAjax){
12154                 Roo.applyIf(o, this.conn);
12155                 if(this.activeRequest){
12156                     Roo.Ajax.abort(this.activeRequest);
12157                 }
12158                 this.activeRequest = Roo.Ajax.request(o);
12159             }else{
12160                 this.conn.request(o);
12161             }
12162         }else{
12163             callback.call(scope||this, null, arg, false);
12164         }
12165     },
12166
12167     // private
12168     loadResponse : function(o, success, response){
12169         delete this.activeRequest;
12170         if(!success){
12171             this.fireEvent("loadexception", this, o, response);
12172             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12173             return;
12174         }
12175         var result;
12176         try {
12177             result = o.reader.read(response);
12178         }catch(e){
12179             this.fireEvent("loadexception", this, o, response, e);
12180             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12181             return;
12182         }
12183         
12184         this.fireEvent("load", this, o, o.request.arg);
12185         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12186     },
12187
12188     // private
12189     update : function(dataSet){
12190
12191     },
12192
12193     // private
12194     updateResponse : function(dataSet){
12195
12196     }
12197 });/*
12198  * Based on:
12199  * Ext JS Library 1.1.1
12200  * Copyright(c) 2006-2007, Ext JS, LLC.
12201  *
12202  * Originally Released Under LGPL - original licence link has changed is not relivant.
12203  *
12204  * Fork - LGPL
12205  * <script type="text/javascript">
12206  */
12207
12208 /**
12209  * @class Roo.data.ScriptTagProxy
12210  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12211  * other than the originating domain of the running page.<br><br>
12212  * <p>
12213  * <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
12214  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12215  * <p>
12216  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12217  * source code that is used as the source inside a &lt;script> tag.<br><br>
12218  * <p>
12219  * In order for the browser to process the returned data, the server must wrap the data object
12220  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12221  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12222  * depending on whether the callback name was passed:
12223  * <p>
12224  * <pre><code>
12225 boolean scriptTag = false;
12226 String cb = request.getParameter("callback");
12227 if (cb != null) {
12228     scriptTag = true;
12229     response.setContentType("text/javascript");
12230 } else {
12231     response.setContentType("application/x-json");
12232 }
12233 Writer out = response.getWriter();
12234 if (scriptTag) {
12235     out.write(cb + "(");
12236 }
12237 out.print(dataBlock.toJsonString());
12238 if (scriptTag) {
12239     out.write(");");
12240 }
12241 </pre></code>
12242  *
12243  * @constructor
12244  * @param {Object} config A configuration object.
12245  */
12246 Roo.data.ScriptTagProxy = function(config){
12247     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12248     Roo.apply(this, config);
12249     this.head = document.getElementsByTagName("head")[0];
12250 };
12251
12252 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12253
12254 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12255     /**
12256      * @cfg {String} url The URL from which to request the data object.
12257      */
12258     /**
12259      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12260      */
12261     timeout : 30000,
12262     /**
12263      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12264      * the server the name of the callback function set up by the load call to process the returned data object.
12265      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12266      * javascript output which calls this named function passing the data object as its only parameter.
12267      */
12268     callbackParam : "callback",
12269     /**
12270      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12271      * name to the request.
12272      */
12273     nocache : true,
12274
12275     /**
12276      * Load data from the configured URL, read the data object into
12277      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12278      * process that block using the passed callback.
12279      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12280      * for the request to the remote server.
12281      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12282      * object into a block of Roo.data.Records.
12283      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12284      * The function must be passed <ul>
12285      * <li>The Record block object</li>
12286      * <li>The "arg" argument from the load function</li>
12287      * <li>A boolean success indicator</li>
12288      * </ul>
12289      * @param {Object} scope The scope in which to call the callback
12290      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12291      */
12292     load : function(params, reader, callback, scope, arg){
12293         if(this.fireEvent("beforeload", this, params) !== false){
12294
12295             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12296
12297             var url = this.url;
12298             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12299             if(this.nocache){
12300                 url += "&_dc=" + (new Date().getTime());
12301             }
12302             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12303             var trans = {
12304                 id : transId,
12305                 cb : "stcCallback"+transId,
12306                 scriptId : "stcScript"+transId,
12307                 params : params,
12308                 arg : arg,
12309                 url : url,
12310                 callback : callback,
12311                 scope : scope,
12312                 reader : reader
12313             };
12314             var conn = this;
12315
12316             window[trans.cb] = function(o){
12317                 conn.handleResponse(o, trans);
12318             };
12319
12320             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12321
12322             if(this.autoAbort !== false){
12323                 this.abort();
12324             }
12325
12326             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12327
12328             var script = document.createElement("script");
12329             script.setAttribute("src", url);
12330             script.setAttribute("type", "text/javascript");
12331             script.setAttribute("id", trans.scriptId);
12332             this.head.appendChild(script);
12333
12334             this.trans = trans;
12335         }else{
12336             callback.call(scope||this, null, arg, false);
12337         }
12338     },
12339
12340     // private
12341     isLoading : function(){
12342         return this.trans ? true : false;
12343     },
12344
12345     /**
12346      * Abort the current server request.
12347      */
12348     abort : function(){
12349         if(this.isLoading()){
12350             this.destroyTrans(this.trans);
12351         }
12352     },
12353
12354     // private
12355     destroyTrans : function(trans, isLoaded){
12356         this.head.removeChild(document.getElementById(trans.scriptId));
12357         clearTimeout(trans.timeoutId);
12358         if(isLoaded){
12359             window[trans.cb] = undefined;
12360             try{
12361                 delete window[trans.cb];
12362             }catch(e){}
12363         }else{
12364             // if hasn't been loaded, wait for load to remove it to prevent script error
12365             window[trans.cb] = function(){
12366                 window[trans.cb] = undefined;
12367                 try{
12368                     delete window[trans.cb];
12369                 }catch(e){}
12370             };
12371         }
12372     },
12373
12374     // private
12375     handleResponse : function(o, trans){
12376         this.trans = false;
12377         this.destroyTrans(trans, true);
12378         var result;
12379         try {
12380             result = trans.reader.readRecords(o);
12381         }catch(e){
12382             this.fireEvent("loadexception", this, o, trans.arg, e);
12383             trans.callback.call(trans.scope||window, null, trans.arg, false);
12384             return;
12385         }
12386         this.fireEvent("load", this, o, trans.arg);
12387         trans.callback.call(trans.scope||window, result, trans.arg, true);
12388     },
12389
12390     // private
12391     handleFailure : function(trans){
12392         this.trans = false;
12393         this.destroyTrans(trans, false);
12394         this.fireEvent("loadexception", this, null, trans.arg);
12395         trans.callback.call(trans.scope||window, null, trans.arg, false);
12396     }
12397 });/*
12398  * Based on:
12399  * Ext JS Library 1.1.1
12400  * Copyright(c) 2006-2007, Ext JS, LLC.
12401  *
12402  * Originally Released Under LGPL - original licence link has changed is not relivant.
12403  *
12404  * Fork - LGPL
12405  * <script type="text/javascript">
12406  */
12407
12408 /**
12409  * @class Roo.data.JsonReader
12410  * @extends Roo.data.DataReader
12411  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12412  * based on mappings in a provided Roo.data.Record constructor.
12413  * 
12414  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12415  * in the reply previously. 
12416  * 
12417  * <p>
12418  * Example code:
12419  * <pre><code>
12420 var RecordDef = Roo.data.Record.create([
12421     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12422     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12423 ]);
12424 var myReader = new Roo.data.JsonReader({
12425     totalProperty: "results",    // The property which contains the total dataset size (optional)
12426     root: "rows",                // The property which contains an Array of row objects
12427     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12428 }, RecordDef);
12429 </code></pre>
12430  * <p>
12431  * This would consume a JSON file like this:
12432  * <pre><code>
12433 { 'results': 2, 'rows': [
12434     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12435     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12436 }
12437 </code></pre>
12438  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12439  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12440  * paged from the remote server.
12441  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12442  * @cfg {String} root name of the property which contains the Array of row objects.
12443  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12444  * @cfg {Array} fields Array of field definition objects
12445  * @constructor
12446  * Create a new JsonReader
12447  * @param {Object} meta Metadata configuration options
12448  * @param {Object} recordType Either an Array of field definition objects,
12449  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12450  */
12451 Roo.data.JsonReader = function(meta, recordType){
12452     
12453     meta = meta || {};
12454     // set some defaults:
12455     Roo.applyIf(meta, {
12456         totalProperty: 'total',
12457         successProperty : 'success',
12458         root : 'data',
12459         id : 'id'
12460     });
12461     
12462     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12463 };
12464 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12465     
12466     /**
12467      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12468      * Used by Store query builder to append _requestMeta to params.
12469      * 
12470      */
12471     metaFromRemote : false,
12472     /**
12473      * This method is only used by a DataProxy which has retrieved data from a remote server.
12474      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12475      * @return {Object} data A data block which is used by an Roo.data.Store object as
12476      * a cache of Roo.data.Records.
12477      */
12478     read : function(response){
12479         var json = response.responseText;
12480        
12481         var o = /* eval:var:o */ eval("("+json+")");
12482         if(!o) {
12483             throw {message: "JsonReader.read: Json object not found"};
12484         }
12485         
12486         if(o.metaData){
12487             
12488             delete this.ef;
12489             this.metaFromRemote = true;
12490             this.meta = o.metaData;
12491             this.recordType = Roo.data.Record.create(o.metaData.fields);
12492             this.onMetaChange(this.meta, this.recordType, o);
12493         }
12494         return this.readRecords(o);
12495     },
12496
12497     // private function a store will implement
12498     onMetaChange : function(meta, recordType, o){
12499
12500     },
12501
12502     /**
12503          * @ignore
12504          */
12505     simpleAccess: function(obj, subsc) {
12506         return obj[subsc];
12507     },
12508
12509         /**
12510          * @ignore
12511          */
12512     getJsonAccessor: function(){
12513         var re = /[\[\.]/;
12514         return function(expr) {
12515             try {
12516                 return(re.test(expr))
12517                     ? new Function("obj", "return obj." + expr)
12518                     : function(obj){
12519                         return obj[expr];
12520                     };
12521             } catch(e){}
12522             return Roo.emptyFn;
12523         };
12524     }(),
12525
12526     /**
12527      * Create a data block containing Roo.data.Records from an XML document.
12528      * @param {Object} o An object which contains an Array of row objects in the property specified
12529      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12530      * which contains the total size of the dataset.
12531      * @return {Object} data A data block which is used by an Roo.data.Store object as
12532      * a cache of Roo.data.Records.
12533      */
12534     readRecords : function(o){
12535         /**
12536          * After any data loads, the raw JSON data is available for further custom processing.
12537          * @type Object
12538          */
12539         this.o = o;
12540         var s = this.meta, Record = this.recordType,
12541             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12542
12543 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12544         if (!this.ef) {
12545             if(s.totalProperty) {
12546                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12547                 }
12548                 if(s.successProperty) {
12549                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12550                 }
12551                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12552                 if (s.id) {
12553                         var g = this.getJsonAccessor(s.id);
12554                         this.getId = function(rec) {
12555                                 var r = g(rec);  
12556                                 return (r === undefined || r === "") ? null : r;
12557                         };
12558                 } else {
12559                         this.getId = function(){return null;};
12560                 }
12561             this.ef = [];
12562             for(var jj = 0; jj < fl; jj++){
12563                 f = fi[jj];
12564                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12565                 this.ef[jj] = this.getJsonAccessor(map);
12566             }
12567         }
12568
12569         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12570         if(s.totalProperty){
12571             var vt = parseInt(this.getTotal(o), 10);
12572             if(!isNaN(vt)){
12573                 totalRecords = vt;
12574             }
12575         }
12576         if(s.successProperty){
12577             var vs = this.getSuccess(o);
12578             if(vs === false || vs === 'false'){
12579                 success = false;
12580             }
12581         }
12582         var records = [];
12583         for(var i = 0; i < c; i++){
12584                 var n = root[i];
12585             var values = {};
12586             var id = this.getId(n);
12587             for(var j = 0; j < fl; j++){
12588                 f = fi[j];
12589             var v = this.ef[j](n);
12590             if (!f.convert) {
12591                 Roo.log('missing convert for ' + f.name);
12592                 Roo.log(f);
12593                 continue;
12594             }
12595             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12596             }
12597             var record = new Record(values, id);
12598             record.json = n;
12599             records[i] = record;
12600         }
12601         return {
12602             raw : o,
12603             success : success,
12604             records : records,
12605             totalRecords : totalRecords
12606         };
12607     }
12608 });/*
12609  * Based on:
12610  * Ext JS Library 1.1.1
12611  * Copyright(c) 2006-2007, Ext JS, LLC.
12612  *
12613  * Originally Released Under LGPL - original licence link has changed is not relivant.
12614  *
12615  * Fork - LGPL
12616  * <script type="text/javascript">
12617  */
12618
12619 /**
12620  * @class Roo.data.ArrayReader
12621  * @extends Roo.data.DataReader
12622  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12623  * Each element of that Array represents a row of data fields. The
12624  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12625  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12626  * <p>
12627  * Example code:.
12628  * <pre><code>
12629 var RecordDef = Roo.data.Record.create([
12630     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12631     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12632 ]);
12633 var myReader = new Roo.data.ArrayReader({
12634     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12635 }, RecordDef);
12636 </code></pre>
12637  * <p>
12638  * This would consume an Array like this:
12639  * <pre><code>
12640 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12641   </code></pre>
12642  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12643  * @constructor
12644  * Create a new JsonReader
12645  * @param {Object} meta Metadata configuration options.
12646  * @param {Object} recordType Either an Array of field definition objects
12647  * as specified to {@link Roo.data.Record#create},
12648  * or an {@link Roo.data.Record} object
12649  * created using {@link Roo.data.Record#create}.
12650  */
12651 Roo.data.ArrayReader = function(meta, recordType){
12652     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12653 };
12654
12655 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12656     /**
12657      * Create a data block containing Roo.data.Records from an XML document.
12658      * @param {Object} o An Array of row objects which represents the dataset.
12659      * @return {Object} data A data block which is used by an Roo.data.Store object as
12660      * a cache of Roo.data.Records.
12661      */
12662     readRecords : function(o){
12663         var sid = this.meta ? this.meta.id : null;
12664         var recordType = this.recordType, fields = recordType.prototype.fields;
12665         var records = [];
12666         var root = o;
12667             for(var i = 0; i < root.length; i++){
12668                     var n = root[i];
12669                 var values = {};
12670                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12671                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12672                 var f = fields.items[j];
12673                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12674                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12675                 v = f.convert(v);
12676                 values[f.name] = v;
12677             }
12678                 var record = new recordType(values, id);
12679                 record.json = n;
12680                 records[records.length] = record;
12681             }
12682             return {
12683                 records : records,
12684                 totalRecords : records.length
12685             };
12686     }
12687 });/*
12688  * - LGPL
12689  * * 
12690  */
12691
12692 /**
12693  * @class Roo.bootstrap.ComboBox
12694  * @extends Roo.bootstrap.TriggerField
12695  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12696  * @cfg {Boolean} append (true|false) default false
12697  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12698  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12699  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12700  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12701  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12702  * @cfg {Boolean} animate default true
12703  * @cfg {Boolean} emptyResultText only for touch device
12704  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12705  * @cfg {String} emptyTitle default ''
12706  * @constructor
12707  * Create a new ComboBox.
12708  * @param {Object} config Configuration options
12709  */
12710 Roo.bootstrap.ComboBox = function(config){
12711     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12712     this.addEvents({
12713         /**
12714          * @event expand
12715          * Fires when the dropdown list is expanded
12716         * @param {Roo.bootstrap.ComboBox} combo This combo box
12717         */
12718         'expand' : true,
12719         /**
12720          * @event collapse
12721          * Fires when the dropdown list is collapsed
12722         * @param {Roo.bootstrap.ComboBox} combo This combo box
12723         */
12724         'collapse' : true,
12725         /**
12726          * @event beforeselect
12727          * Fires before a list item is selected. Return false to cancel the selection.
12728         * @param {Roo.bootstrap.ComboBox} combo This combo box
12729         * @param {Roo.data.Record} record The data record returned from the underlying store
12730         * @param {Number} index The index of the selected item in the dropdown list
12731         */
12732         'beforeselect' : true,
12733         /**
12734          * @event select
12735          * Fires when a list item is selected
12736         * @param {Roo.bootstrap.ComboBox} combo This combo box
12737         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12738         * @param {Number} index The index of the selected item in the dropdown list
12739         */
12740         'select' : true,
12741         /**
12742          * @event beforequery
12743          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12744          * The event object passed has these properties:
12745         * @param {Roo.bootstrap.ComboBox} combo This combo box
12746         * @param {String} query The query
12747         * @param {Boolean} forceAll true to force "all" query
12748         * @param {Boolean} cancel true to cancel the query
12749         * @param {Object} e The query event object
12750         */
12751         'beforequery': true,
12752          /**
12753          * @event add
12754          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12755         * @param {Roo.bootstrap.ComboBox} combo This combo box
12756         */
12757         'add' : true,
12758         /**
12759          * @event edit
12760          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12761         * @param {Roo.bootstrap.ComboBox} combo This combo box
12762         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12763         */
12764         'edit' : true,
12765         /**
12766          * @event remove
12767          * Fires when the remove value from the combobox array
12768         * @param {Roo.bootstrap.ComboBox} combo This combo box
12769         */
12770         'remove' : true,
12771         /**
12772          * @event afterremove
12773          * Fires when the remove value from the combobox array
12774         * @param {Roo.bootstrap.ComboBox} combo This combo box
12775         */
12776         'afterremove' : true,
12777         /**
12778          * @event specialfilter
12779          * Fires when specialfilter
12780             * @param {Roo.bootstrap.ComboBox} combo This combo box
12781             */
12782         'specialfilter' : true,
12783         /**
12784          * @event tick
12785          * Fires when tick the element
12786             * @param {Roo.bootstrap.ComboBox} combo This combo box
12787             */
12788         'tick' : true,
12789         /**
12790          * @event touchviewdisplay
12791          * Fires when touch view require special display (default is using displayField)
12792             * @param {Roo.bootstrap.ComboBox} combo This combo box
12793             * @param {Object} cfg set html .
12794             */
12795         'touchviewdisplay' : true
12796         
12797     });
12798     
12799     this.item = [];
12800     this.tickItems = [];
12801     
12802     this.selectedIndex = -1;
12803     if(this.mode == 'local'){
12804         if(config.queryDelay === undefined){
12805             this.queryDelay = 10;
12806         }
12807         if(config.minChars === undefined){
12808             this.minChars = 0;
12809         }
12810     }
12811 };
12812
12813 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12814      
12815     /**
12816      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12817      * rendering into an Roo.Editor, defaults to false)
12818      */
12819     /**
12820      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12821      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12822      */
12823     /**
12824      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12825      */
12826     /**
12827      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12828      * the dropdown list (defaults to undefined, with no header element)
12829      */
12830
12831      /**
12832      * @cfg {String/Roo.Template} tpl The template to use to render the output
12833      */
12834      
12835      /**
12836      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12837      */
12838     listWidth: undefined,
12839     /**
12840      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12841      * mode = 'remote' or 'text' if mode = 'local')
12842      */
12843     displayField: undefined,
12844     
12845     /**
12846      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12847      * mode = 'remote' or 'value' if mode = 'local'). 
12848      * Note: use of a valueField requires the user make a selection
12849      * in order for a value to be mapped.
12850      */
12851     valueField: undefined,
12852     /**
12853      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12854      */
12855     modalTitle : '',
12856     
12857     /**
12858      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12859      * field's data value (defaults to the underlying DOM element's name)
12860      */
12861     hiddenName: undefined,
12862     /**
12863      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12864      */
12865     listClass: '',
12866     /**
12867      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12868      */
12869     selectedClass: 'active',
12870     
12871     /**
12872      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12873      */
12874     shadow:'sides',
12875     /**
12876      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12877      * anchor positions (defaults to 'tl-bl')
12878      */
12879     listAlign: 'tl-bl?',
12880     /**
12881      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12882      */
12883     maxHeight: 300,
12884     /**
12885      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12886      * query specified by the allQuery config option (defaults to 'query')
12887      */
12888     triggerAction: 'query',
12889     /**
12890      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12891      * (defaults to 4, does not apply if editable = false)
12892      */
12893     minChars : 4,
12894     /**
12895      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12896      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12897      */
12898     typeAhead: false,
12899     /**
12900      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12901      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12902      */
12903     queryDelay: 500,
12904     /**
12905      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12906      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12907      */
12908     pageSize: 0,
12909     /**
12910      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12911      * when editable = true (defaults to false)
12912      */
12913     selectOnFocus:false,
12914     /**
12915      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12916      */
12917     queryParam: 'query',
12918     /**
12919      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12920      * when mode = 'remote' (defaults to 'Loading...')
12921      */
12922     loadingText: 'Loading...',
12923     /**
12924      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12925      */
12926     resizable: false,
12927     /**
12928      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12929      */
12930     handleHeight : 8,
12931     /**
12932      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12933      * traditional select (defaults to true)
12934      */
12935     editable: true,
12936     /**
12937      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12938      */
12939     allQuery: '',
12940     /**
12941      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12942      */
12943     mode: 'remote',
12944     /**
12945      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12946      * listWidth has a higher value)
12947      */
12948     minListWidth : 70,
12949     /**
12950      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12951      * allow the user to set arbitrary text into the field (defaults to false)
12952      */
12953     forceSelection:false,
12954     /**
12955      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12956      * if typeAhead = true (defaults to 250)
12957      */
12958     typeAheadDelay : 250,
12959     /**
12960      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12961      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12962      */
12963     valueNotFoundText : undefined,
12964     /**
12965      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12966      */
12967     blockFocus : false,
12968     
12969     /**
12970      * @cfg {Boolean} disableClear Disable showing of clear button.
12971      */
12972     disableClear : false,
12973     /**
12974      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12975      */
12976     alwaysQuery : false,
12977     
12978     /**
12979      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12980      */
12981     multiple : false,
12982     
12983     /**
12984      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12985      */
12986     invalidClass : "has-warning",
12987     
12988     /**
12989      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12990      */
12991     validClass : "has-success",
12992     
12993     /**
12994      * @cfg {Boolean} specialFilter (true|false) special filter default false
12995      */
12996     specialFilter : false,
12997     
12998     /**
12999      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13000      */
13001     mobileTouchView : true,
13002     
13003     /**
13004      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13005      */
13006     useNativeIOS : false,
13007     
13008     ios_options : false,
13009     
13010     //private
13011     addicon : false,
13012     editicon: false,
13013     
13014     page: 0,
13015     hasQuery: false,
13016     append: false,
13017     loadNext: false,
13018     autoFocus : true,
13019     tickable : false,
13020     btnPosition : 'right',
13021     triggerList : true,
13022     showToggleBtn : true,
13023     animate : true,
13024     emptyResultText: 'Empty',
13025     triggerText : 'Select',
13026     emptyTitle : '',
13027     
13028     // element that contains real text value.. (when hidden is used..)
13029     
13030     getAutoCreate : function()
13031     {   
13032         var cfg = false;
13033         //render
13034         /*
13035          * Render classic select for iso
13036          */
13037         
13038         if(Roo.isIOS && this.useNativeIOS){
13039             cfg = this.getAutoCreateNativeIOS();
13040             return cfg;
13041         }
13042         
13043         /*
13044          * Touch Devices
13045          */
13046         
13047         if(Roo.isTouch && this.mobileTouchView){
13048             cfg = this.getAutoCreateTouchView();
13049             return cfg;;
13050         }
13051         
13052         /*
13053          *  Normal ComboBox
13054          */
13055         if(!this.tickable){
13056             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13057             return cfg;
13058         }
13059         
13060         /*
13061          *  ComboBox with tickable selections
13062          */
13063              
13064         var align = this.labelAlign || this.parentLabelAlign();
13065         
13066         cfg = {
13067             cls : 'form-group roo-combobox-tickable' //input-group
13068         };
13069         
13070         var btn_text_select = '';
13071         var btn_text_done = '';
13072         var btn_text_cancel = '';
13073         
13074         if (this.btn_text_show) {
13075             btn_text_select = 'Select';
13076             btn_text_done = 'Done';
13077             btn_text_cancel = 'Cancel'; 
13078         }
13079         
13080         var buttons = {
13081             tag : 'div',
13082             cls : 'tickable-buttons',
13083             cn : [
13084                 {
13085                     tag : 'button',
13086                     type : 'button',
13087                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13088                     //html : this.triggerText
13089                     html: btn_text_select
13090                 },
13091                 {
13092                     tag : 'button',
13093                     type : 'button',
13094                     name : 'ok',
13095                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13096                     //html : 'Done'
13097                     html: btn_text_done
13098                 },
13099                 {
13100                     tag : 'button',
13101                     type : 'button',
13102                     name : 'cancel',
13103                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13104                     //html : 'Cancel'
13105                     html: btn_text_cancel
13106                 }
13107             ]
13108         };
13109         
13110         if(this.editable){
13111             buttons.cn.unshift({
13112                 tag: 'input',
13113                 cls: 'roo-select2-search-field-input'
13114             });
13115         }
13116         
13117         var _this = this;
13118         
13119         Roo.each(buttons.cn, function(c){
13120             if (_this.size) {
13121                 c.cls += ' btn-' + _this.size;
13122             }
13123
13124             if (_this.disabled) {
13125                 c.disabled = true;
13126             }
13127         });
13128         
13129         var box = {
13130             tag: 'div',
13131             cn: [
13132                 {
13133                     tag: 'input',
13134                     type : 'hidden',
13135                     cls: 'form-hidden-field'
13136                 },
13137                 {
13138                     tag: 'ul',
13139                     cls: 'roo-select2-choices',
13140                     cn:[
13141                         {
13142                             tag: 'li',
13143                             cls: 'roo-select2-search-field',
13144                             cn: [
13145                                 buttons
13146                             ]
13147                         }
13148                     ]
13149                 }
13150             ]
13151         };
13152         
13153         var combobox = {
13154             cls: 'roo-select2-container input-group roo-select2-container-multi',
13155             cn: [
13156                 box
13157 //                {
13158 //                    tag: 'ul',
13159 //                    cls: 'typeahead typeahead-long dropdown-menu',
13160 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13161 //                }
13162             ]
13163         };
13164         
13165         if(this.hasFeedback && !this.allowBlank){
13166             
13167             var feedback = {
13168                 tag: 'span',
13169                 cls: 'glyphicon form-control-feedback'
13170             };
13171
13172             combobox.cn.push(feedback);
13173         }
13174         
13175         
13176         if (align ==='left' && this.fieldLabel.length) {
13177             
13178             cfg.cls += ' roo-form-group-label-left';
13179             
13180             cfg.cn = [
13181                 {
13182                     tag : 'i',
13183                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13184                     tooltip : 'This field is required'
13185                 },
13186                 {
13187                     tag: 'label',
13188                     'for' :  id,
13189                     cls : 'control-label',
13190                     html : this.fieldLabel
13191
13192                 },
13193                 {
13194                     cls : "", 
13195                     cn: [
13196                         combobox
13197                     ]
13198                 }
13199
13200             ];
13201             
13202             var labelCfg = cfg.cn[1];
13203             var contentCfg = cfg.cn[2];
13204             
13205
13206             if(this.indicatorpos == 'right'){
13207                 
13208                 cfg.cn = [
13209                     {
13210                         tag: 'label',
13211                         'for' :  id,
13212                         cls : 'control-label',
13213                         cn : [
13214                             {
13215                                 tag : 'span',
13216                                 html : this.fieldLabel
13217                             },
13218                             {
13219                                 tag : 'i',
13220                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13221                                 tooltip : 'This field is required'
13222                             }
13223                         ]
13224                     },
13225                     {
13226                         cls : "",
13227                         cn: [
13228                             combobox
13229                         ]
13230                     }
13231
13232                 ];
13233                 
13234                 
13235                 
13236                 labelCfg = cfg.cn[0];
13237                 contentCfg = cfg.cn[1];
13238             
13239             }
13240             
13241             if(this.labelWidth > 12){
13242                 labelCfg.style = "width: " + this.labelWidth + 'px';
13243             }
13244             
13245             if(this.labelWidth < 13 && this.labelmd == 0){
13246                 this.labelmd = this.labelWidth;
13247             }
13248             
13249             if(this.labellg > 0){
13250                 labelCfg.cls += ' col-lg-' + this.labellg;
13251                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13252             }
13253             
13254             if(this.labelmd > 0){
13255                 labelCfg.cls += ' col-md-' + this.labelmd;
13256                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13257             }
13258             
13259             if(this.labelsm > 0){
13260                 labelCfg.cls += ' col-sm-' + this.labelsm;
13261                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13262             }
13263             
13264             if(this.labelxs > 0){
13265                 labelCfg.cls += ' col-xs-' + this.labelxs;
13266                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13267             }
13268                 
13269                 
13270         } else if ( this.fieldLabel.length) {
13271 //                Roo.log(" label");
13272                  cfg.cn = [
13273                     {
13274                         tag : 'i',
13275                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13276                         tooltip : 'This field is required'
13277                     },
13278                     {
13279                         tag: 'label',
13280                         //cls : 'input-group-addon',
13281                         html : this.fieldLabel
13282                     },
13283                     combobox
13284                 ];
13285                 
13286                 if(this.indicatorpos == 'right'){
13287                     cfg.cn = [
13288                         {
13289                             tag: 'label',
13290                             //cls : 'input-group-addon',
13291                             html : this.fieldLabel
13292                         },
13293                         {
13294                             tag : 'i',
13295                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13296                             tooltip : 'This field is required'
13297                         },
13298                         combobox
13299                     ];
13300                     
13301                 }
13302
13303         } else {
13304             
13305 //                Roo.log(" no label && no align");
13306                 cfg = combobox
13307                      
13308                 
13309         }
13310          
13311         var settings=this;
13312         ['xs','sm','md','lg'].map(function(size){
13313             if (settings[size]) {
13314                 cfg.cls += ' col-' + size + '-' + settings[size];
13315             }
13316         });
13317         
13318         return cfg;
13319         
13320     },
13321     
13322     _initEventsCalled : false,
13323     
13324     // private
13325     initEvents: function()
13326     {   
13327         if (this._initEventsCalled) { // as we call render... prevent looping...
13328             return;
13329         }
13330         this._initEventsCalled = true;
13331         
13332         if (!this.store) {
13333             throw "can not find store for combo";
13334         }
13335         
13336         this.indicator = this.indicatorEl();
13337         
13338         this.store = Roo.factory(this.store, Roo.data);
13339         this.store.parent = this;
13340         
13341         // if we are building from html. then this element is so complex, that we can not really
13342         // use the rendered HTML.
13343         // so we have to trash and replace the previous code.
13344         if (Roo.XComponent.build_from_html) {
13345             // remove this element....
13346             var e = this.el.dom, k=0;
13347             while (e ) { e = e.previousSibling;  ++k;}
13348
13349             this.el.remove();
13350             
13351             this.el=false;
13352             this.rendered = false;
13353             
13354             this.render(this.parent().getChildContainer(true), k);
13355         }
13356         
13357         if(Roo.isIOS && this.useNativeIOS){
13358             this.initIOSView();
13359             return;
13360         }
13361         
13362         /*
13363          * Touch Devices
13364          */
13365         
13366         if(Roo.isTouch && this.mobileTouchView){
13367             this.initTouchView();
13368             return;
13369         }
13370         
13371         if(this.tickable){
13372             this.initTickableEvents();
13373             return;
13374         }
13375         
13376         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13377         
13378         if(this.hiddenName){
13379             
13380             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13381             
13382             this.hiddenField.dom.value =
13383                 this.hiddenValue !== undefined ? this.hiddenValue :
13384                 this.value !== undefined ? this.value : '';
13385
13386             // prevent input submission
13387             this.el.dom.removeAttribute('name');
13388             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13389              
13390              
13391         }
13392         //if(Roo.isGecko){
13393         //    this.el.dom.setAttribute('autocomplete', 'off');
13394         //}
13395         
13396         var cls = 'x-combo-list';
13397         
13398         //this.list = new Roo.Layer({
13399         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13400         //});
13401         
13402         var _this = this;
13403         
13404         (function(){
13405             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13406             _this.list.setWidth(lw);
13407         }).defer(100);
13408         
13409         this.list.on('mouseover', this.onViewOver, this);
13410         this.list.on('mousemove', this.onViewMove, this);
13411         this.list.on('scroll', this.onViewScroll, this);
13412         
13413         /*
13414         this.list.swallowEvent('mousewheel');
13415         this.assetHeight = 0;
13416
13417         if(this.title){
13418             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13419             this.assetHeight += this.header.getHeight();
13420         }
13421
13422         this.innerList = this.list.createChild({cls:cls+'-inner'});
13423         this.innerList.on('mouseover', this.onViewOver, this);
13424         this.innerList.on('mousemove', this.onViewMove, this);
13425         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13426         
13427         if(this.allowBlank && !this.pageSize && !this.disableClear){
13428             this.footer = this.list.createChild({cls:cls+'-ft'});
13429             this.pageTb = new Roo.Toolbar(this.footer);
13430            
13431         }
13432         if(this.pageSize){
13433             this.footer = this.list.createChild({cls:cls+'-ft'});
13434             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13435                     {pageSize: this.pageSize});
13436             
13437         }
13438         
13439         if (this.pageTb && this.allowBlank && !this.disableClear) {
13440             var _this = this;
13441             this.pageTb.add(new Roo.Toolbar.Fill(), {
13442                 cls: 'x-btn-icon x-btn-clear',
13443                 text: '&#160;',
13444                 handler: function()
13445                 {
13446                     _this.collapse();
13447                     _this.clearValue();
13448                     _this.onSelect(false, -1);
13449                 }
13450             });
13451         }
13452         if (this.footer) {
13453             this.assetHeight += this.footer.getHeight();
13454         }
13455         */
13456             
13457         if(!this.tpl){
13458             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13459         }
13460
13461         this.view = new Roo.View(this.list, this.tpl, {
13462             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13463         });
13464         //this.view.wrapEl.setDisplayed(false);
13465         this.view.on('click', this.onViewClick, this);
13466         
13467         
13468         this.store.on('beforeload', this.onBeforeLoad, this);
13469         this.store.on('load', this.onLoad, this);
13470         this.store.on('loadexception', this.onLoadException, this);
13471         /*
13472         if(this.resizable){
13473             this.resizer = new Roo.Resizable(this.list,  {
13474                pinned:true, handles:'se'
13475             });
13476             this.resizer.on('resize', function(r, w, h){
13477                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13478                 this.listWidth = w;
13479                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13480                 this.restrictHeight();
13481             }, this);
13482             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13483         }
13484         */
13485         if(!this.editable){
13486             this.editable = true;
13487             this.setEditable(false);
13488         }
13489         
13490         /*
13491         
13492         if (typeof(this.events.add.listeners) != 'undefined') {
13493             
13494             this.addicon = this.wrap.createChild(
13495                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13496        
13497             this.addicon.on('click', function(e) {
13498                 this.fireEvent('add', this);
13499             }, this);
13500         }
13501         if (typeof(this.events.edit.listeners) != 'undefined') {
13502             
13503             this.editicon = this.wrap.createChild(
13504                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13505             if (this.addicon) {
13506                 this.editicon.setStyle('margin-left', '40px');
13507             }
13508             this.editicon.on('click', function(e) {
13509                 
13510                 // we fire even  if inothing is selected..
13511                 this.fireEvent('edit', this, this.lastData );
13512                 
13513             }, this);
13514         }
13515         */
13516         
13517         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13518             "up" : function(e){
13519                 this.inKeyMode = true;
13520                 this.selectPrev();
13521             },
13522
13523             "down" : function(e){
13524                 if(!this.isExpanded()){
13525                     this.onTriggerClick();
13526                 }else{
13527                     this.inKeyMode = true;
13528                     this.selectNext();
13529                 }
13530             },
13531
13532             "enter" : function(e){
13533 //                this.onViewClick();
13534                 //return true;
13535                 this.collapse();
13536                 
13537                 if(this.fireEvent("specialkey", this, e)){
13538                     this.onViewClick(false);
13539                 }
13540                 
13541                 return true;
13542             },
13543
13544             "esc" : function(e){
13545                 this.collapse();
13546             },
13547
13548             "tab" : function(e){
13549                 this.collapse();
13550                 
13551                 if(this.fireEvent("specialkey", this, e)){
13552                     this.onViewClick(false);
13553                 }
13554                 
13555                 return true;
13556             },
13557
13558             scope : this,
13559
13560             doRelay : function(foo, bar, hname){
13561                 if(hname == 'down' || this.scope.isExpanded()){
13562                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13563                 }
13564                 return true;
13565             },
13566
13567             forceKeyDown: true
13568         });
13569         
13570         
13571         this.queryDelay = Math.max(this.queryDelay || 10,
13572                 this.mode == 'local' ? 10 : 250);
13573         
13574         
13575         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13576         
13577         if(this.typeAhead){
13578             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13579         }
13580         if(this.editable !== false){
13581             this.inputEl().on("keyup", this.onKeyUp, this);
13582         }
13583         if(this.forceSelection){
13584             this.inputEl().on('blur', this.doForce, this);
13585         }
13586         
13587         if(this.multiple){
13588             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13589             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13590         }
13591     },
13592     
13593     initTickableEvents: function()
13594     {   
13595         this.createList();
13596         
13597         if(this.hiddenName){
13598             
13599             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13600             
13601             this.hiddenField.dom.value =
13602                 this.hiddenValue !== undefined ? this.hiddenValue :
13603                 this.value !== undefined ? this.value : '';
13604
13605             // prevent input submission
13606             this.el.dom.removeAttribute('name');
13607             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13608              
13609              
13610         }
13611         
13612 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13613         
13614         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13615         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13616         if(this.triggerList){
13617             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13618         }
13619          
13620         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13621         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13622         
13623         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13624         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13625         
13626         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13627         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13628         
13629         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13630         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13631         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13632         
13633         this.okBtn.hide();
13634         this.cancelBtn.hide();
13635         
13636         var _this = this;
13637         
13638         (function(){
13639             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13640             _this.list.setWidth(lw);
13641         }).defer(100);
13642         
13643         this.list.on('mouseover', this.onViewOver, this);
13644         this.list.on('mousemove', this.onViewMove, this);
13645         
13646         this.list.on('scroll', this.onViewScroll, this);
13647         
13648         if(!this.tpl){
13649             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13650                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13651         }
13652
13653         this.view = new Roo.View(this.list, this.tpl, {
13654             singleSelect:true,
13655             tickable:true,
13656             parent:this,
13657             store: this.store,
13658             selectedClass: this.selectedClass
13659         });
13660         
13661         //this.view.wrapEl.setDisplayed(false);
13662         this.view.on('click', this.onViewClick, this);
13663         
13664         
13665         
13666         this.store.on('beforeload', this.onBeforeLoad, this);
13667         this.store.on('load', this.onLoad, this);
13668         this.store.on('loadexception', this.onLoadException, this);
13669         
13670         if(this.editable){
13671             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13672                 "up" : function(e){
13673                     this.inKeyMode = true;
13674                     this.selectPrev();
13675                 },
13676
13677                 "down" : function(e){
13678                     this.inKeyMode = true;
13679                     this.selectNext();
13680                 },
13681
13682                 "enter" : function(e){
13683                     if(this.fireEvent("specialkey", this, e)){
13684                         this.onViewClick(false);
13685                     }
13686                     
13687                     return true;
13688                 },
13689
13690                 "esc" : function(e){
13691                     this.onTickableFooterButtonClick(e, false, false);
13692                 },
13693
13694                 "tab" : function(e){
13695                     this.fireEvent("specialkey", this, e);
13696                     
13697                     this.onTickableFooterButtonClick(e, false, false);
13698                     
13699                     return true;
13700                 },
13701
13702                 scope : this,
13703
13704                 doRelay : function(e, fn, key){
13705                     if(this.scope.isExpanded()){
13706                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13707                     }
13708                     return true;
13709                 },
13710
13711                 forceKeyDown: true
13712             });
13713         }
13714         
13715         this.queryDelay = Math.max(this.queryDelay || 10,
13716                 this.mode == 'local' ? 10 : 250);
13717         
13718         
13719         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13720         
13721         if(this.typeAhead){
13722             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13723         }
13724         
13725         if(this.editable !== false){
13726             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13727         }
13728         
13729         this.indicator = this.indicatorEl();
13730         
13731         if(this.indicator){
13732             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13733             this.indicator.hide();
13734         }
13735         
13736     },
13737
13738     onDestroy : function(){
13739         if(this.view){
13740             this.view.setStore(null);
13741             this.view.el.removeAllListeners();
13742             this.view.el.remove();
13743             this.view.purgeListeners();
13744         }
13745         if(this.list){
13746             this.list.dom.innerHTML  = '';
13747         }
13748         
13749         if(this.store){
13750             this.store.un('beforeload', this.onBeforeLoad, this);
13751             this.store.un('load', this.onLoad, this);
13752             this.store.un('loadexception', this.onLoadException, this);
13753         }
13754         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13755     },
13756
13757     // private
13758     fireKey : function(e){
13759         if(e.isNavKeyPress() && !this.list.isVisible()){
13760             this.fireEvent("specialkey", this, e);
13761         }
13762     },
13763
13764     // private
13765     onResize: function(w, h){
13766 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13767 //        
13768 //        if(typeof w != 'number'){
13769 //            // we do not handle it!?!?
13770 //            return;
13771 //        }
13772 //        var tw = this.trigger.getWidth();
13773 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13774 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13775 //        var x = w - tw;
13776 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13777 //            
13778 //        //this.trigger.setStyle('left', x+'px');
13779 //        
13780 //        if(this.list && this.listWidth === undefined){
13781 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13782 //            this.list.setWidth(lw);
13783 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13784 //        }
13785         
13786     
13787         
13788     },
13789
13790     /**
13791      * Allow or prevent the user from directly editing the field text.  If false is passed,
13792      * the user will only be able to select from the items defined in the dropdown list.  This method
13793      * is the runtime equivalent of setting the 'editable' config option at config time.
13794      * @param {Boolean} value True to allow the user to directly edit the field text
13795      */
13796     setEditable : function(value){
13797         if(value == this.editable){
13798             return;
13799         }
13800         this.editable = value;
13801         if(!value){
13802             this.inputEl().dom.setAttribute('readOnly', true);
13803             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13804             this.inputEl().addClass('x-combo-noedit');
13805         }else{
13806             this.inputEl().dom.setAttribute('readOnly', false);
13807             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13808             this.inputEl().removeClass('x-combo-noedit');
13809         }
13810     },
13811
13812     // private
13813     
13814     onBeforeLoad : function(combo,opts){
13815         if(!this.hasFocus){
13816             return;
13817         }
13818          if (!opts.add) {
13819             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13820          }
13821         this.restrictHeight();
13822         this.selectedIndex = -1;
13823     },
13824
13825     // private
13826     onLoad : function(){
13827         
13828         this.hasQuery = false;
13829         
13830         if(!this.hasFocus){
13831             return;
13832         }
13833         
13834         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13835             this.loading.hide();
13836         }
13837         
13838         if(this.store.getCount() > 0){
13839             
13840             this.expand();
13841             this.restrictHeight();
13842             if(this.lastQuery == this.allQuery){
13843                 if(this.editable && !this.tickable){
13844                     this.inputEl().dom.select();
13845                 }
13846                 
13847                 if(
13848                     !this.selectByValue(this.value, true) &&
13849                     this.autoFocus && 
13850                     (
13851                         !this.store.lastOptions ||
13852                         typeof(this.store.lastOptions.add) == 'undefined' || 
13853                         this.store.lastOptions.add != true
13854                     )
13855                 ){
13856                     this.select(0, true);
13857                 }
13858             }else{
13859                 if(this.autoFocus){
13860                     this.selectNext();
13861                 }
13862                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13863                     this.taTask.delay(this.typeAheadDelay);
13864                 }
13865             }
13866         }else{
13867             this.onEmptyResults();
13868         }
13869         
13870         //this.el.focus();
13871     },
13872     // private
13873     onLoadException : function()
13874     {
13875         this.hasQuery = false;
13876         
13877         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13878             this.loading.hide();
13879         }
13880         
13881         if(this.tickable && this.editable){
13882             return;
13883         }
13884         
13885         this.collapse();
13886         // only causes errors at present
13887         //Roo.log(this.store.reader.jsonData);
13888         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13889             // fixme
13890             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13891         //}
13892         
13893         
13894     },
13895     // private
13896     onTypeAhead : function(){
13897         if(this.store.getCount() > 0){
13898             var r = this.store.getAt(0);
13899             var newValue = r.data[this.displayField];
13900             var len = newValue.length;
13901             var selStart = this.getRawValue().length;
13902             
13903             if(selStart != len){
13904                 this.setRawValue(newValue);
13905                 this.selectText(selStart, newValue.length);
13906             }
13907         }
13908     },
13909
13910     // private
13911     onSelect : function(record, index){
13912         
13913         if(this.fireEvent('beforeselect', this, record, index) !== false){
13914         
13915             this.setFromData(index > -1 ? record.data : false);
13916             
13917             this.collapse();
13918             this.fireEvent('select', this, record, index);
13919         }
13920     },
13921
13922     /**
13923      * Returns the currently selected field value or empty string if no value is set.
13924      * @return {String} value The selected value
13925      */
13926     getValue : function()
13927     {
13928         if(Roo.isIOS && this.useNativeIOS){
13929             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13930         }
13931         
13932         if(this.multiple){
13933             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13934         }
13935         
13936         if(this.valueField){
13937             return typeof this.value != 'undefined' ? this.value : '';
13938         }else{
13939             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13940         }
13941     },
13942     
13943     getRawValue : function()
13944     {
13945         if(Roo.isIOS && this.useNativeIOS){
13946             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13947         }
13948         
13949         var v = this.inputEl().getValue();
13950         
13951         return v;
13952     },
13953
13954     /**
13955      * Clears any text/value currently set in the field
13956      */
13957     clearValue : function(){
13958         
13959         if(this.hiddenField){
13960             this.hiddenField.dom.value = '';
13961         }
13962         this.value = '';
13963         this.setRawValue('');
13964         this.lastSelectionText = '';
13965         this.lastData = false;
13966         
13967         var close = this.closeTriggerEl();
13968         
13969         if(close){
13970             close.hide();
13971         }
13972         
13973         this.validate();
13974         
13975     },
13976
13977     /**
13978      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13979      * will be displayed in the field.  If the value does not match the data value of an existing item,
13980      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13981      * Otherwise the field will be blank (although the value will still be set).
13982      * @param {String} value The value to match
13983      */
13984     setValue : function(v)
13985     {
13986         if(Roo.isIOS && this.useNativeIOS){
13987             this.setIOSValue(v);
13988             return;
13989         }
13990         
13991         if(this.multiple){
13992             this.syncValue();
13993             return;
13994         }
13995         
13996         var text = v;
13997         if(this.valueField){
13998             var r = this.findRecord(this.valueField, v);
13999             if(r){
14000                 text = r.data[this.displayField];
14001             }else if(this.valueNotFoundText !== undefined){
14002                 text = this.valueNotFoundText;
14003             }
14004         }
14005         this.lastSelectionText = text;
14006         if(this.hiddenField){
14007             this.hiddenField.dom.value = v;
14008         }
14009         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14010         this.value = v;
14011         
14012         var close = this.closeTriggerEl();
14013         
14014         if(close){
14015             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14016         }
14017         
14018         this.validate();
14019     },
14020     /**
14021      * @property {Object} the last set data for the element
14022      */
14023     
14024     lastData : false,
14025     /**
14026      * Sets the value of the field based on a object which is related to the record format for the store.
14027      * @param {Object} value the value to set as. or false on reset?
14028      */
14029     setFromData : function(o){
14030         
14031         if(this.multiple){
14032             this.addItem(o);
14033             return;
14034         }
14035             
14036         var dv = ''; // display value
14037         var vv = ''; // value value..
14038         this.lastData = o;
14039         if (this.displayField) {
14040             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14041         } else {
14042             // this is an error condition!!!
14043             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14044         }
14045         
14046         if(this.valueField){
14047             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14048         }
14049         
14050         var close = this.closeTriggerEl();
14051         
14052         if(close){
14053             if(dv.length || vv * 1 > 0){
14054                 close.show() ;
14055                 this.blockFocus=true;
14056             } else {
14057                 close.hide();
14058             }             
14059         }
14060         
14061         if(this.hiddenField){
14062             this.hiddenField.dom.value = vv;
14063             
14064             this.lastSelectionText = dv;
14065             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14066             this.value = vv;
14067             return;
14068         }
14069         // no hidden field.. - we store the value in 'value', but still display
14070         // display field!!!!
14071         this.lastSelectionText = dv;
14072         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14073         this.value = vv;
14074         
14075         
14076         
14077     },
14078     // private
14079     reset : function(){
14080         // overridden so that last data is reset..
14081         
14082         if(this.multiple){
14083             this.clearItem();
14084             return;
14085         }
14086         
14087         this.setValue(this.originalValue);
14088         //this.clearInvalid();
14089         this.lastData = false;
14090         if (this.view) {
14091             this.view.clearSelections();
14092         }
14093         
14094         this.validate();
14095     },
14096     // private
14097     findRecord : function(prop, value){
14098         var record;
14099         if(this.store.getCount() > 0){
14100             this.store.each(function(r){
14101                 if(r.data[prop] == value){
14102                     record = r;
14103                     return false;
14104                 }
14105                 return true;
14106             });
14107         }
14108         return record;
14109     },
14110     
14111     getName: function()
14112     {
14113         // returns hidden if it's set..
14114         if (!this.rendered) {return ''};
14115         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14116         
14117     },
14118     // private
14119     onViewMove : function(e, t){
14120         this.inKeyMode = false;
14121     },
14122
14123     // private
14124     onViewOver : function(e, t){
14125         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14126             return;
14127         }
14128         var item = this.view.findItemFromChild(t);
14129         
14130         if(item){
14131             var index = this.view.indexOf(item);
14132             this.select(index, false);
14133         }
14134     },
14135
14136     // private
14137     onViewClick : function(view, doFocus, el, e)
14138     {
14139         var index = this.view.getSelectedIndexes()[0];
14140         
14141         var r = this.store.getAt(index);
14142         
14143         if(this.tickable){
14144             
14145             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14146                 return;
14147             }
14148             
14149             var rm = false;
14150             var _this = this;
14151             
14152             Roo.each(this.tickItems, function(v,k){
14153                 
14154                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14155                     Roo.log(v);
14156                     _this.tickItems.splice(k, 1);
14157                     
14158                     if(typeof(e) == 'undefined' && view == false){
14159                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14160                     }
14161                     
14162                     rm = true;
14163                     return;
14164                 }
14165             });
14166             
14167             if(rm){
14168                 return;
14169             }
14170             
14171             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14172                 this.tickItems.push(r.data);
14173             }
14174             
14175             if(typeof(e) == 'undefined' && view == false){
14176                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14177             }
14178                     
14179             return;
14180         }
14181         
14182         if(r){
14183             this.onSelect(r, index);
14184         }
14185         if(doFocus !== false && !this.blockFocus){
14186             this.inputEl().focus();
14187         }
14188     },
14189
14190     // private
14191     restrictHeight : function(){
14192         //this.innerList.dom.style.height = '';
14193         //var inner = this.innerList.dom;
14194         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14195         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14196         //this.list.beginUpdate();
14197         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14198         this.list.alignTo(this.inputEl(), this.listAlign);
14199         this.list.alignTo(this.inputEl(), this.listAlign);
14200         //this.list.endUpdate();
14201     },
14202
14203     // private
14204     onEmptyResults : function(){
14205         
14206         if(this.tickable && this.editable){
14207             this.hasFocus = false;
14208             this.restrictHeight();
14209             return;
14210         }
14211         
14212         this.collapse();
14213     },
14214
14215     /**
14216      * Returns true if the dropdown list is expanded, else false.
14217      */
14218     isExpanded : function(){
14219         return this.list.isVisible();
14220     },
14221
14222     /**
14223      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14224      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14225      * @param {String} value The data value of the item to select
14226      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14227      * selected item if it is not currently in view (defaults to true)
14228      * @return {Boolean} True if the value matched an item in the list, else false
14229      */
14230     selectByValue : function(v, scrollIntoView){
14231         if(v !== undefined && v !== null){
14232             var r = this.findRecord(this.valueField || this.displayField, v);
14233             if(r){
14234                 this.select(this.store.indexOf(r), scrollIntoView);
14235                 return true;
14236             }
14237         }
14238         return false;
14239     },
14240
14241     /**
14242      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14243      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14244      * @param {Number} index The zero-based index of the list item to select
14245      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14246      * selected item if it is not currently in view (defaults to true)
14247      */
14248     select : function(index, scrollIntoView){
14249         this.selectedIndex = index;
14250         this.view.select(index);
14251         if(scrollIntoView !== false){
14252             var el = this.view.getNode(index);
14253             /*
14254              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14255              */
14256             if(el){
14257                 this.list.scrollChildIntoView(el, false);
14258             }
14259         }
14260     },
14261
14262     // private
14263     selectNext : function(){
14264         var ct = this.store.getCount();
14265         if(ct > 0){
14266             if(this.selectedIndex == -1){
14267                 this.select(0);
14268             }else if(this.selectedIndex < ct-1){
14269                 this.select(this.selectedIndex+1);
14270             }
14271         }
14272     },
14273
14274     // private
14275     selectPrev : function(){
14276         var ct = this.store.getCount();
14277         if(ct > 0){
14278             if(this.selectedIndex == -1){
14279                 this.select(0);
14280             }else if(this.selectedIndex != 0){
14281                 this.select(this.selectedIndex-1);
14282             }
14283         }
14284     },
14285
14286     // private
14287     onKeyUp : function(e){
14288         if(this.editable !== false && !e.isSpecialKey()){
14289             this.lastKey = e.getKey();
14290             this.dqTask.delay(this.queryDelay);
14291         }
14292     },
14293
14294     // private
14295     validateBlur : function(){
14296         return !this.list || !this.list.isVisible();   
14297     },
14298
14299     // private
14300     initQuery : function(){
14301         
14302         var v = this.getRawValue();
14303         
14304         if(this.tickable && this.editable){
14305             v = this.tickableInputEl().getValue();
14306         }
14307         
14308         this.doQuery(v);
14309     },
14310
14311     // private
14312     doForce : function(){
14313         if(this.inputEl().dom.value.length > 0){
14314             this.inputEl().dom.value =
14315                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14316              
14317         }
14318     },
14319
14320     /**
14321      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14322      * query allowing the query action to be canceled if needed.
14323      * @param {String} query The SQL query to execute
14324      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14325      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14326      * saved in the current store (defaults to false)
14327      */
14328     doQuery : function(q, forceAll){
14329         
14330         if(q === undefined || q === null){
14331             q = '';
14332         }
14333         var qe = {
14334             query: q,
14335             forceAll: forceAll,
14336             combo: this,
14337             cancel:false
14338         };
14339         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14340             return false;
14341         }
14342         q = qe.query;
14343         
14344         forceAll = qe.forceAll;
14345         if(forceAll === true || (q.length >= this.minChars)){
14346             
14347             this.hasQuery = true;
14348             
14349             if(this.lastQuery != q || this.alwaysQuery){
14350                 this.lastQuery = q;
14351                 if(this.mode == 'local'){
14352                     this.selectedIndex = -1;
14353                     if(forceAll){
14354                         this.store.clearFilter();
14355                     }else{
14356                         
14357                         if(this.specialFilter){
14358                             this.fireEvent('specialfilter', this);
14359                             this.onLoad();
14360                             return;
14361                         }
14362                         
14363                         this.store.filter(this.displayField, q);
14364                     }
14365                     
14366                     this.store.fireEvent("datachanged", this.store);
14367                     
14368                     this.onLoad();
14369                     
14370                     
14371                 }else{
14372                     
14373                     this.store.baseParams[this.queryParam] = q;
14374                     
14375                     var options = {params : this.getParams(q)};
14376                     
14377                     if(this.loadNext){
14378                         options.add = true;
14379                         options.params.start = this.page * this.pageSize;
14380                     }
14381                     
14382                     this.store.load(options);
14383                     
14384                     /*
14385                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14386                      *  we should expand the list on onLoad
14387                      *  so command out it
14388                      */
14389 //                    this.expand();
14390                 }
14391             }else{
14392                 this.selectedIndex = -1;
14393                 this.onLoad();   
14394             }
14395         }
14396         
14397         this.loadNext = false;
14398     },
14399     
14400     // private
14401     getParams : function(q){
14402         var p = {};
14403         //p[this.queryParam] = q;
14404         
14405         if(this.pageSize){
14406             p.start = 0;
14407             p.limit = this.pageSize;
14408         }
14409         return p;
14410     },
14411
14412     /**
14413      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14414      */
14415     collapse : function(){
14416         if(!this.isExpanded()){
14417             return;
14418         }
14419         
14420         this.list.hide();
14421         
14422         this.hasFocus = false;
14423         
14424         if(this.tickable){
14425             this.okBtn.hide();
14426             this.cancelBtn.hide();
14427             this.trigger.show();
14428             
14429             if(this.editable){
14430                 this.tickableInputEl().dom.value = '';
14431                 this.tickableInputEl().blur();
14432             }
14433             
14434         }
14435         
14436         Roo.get(document).un('mousedown', this.collapseIf, this);
14437         Roo.get(document).un('mousewheel', this.collapseIf, this);
14438         if (!this.editable) {
14439             Roo.get(document).un('keydown', this.listKeyPress, this);
14440         }
14441         this.fireEvent('collapse', this);
14442         
14443         this.validate();
14444     },
14445
14446     // private
14447     collapseIf : function(e){
14448         var in_combo  = e.within(this.el);
14449         var in_list =  e.within(this.list);
14450         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14451         
14452         if (in_combo || in_list || is_list) {
14453             //e.stopPropagation();
14454             return;
14455         }
14456         
14457         if(this.tickable){
14458             this.onTickableFooterButtonClick(e, false, false);
14459         }
14460
14461         this.collapse();
14462         
14463     },
14464
14465     /**
14466      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14467      */
14468     expand : function(){
14469        
14470         if(this.isExpanded() || !this.hasFocus){
14471             return;
14472         }
14473         
14474         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14475         this.list.setWidth(lw);
14476         
14477         Roo.log('expand');
14478         
14479         this.list.show();
14480         
14481         this.restrictHeight();
14482         
14483         if(this.tickable){
14484             
14485             this.tickItems = Roo.apply([], this.item);
14486             
14487             this.okBtn.show();
14488             this.cancelBtn.show();
14489             this.trigger.hide();
14490             
14491             if(this.editable){
14492                 this.tickableInputEl().focus();
14493             }
14494             
14495         }
14496         
14497         Roo.get(document).on('mousedown', this.collapseIf, this);
14498         Roo.get(document).on('mousewheel', this.collapseIf, this);
14499         if (!this.editable) {
14500             Roo.get(document).on('keydown', this.listKeyPress, this);
14501         }
14502         
14503         this.fireEvent('expand', this);
14504     },
14505
14506     // private
14507     // Implements the default empty TriggerField.onTriggerClick function
14508     onTriggerClick : function(e)
14509     {
14510         Roo.log('trigger click');
14511         
14512         if(this.disabled || !this.triggerList){
14513             return;
14514         }
14515         
14516         this.page = 0;
14517         this.loadNext = false;
14518         
14519         if(this.isExpanded()){
14520             this.collapse();
14521             if (!this.blockFocus) {
14522                 this.inputEl().focus();
14523             }
14524             
14525         }else {
14526             this.hasFocus = true;
14527             if(this.triggerAction == 'all') {
14528                 this.doQuery(this.allQuery, true);
14529             } else {
14530                 this.doQuery(this.getRawValue());
14531             }
14532             if (!this.blockFocus) {
14533                 this.inputEl().focus();
14534             }
14535         }
14536     },
14537     
14538     onTickableTriggerClick : function(e)
14539     {
14540         if(this.disabled){
14541             return;
14542         }
14543         
14544         this.page = 0;
14545         this.loadNext = false;
14546         this.hasFocus = true;
14547         
14548         if(this.triggerAction == 'all') {
14549             this.doQuery(this.allQuery, true);
14550         } else {
14551             this.doQuery(this.getRawValue());
14552         }
14553     },
14554     
14555     onSearchFieldClick : function(e)
14556     {
14557         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14558             this.onTickableFooterButtonClick(e, false, false);
14559             return;
14560         }
14561         
14562         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14563             return;
14564         }
14565         
14566         this.page = 0;
14567         this.loadNext = false;
14568         this.hasFocus = true;
14569         
14570         if(this.triggerAction == 'all') {
14571             this.doQuery(this.allQuery, true);
14572         } else {
14573             this.doQuery(this.getRawValue());
14574         }
14575     },
14576     
14577     listKeyPress : function(e)
14578     {
14579         //Roo.log('listkeypress');
14580         // scroll to first matching element based on key pres..
14581         if (e.isSpecialKey()) {
14582             return false;
14583         }
14584         var k = String.fromCharCode(e.getKey()).toUpperCase();
14585         //Roo.log(k);
14586         var match  = false;
14587         var csel = this.view.getSelectedNodes();
14588         var cselitem = false;
14589         if (csel.length) {
14590             var ix = this.view.indexOf(csel[0]);
14591             cselitem  = this.store.getAt(ix);
14592             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14593                 cselitem = false;
14594             }
14595             
14596         }
14597         
14598         this.store.each(function(v) { 
14599             if (cselitem) {
14600                 // start at existing selection.
14601                 if (cselitem.id == v.id) {
14602                     cselitem = false;
14603                 }
14604                 return true;
14605             }
14606                 
14607             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14608                 match = this.store.indexOf(v);
14609                 return false;
14610             }
14611             return true;
14612         }, this);
14613         
14614         if (match === false) {
14615             return true; // no more action?
14616         }
14617         // scroll to?
14618         this.view.select(match);
14619         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14620         sn.scrollIntoView(sn.dom.parentNode, false);
14621     },
14622     
14623     onViewScroll : function(e, t){
14624         
14625         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){
14626             return;
14627         }
14628         
14629         this.hasQuery = true;
14630         
14631         this.loading = this.list.select('.loading', true).first();
14632         
14633         if(this.loading === null){
14634             this.list.createChild({
14635                 tag: 'div',
14636                 cls: 'loading roo-select2-more-results roo-select2-active',
14637                 html: 'Loading more results...'
14638             });
14639             
14640             this.loading = this.list.select('.loading', true).first();
14641             
14642             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14643             
14644             this.loading.hide();
14645         }
14646         
14647         this.loading.show();
14648         
14649         var _combo = this;
14650         
14651         this.page++;
14652         this.loadNext = true;
14653         
14654         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14655         
14656         return;
14657     },
14658     
14659     addItem : function(o)
14660     {   
14661         var dv = ''; // display value
14662         
14663         if (this.displayField) {
14664             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14665         } else {
14666             // this is an error condition!!!
14667             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14668         }
14669         
14670         if(!dv.length){
14671             return;
14672         }
14673         
14674         var choice = this.choices.createChild({
14675             tag: 'li',
14676             cls: 'roo-select2-search-choice',
14677             cn: [
14678                 {
14679                     tag: 'div',
14680                     html: dv
14681                 },
14682                 {
14683                     tag: 'a',
14684                     href: '#',
14685                     cls: 'roo-select2-search-choice-close fa fa-times',
14686                     tabindex: '-1'
14687                 }
14688             ]
14689             
14690         }, this.searchField);
14691         
14692         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14693         
14694         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14695         
14696         this.item.push(o);
14697         
14698         this.lastData = o;
14699         
14700         this.syncValue();
14701         
14702         this.inputEl().dom.value = '';
14703         
14704         this.validate();
14705     },
14706     
14707     onRemoveItem : function(e, _self, o)
14708     {
14709         e.preventDefault();
14710         
14711         this.lastItem = Roo.apply([], this.item);
14712         
14713         var index = this.item.indexOf(o.data) * 1;
14714         
14715         if( index < 0){
14716             Roo.log('not this item?!');
14717             return;
14718         }
14719         
14720         this.item.splice(index, 1);
14721         o.item.remove();
14722         
14723         this.syncValue();
14724         
14725         this.fireEvent('remove', this, e);
14726         
14727         this.validate();
14728         
14729     },
14730     
14731     syncValue : function()
14732     {
14733         if(!this.item.length){
14734             this.clearValue();
14735             return;
14736         }
14737             
14738         var value = [];
14739         var _this = this;
14740         Roo.each(this.item, function(i){
14741             if(_this.valueField){
14742                 value.push(i[_this.valueField]);
14743                 return;
14744             }
14745
14746             value.push(i);
14747         });
14748
14749         this.value = value.join(',');
14750
14751         if(this.hiddenField){
14752             this.hiddenField.dom.value = this.value;
14753         }
14754         
14755         this.store.fireEvent("datachanged", this.store);
14756         
14757         this.validate();
14758     },
14759     
14760     clearItem : function()
14761     {
14762         if(!this.multiple){
14763             return;
14764         }
14765         
14766         this.item = [];
14767         
14768         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14769            c.remove();
14770         });
14771         
14772         this.syncValue();
14773         
14774         this.validate();
14775         
14776         if(this.tickable && !Roo.isTouch){
14777             this.view.refresh();
14778         }
14779     },
14780     
14781     inputEl: function ()
14782     {
14783         if(Roo.isIOS && this.useNativeIOS){
14784             return this.el.select('select.roo-ios-select', true).first();
14785         }
14786         
14787         if(Roo.isTouch && this.mobileTouchView){
14788             return this.el.select('input.form-control',true).first();
14789         }
14790         
14791         if(this.tickable){
14792             return this.searchField;
14793         }
14794         
14795         return this.el.select('input.form-control',true).first();
14796     },
14797     
14798     onTickableFooterButtonClick : function(e, btn, el)
14799     {
14800         e.preventDefault();
14801         
14802         this.lastItem = Roo.apply([], this.item);
14803         
14804         if(btn && btn.name == 'cancel'){
14805             this.tickItems = Roo.apply([], this.item);
14806             this.collapse();
14807             return;
14808         }
14809         
14810         this.clearItem();
14811         
14812         var _this = this;
14813         
14814         Roo.each(this.tickItems, function(o){
14815             _this.addItem(o);
14816         });
14817         
14818         this.collapse();
14819         
14820     },
14821     
14822     validate : function()
14823     {
14824         if(this.getVisibilityEl().hasClass('hidden')){
14825             return true;
14826         }
14827         
14828         var v = this.getRawValue();
14829         
14830         if(this.multiple){
14831             v = this.getValue();
14832         }
14833         
14834         if(this.disabled || this.allowBlank || v.length){
14835             this.markValid();
14836             return true;
14837         }
14838         
14839         this.markInvalid();
14840         return false;
14841     },
14842     
14843     tickableInputEl : function()
14844     {
14845         if(!this.tickable || !this.editable){
14846             return this.inputEl();
14847         }
14848         
14849         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14850     },
14851     
14852     
14853     getAutoCreateTouchView : function()
14854     {
14855         var id = Roo.id();
14856         
14857         var cfg = {
14858             cls: 'form-group' //input-group
14859         };
14860         
14861         var input =  {
14862             tag: 'input',
14863             id : id,
14864             type : this.inputType,
14865             cls : 'form-control x-combo-noedit',
14866             autocomplete: 'new-password',
14867             placeholder : this.placeholder || '',
14868             readonly : true
14869         };
14870         
14871         if (this.name) {
14872             input.name = this.name;
14873         }
14874         
14875         if (this.size) {
14876             input.cls += ' input-' + this.size;
14877         }
14878         
14879         if (this.disabled) {
14880             input.disabled = true;
14881         }
14882         
14883         var inputblock = {
14884             cls : '',
14885             cn : [
14886                 input
14887             ]
14888         };
14889         
14890         if(this.before){
14891             inputblock.cls += ' input-group';
14892             
14893             inputblock.cn.unshift({
14894                 tag :'span',
14895                 cls : 'input-group-addon',
14896                 html : this.before
14897             });
14898         }
14899         
14900         if(this.removable && !this.multiple){
14901             inputblock.cls += ' roo-removable';
14902             
14903             inputblock.cn.push({
14904                 tag: 'button',
14905                 html : 'x',
14906                 cls : 'roo-combo-removable-btn close'
14907             });
14908         }
14909
14910         if(this.hasFeedback && !this.allowBlank){
14911             
14912             inputblock.cls += ' has-feedback';
14913             
14914             inputblock.cn.push({
14915                 tag: 'span',
14916                 cls: 'glyphicon form-control-feedback'
14917             });
14918             
14919         }
14920         
14921         if (this.after) {
14922             
14923             inputblock.cls += (this.before) ? '' : ' input-group';
14924             
14925             inputblock.cn.push({
14926                 tag :'span',
14927                 cls : 'input-group-addon',
14928                 html : this.after
14929             });
14930         }
14931
14932         var box = {
14933             tag: 'div',
14934             cn: [
14935                 {
14936                     tag: 'input',
14937                     type : 'hidden',
14938                     cls: 'form-hidden-field'
14939                 },
14940                 inputblock
14941             ]
14942             
14943         };
14944         
14945         if(this.multiple){
14946             box = {
14947                 tag: 'div',
14948                 cn: [
14949                     {
14950                         tag: 'input',
14951                         type : 'hidden',
14952                         cls: 'form-hidden-field'
14953                     },
14954                     {
14955                         tag: 'ul',
14956                         cls: 'roo-select2-choices',
14957                         cn:[
14958                             {
14959                                 tag: 'li',
14960                                 cls: 'roo-select2-search-field',
14961                                 cn: [
14962
14963                                     inputblock
14964                                 ]
14965                             }
14966                         ]
14967                     }
14968                 ]
14969             }
14970         };
14971         
14972         var combobox = {
14973             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14974             cn: [
14975                 box
14976             ]
14977         };
14978         
14979         if(!this.multiple && this.showToggleBtn){
14980             
14981             var caret = {
14982                         tag: 'span',
14983                         cls: 'caret'
14984             };
14985             
14986             if (this.caret != false) {
14987                 caret = {
14988                      tag: 'i',
14989                      cls: 'fa fa-' + this.caret
14990                 };
14991                 
14992             }
14993             
14994             combobox.cn.push({
14995                 tag :'span',
14996                 cls : 'input-group-addon btn dropdown-toggle',
14997                 cn : [
14998                     caret,
14999                     {
15000                         tag: 'span',
15001                         cls: 'combobox-clear',
15002                         cn  : [
15003                             {
15004                                 tag : 'i',
15005                                 cls: 'icon-remove'
15006                             }
15007                         ]
15008                     }
15009                 ]
15010
15011             })
15012         }
15013         
15014         if(this.multiple){
15015             combobox.cls += ' roo-select2-container-multi';
15016         }
15017         
15018         var align = this.labelAlign || this.parentLabelAlign();
15019         
15020         if (align ==='left' && this.fieldLabel.length) {
15021
15022             cfg.cn = [
15023                 {
15024                    tag : 'i',
15025                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15026                    tooltip : 'This field is required'
15027                 },
15028                 {
15029                     tag: 'label',
15030                     cls : 'control-label',
15031                     html : this.fieldLabel
15032
15033                 },
15034                 {
15035                     cls : '', 
15036                     cn: [
15037                         combobox
15038                     ]
15039                 }
15040             ];
15041             
15042             var labelCfg = cfg.cn[1];
15043             var contentCfg = cfg.cn[2];
15044             
15045
15046             if(this.indicatorpos == 'right'){
15047                 cfg.cn = [
15048                     {
15049                         tag: 'label',
15050                         'for' :  id,
15051                         cls : 'control-label',
15052                         cn : [
15053                             {
15054                                 tag : 'span',
15055                                 html : this.fieldLabel
15056                             },
15057                             {
15058                                 tag : 'i',
15059                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15060                                 tooltip : 'This field is required'
15061                             }
15062                         ]
15063                     },
15064                     {
15065                         cls : "",
15066                         cn: [
15067                             combobox
15068                         ]
15069                     }
15070
15071                 ];
15072                 
15073                 labelCfg = cfg.cn[0];
15074                 contentCfg = cfg.cn[1];
15075             }
15076             
15077            
15078             
15079             if(this.labelWidth > 12){
15080                 labelCfg.style = "width: " + this.labelWidth + 'px';
15081             }
15082             
15083             if(this.labelWidth < 13 && this.labelmd == 0){
15084                 this.labelmd = this.labelWidth;
15085             }
15086             
15087             if(this.labellg > 0){
15088                 labelCfg.cls += ' col-lg-' + this.labellg;
15089                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15090             }
15091             
15092             if(this.labelmd > 0){
15093                 labelCfg.cls += ' col-md-' + this.labelmd;
15094                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15095             }
15096             
15097             if(this.labelsm > 0){
15098                 labelCfg.cls += ' col-sm-' + this.labelsm;
15099                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15100             }
15101             
15102             if(this.labelxs > 0){
15103                 labelCfg.cls += ' col-xs-' + this.labelxs;
15104                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15105             }
15106                 
15107                 
15108         } else if ( this.fieldLabel.length) {
15109             cfg.cn = [
15110                 {
15111                    tag : 'i',
15112                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15113                    tooltip : 'This field is required'
15114                 },
15115                 {
15116                     tag: 'label',
15117                     cls : 'control-label',
15118                     html : this.fieldLabel
15119
15120                 },
15121                 {
15122                     cls : '', 
15123                     cn: [
15124                         combobox
15125                     ]
15126                 }
15127             ];
15128             
15129             if(this.indicatorpos == 'right'){
15130                 cfg.cn = [
15131                     {
15132                         tag: 'label',
15133                         cls : 'control-label',
15134                         html : this.fieldLabel,
15135                         cn : [
15136                             {
15137                                tag : 'i',
15138                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15139                                tooltip : 'This field is required'
15140                             }
15141                         ]
15142                     },
15143                     {
15144                         cls : '', 
15145                         cn: [
15146                             combobox
15147                         ]
15148                     }
15149                 ];
15150             }
15151         } else {
15152             cfg.cn = combobox;    
15153         }
15154         
15155         
15156         var settings = this;
15157         
15158         ['xs','sm','md','lg'].map(function(size){
15159             if (settings[size]) {
15160                 cfg.cls += ' col-' + size + '-' + settings[size];
15161             }
15162         });
15163         
15164         return cfg;
15165     },
15166     
15167     initTouchView : function()
15168     {
15169         this.renderTouchView();
15170         
15171         this.touchViewEl.on('scroll', function(){
15172             this.el.dom.scrollTop = 0;
15173         }, this);
15174         
15175         this.originalValue = this.getValue();
15176         
15177         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15178         
15179         this.inputEl().on("click", this.showTouchView, this);
15180         if (this.triggerEl) {
15181             this.triggerEl.on("click", this.showTouchView, this);
15182         }
15183         
15184         
15185         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15186         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15187         
15188         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15189         
15190         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15191         this.store.on('load', this.onTouchViewLoad, this);
15192         this.store.on('loadexception', this.onTouchViewLoadException, this);
15193         
15194         if(this.hiddenName){
15195             
15196             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15197             
15198             this.hiddenField.dom.value =
15199                 this.hiddenValue !== undefined ? this.hiddenValue :
15200                 this.value !== undefined ? this.value : '';
15201         
15202             this.el.dom.removeAttribute('name');
15203             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15204         }
15205         
15206         if(this.multiple){
15207             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15208             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15209         }
15210         
15211         if(this.removable && !this.multiple){
15212             var close = this.closeTriggerEl();
15213             if(close){
15214                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15215                 close.on('click', this.removeBtnClick, this, close);
15216             }
15217         }
15218         /*
15219          * fix the bug in Safari iOS8
15220          */
15221         this.inputEl().on("focus", function(e){
15222             document.activeElement.blur();
15223         }, this);
15224         
15225         return;
15226         
15227         
15228     },
15229     
15230     renderTouchView : function()
15231     {
15232         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15233         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15234         
15235         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15236         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15237         
15238         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15239         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15240         this.touchViewBodyEl.setStyle('overflow', 'auto');
15241         
15242         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15243         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15244         
15245         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15246         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15247         
15248     },
15249     
15250     showTouchView : function()
15251     {
15252         if(this.disabled){
15253             return;
15254         }
15255         
15256         this.touchViewHeaderEl.hide();
15257
15258         if(this.modalTitle.length){
15259             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15260             this.touchViewHeaderEl.show();
15261         }
15262
15263         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15264         this.touchViewEl.show();
15265
15266         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15267         
15268         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15269         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15270
15271         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15272
15273         if(this.modalTitle.length){
15274             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15275         }
15276         
15277         this.touchViewBodyEl.setHeight(bodyHeight);
15278
15279         if(this.animate){
15280             var _this = this;
15281             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15282         }else{
15283             this.touchViewEl.addClass('in');
15284         }
15285
15286         this.doTouchViewQuery();
15287         
15288     },
15289     
15290     hideTouchView : function()
15291     {
15292         this.touchViewEl.removeClass('in');
15293
15294         if(this.animate){
15295             var _this = this;
15296             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15297         }else{
15298             this.touchViewEl.setStyle('display', 'none');
15299         }
15300         
15301     },
15302     
15303     setTouchViewValue : function()
15304     {
15305         if(this.multiple){
15306             this.clearItem();
15307         
15308             var _this = this;
15309
15310             Roo.each(this.tickItems, function(o){
15311                 this.addItem(o);
15312             }, this);
15313         }
15314         
15315         this.hideTouchView();
15316     },
15317     
15318     doTouchViewQuery : function()
15319     {
15320         var qe = {
15321             query: '',
15322             forceAll: true,
15323             combo: this,
15324             cancel:false
15325         };
15326         
15327         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15328             return false;
15329         }
15330         
15331         if(!this.alwaysQuery || this.mode == 'local'){
15332             this.onTouchViewLoad();
15333             return;
15334         }
15335         
15336         this.store.load();
15337     },
15338     
15339     onTouchViewBeforeLoad : function(combo,opts)
15340     {
15341         return;
15342     },
15343
15344     // private
15345     onTouchViewLoad : function()
15346     {
15347         if(this.store.getCount() < 1){
15348             this.onTouchViewEmptyResults();
15349             return;
15350         }
15351         
15352         this.clearTouchView();
15353         
15354         var rawValue = this.getRawValue();
15355         
15356         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15357         
15358         this.tickItems = [];
15359         
15360         this.store.data.each(function(d, rowIndex){
15361             var row = this.touchViewListGroup.createChild(template);
15362             
15363             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15364                 row.addClass(d.data.cls);
15365             }
15366             
15367             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15368                 var cfg = {
15369                     data : d.data,
15370                     html : d.data[this.displayField]
15371                 };
15372                 
15373                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15374                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15375                 }
15376             }
15377             row.removeClass('selected');
15378             if(!this.multiple && this.valueField &&
15379                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15380             {
15381                 // radio buttons..
15382                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15383                 row.addClass('selected');
15384             }
15385             
15386             if(this.multiple && this.valueField &&
15387                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15388             {
15389                 
15390                 // checkboxes...
15391                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15392                 this.tickItems.push(d.data);
15393             }
15394             
15395             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15396             
15397         }, this);
15398         
15399         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15400         
15401         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15402
15403         if(this.modalTitle.length){
15404             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15405         }
15406
15407         var listHeight = this.touchViewListGroup.getHeight();
15408         
15409         var _this = this;
15410         
15411         if(firstChecked && listHeight > bodyHeight){
15412             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15413         }
15414         
15415     },
15416     
15417     onTouchViewLoadException : function()
15418     {
15419         this.hideTouchView();
15420     },
15421     
15422     onTouchViewEmptyResults : function()
15423     {
15424         this.clearTouchView();
15425         
15426         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15427         
15428         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15429         
15430     },
15431     
15432     clearTouchView : function()
15433     {
15434         this.touchViewListGroup.dom.innerHTML = '';
15435     },
15436     
15437     onTouchViewClick : function(e, el, o)
15438     {
15439         e.preventDefault();
15440         
15441         var row = o.row;
15442         var rowIndex = o.rowIndex;
15443         
15444         var r = this.store.getAt(rowIndex);
15445         
15446         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15447             
15448             if(!this.multiple){
15449                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15450                     c.dom.removeAttribute('checked');
15451                 }, this);
15452
15453                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15454
15455                 this.setFromData(r.data);
15456
15457                 var close = this.closeTriggerEl();
15458
15459                 if(close){
15460                     close.show();
15461                 }
15462
15463                 this.hideTouchView();
15464
15465                 this.fireEvent('select', this, r, rowIndex);
15466
15467                 return;
15468             }
15469
15470             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15471                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15472                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15473                 return;
15474             }
15475
15476             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15477             this.addItem(r.data);
15478             this.tickItems.push(r.data);
15479         }
15480     },
15481     
15482     getAutoCreateNativeIOS : function()
15483     {
15484         var cfg = {
15485             cls: 'form-group' //input-group,
15486         };
15487         
15488         var combobox =  {
15489             tag: 'select',
15490             cls : 'roo-ios-select'
15491         };
15492         
15493         if (this.name) {
15494             combobox.name = this.name;
15495         }
15496         
15497         if (this.disabled) {
15498             combobox.disabled = true;
15499         }
15500         
15501         var settings = this;
15502         
15503         ['xs','sm','md','lg'].map(function(size){
15504             if (settings[size]) {
15505                 cfg.cls += ' col-' + size + '-' + settings[size];
15506             }
15507         });
15508         
15509         cfg.cn = combobox;
15510         
15511         return cfg;
15512         
15513     },
15514     
15515     initIOSView : function()
15516     {
15517         this.store.on('load', this.onIOSViewLoad, this);
15518         
15519         return;
15520     },
15521     
15522     onIOSViewLoad : function()
15523     {
15524         if(this.store.getCount() < 1){
15525             return;
15526         }
15527         
15528         this.clearIOSView();
15529         
15530         if(this.allowBlank) {
15531             
15532             var default_text = '-- SELECT --';
15533             
15534             if(this.placeholder.length){
15535                 default_text = this.placeholder;
15536             }
15537             
15538             if(this.emptyTitle.length){
15539                 default_text += ' - ' + this.emptyTitle + ' -';
15540             }
15541             
15542             var opt = this.inputEl().createChild({
15543                 tag: 'option',
15544                 value : 0,
15545                 html : default_text
15546             });
15547             
15548             var o = {};
15549             o[this.valueField] = 0;
15550             o[this.displayField] = default_text;
15551             
15552             this.ios_options.push({
15553                 data : o,
15554                 el : opt
15555             });
15556             
15557         }
15558         
15559         this.store.data.each(function(d, rowIndex){
15560             
15561             var html = '';
15562             
15563             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15564                 html = d.data[this.displayField];
15565             }
15566             
15567             var value = '';
15568             
15569             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15570                 value = d.data[this.valueField];
15571             }
15572             
15573             var option = {
15574                 tag: 'option',
15575                 value : value,
15576                 html : html
15577             };
15578             
15579             if(this.value == d.data[this.valueField]){
15580                 option['selected'] = true;
15581             }
15582             
15583             var opt = this.inputEl().createChild(option);
15584             
15585             this.ios_options.push({
15586                 data : d.data,
15587                 el : opt
15588             });
15589             
15590         }, this);
15591         
15592         this.inputEl().on('change', function(){
15593            this.fireEvent('select', this);
15594         }, this);
15595         
15596     },
15597     
15598     clearIOSView: function()
15599     {
15600         this.inputEl().dom.innerHTML = '';
15601         
15602         this.ios_options = [];
15603     },
15604     
15605     setIOSValue: function(v)
15606     {
15607         this.value = v;
15608         
15609         if(!this.ios_options){
15610             return;
15611         }
15612         
15613         Roo.each(this.ios_options, function(opts){
15614            
15615            opts.el.dom.removeAttribute('selected');
15616            
15617            if(opts.data[this.valueField] != v){
15618                return;
15619            }
15620            
15621            opts.el.dom.setAttribute('selected', true);
15622            
15623         }, this);
15624     }
15625
15626     /** 
15627     * @cfg {Boolean} grow 
15628     * @hide 
15629     */
15630     /** 
15631     * @cfg {Number} growMin 
15632     * @hide 
15633     */
15634     /** 
15635     * @cfg {Number} growMax 
15636     * @hide 
15637     */
15638     /**
15639      * @hide
15640      * @method autoSize
15641      */
15642 });
15643
15644 Roo.apply(Roo.bootstrap.ComboBox,  {
15645     
15646     header : {
15647         tag: 'div',
15648         cls: 'modal-header',
15649         cn: [
15650             {
15651                 tag: 'h4',
15652                 cls: 'modal-title'
15653             }
15654         ]
15655     },
15656     
15657     body : {
15658         tag: 'div',
15659         cls: 'modal-body',
15660         cn: [
15661             {
15662                 tag: 'ul',
15663                 cls: 'list-group'
15664             }
15665         ]
15666     },
15667     
15668     listItemRadio : {
15669         tag: 'li',
15670         cls: 'list-group-item',
15671         cn: [
15672             {
15673                 tag: 'span',
15674                 cls: 'roo-combobox-list-group-item-value'
15675             },
15676             {
15677                 tag: 'div',
15678                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15679                 cn: [
15680                     {
15681                         tag: 'input',
15682                         type: 'radio'
15683                     },
15684                     {
15685                         tag: 'label'
15686                     }
15687                 ]
15688             }
15689         ]
15690     },
15691     
15692     listItemCheckbox : {
15693         tag: 'li',
15694         cls: 'list-group-item',
15695         cn: [
15696             {
15697                 tag: 'span',
15698                 cls: 'roo-combobox-list-group-item-value'
15699             },
15700             {
15701                 tag: 'div',
15702                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15703                 cn: [
15704                     {
15705                         tag: 'input',
15706                         type: 'checkbox'
15707                     },
15708                     {
15709                         tag: 'label'
15710                     }
15711                 ]
15712             }
15713         ]
15714     },
15715     
15716     emptyResult : {
15717         tag: 'div',
15718         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15719     },
15720     
15721     footer : {
15722         tag: 'div',
15723         cls: 'modal-footer',
15724         cn: [
15725             {
15726                 tag: 'div',
15727                 cls: 'row',
15728                 cn: [
15729                     {
15730                         tag: 'div',
15731                         cls: 'col-xs-6 text-left',
15732                         cn: {
15733                             tag: 'button',
15734                             cls: 'btn btn-danger roo-touch-view-cancel',
15735                             html: 'Cancel'
15736                         }
15737                     },
15738                     {
15739                         tag: 'div',
15740                         cls: 'col-xs-6 text-right',
15741                         cn: {
15742                             tag: 'button',
15743                             cls: 'btn btn-success roo-touch-view-ok',
15744                             html: 'OK'
15745                         }
15746                     }
15747                 ]
15748             }
15749         ]
15750         
15751     }
15752 });
15753
15754 Roo.apply(Roo.bootstrap.ComboBox,  {
15755     
15756     touchViewTemplate : {
15757         tag: 'div',
15758         cls: 'modal fade roo-combobox-touch-view',
15759         cn: [
15760             {
15761                 tag: 'div',
15762                 cls: 'modal-dialog',
15763                 style : 'position:fixed', // we have to fix position....
15764                 cn: [
15765                     {
15766                         tag: 'div',
15767                         cls: 'modal-content',
15768                         cn: [
15769                             Roo.bootstrap.ComboBox.header,
15770                             Roo.bootstrap.ComboBox.body,
15771                             Roo.bootstrap.ComboBox.footer
15772                         ]
15773                     }
15774                 ]
15775             }
15776         ]
15777     }
15778 });/*
15779  * Based on:
15780  * Ext JS Library 1.1.1
15781  * Copyright(c) 2006-2007, Ext JS, LLC.
15782  *
15783  * Originally Released Under LGPL - original licence link has changed is not relivant.
15784  *
15785  * Fork - LGPL
15786  * <script type="text/javascript">
15787  */
15788
15789 /**
15790  * @class Roo.View
15791  * @extends Roo.util.Observable
15792  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15793  * This class also supports single and multi selection modes. <br>
15794  * Create a data model bound view:
15795  <pre><code>
15796  var store = new Roo.data.Store(...);
15797
15798  var view = new Roo.View({
15799     el : "my-element",
15800     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15801  
15802     singleSelect: true,
15803     selectedClass: "ydataview-selected",
15804     store: store
15805  });
15806
15807  // listen for node click?
15808  view.on("click", function(vw, index, node, e){
15809  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15810  });
15811
15812  // load XML data
15813  dataModel.load("foobar.xml");
15814  </code></pre>
15815  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15816  * <br><br>
15817  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15818  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15819  * 
15820  * Note: old style constructor is still suported (container, template, config)
15821  * 
15822  * @constructor
15823  * Create a new View
15824  * @param {Object} config The config object
15825  * 
15826  */
15827 Roo.View = function(config, depreciated_tpl, depreciated_config){
15828     
15829     this.parent = false;
15830     
15831     if (typeof(depreciated_tpl) == 'undefined') {
15832         // new way.. - universal constructor.
15833         Roo.apply(this, config);
15834         this.el  = Roo.get(this.el);
15835     } else {
15836         // old format..
15837         this.el  = Roo.get(config);
15838         this.tpl = depreciated_tpl;
15839         Roo.apply(this, depreciated_config);
15840     }
15841     this.wrapEl  = this.el.wrap().wrap();
15842     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15843     
15844     
15845     if(typeof(this.tpl) == "string"){
15846         this.tpl = new Roo.Template(this.tpl);
15847     } else {
15848         // support xtype ctors..
15849         this.tpl = new Roo.factory(this.tpl, Roo);
15850     }
15851     
15852     
15853     this.tpl.compile();
15854     
15855     /** @private */
15856     this.addEvents({
15857         /**
15858          * @event beforeclick
15859          * Fires before a click is processed. Returns false to cancel the default action.
15860          * @param {Roo.View} this
15861          * @param {Number} index The index of the target node
15862          * @param {HTMLElement} node The target node
15863          * @param {Roo.EventObject} e The raw event object
15864          */
15865             "beforeclick" : true,
15866         /**
15867          * @event click
15868          * Fires when a template node is clicked.
15869          * @param {Roo.View} this
15870          * @param {Number} index The index of the target node
15871          * @param {HTMLElement} node The target node
15872          * @param {Roo.EventObject} e The raw event object
15873          */
15874             "click" : true,
15875         /**
15876          * @event dblclick
15877          * Fires when a template node is double clicked.
15878          * @param {Roo.View} this
15879          * @param {Number} index The index of the target node
15880          * @param {HTMLElement} node The target node
15881          * @param {Roo.EventObject} e The raw event object
15882          */
15883             "dblclick" : true,
15884         /**
15885          * @event contextmenu
15886          * Fires when a template node is right clicked.
15887          * @param {Roo.View} this
15888          * @param {Number} index The index of the target node
15889          * @param {HTMLElement} node The target node
15890          * @param {Roo.EventObject} e The raw event object
15891          */
15892             "contextmenu" : true,
15893         /**
15894          * @event selectionchange
15895          * Fires when the selected nodes change.
15896          * @param {Roo.View} this
15897          * @param {Array} selections Array of the selected nodes
15898          */
15899             "selectionchange" : true,
15900     
15901         /**
15902          * @event beforeselect
15903          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15904          * @param {Roo.View} this
15905          * @param {HTMLElement} node The node to be selected
15906          * @param {Array} selections Array of currently selected nodes
15907          */
15908             "beforeselect" : true,
15909         /**
15910          * @event preparedata
15911          * Fires on every row to render, to allow you to change the data.
15912          * @param {Roo.View} this
15913          * @param {Object} data to be rendered (change this)
15914          */
15915           "preparedata" : true
15916           
15917           
15918         });
15919
15920
15921
15922     this.el.on({
15923         "click": this.onClick,
15924         "dblclick": this.onDblClick,
15925         "contextmenu": this.onContextMenu,
15926         scope:this
15927     });
15928
15929     this.selections = [];
15930     this.nodes = [];
15931     this.cmp = new Roo.CompositeElementLite([]);
15932     if(this.store){
15933         this.store = Roo.factory(this.store, Roo.data);
15934         this.setStore(this.store, true);
15935     }
15936     
15937     if ( this.footer && this.footer.xtype) {
15938            
15939          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15940         
15941         this.footer.dataSource = this.store;
15942         this.footer.container = fctr;
15943         this.footer = Roo.factory(this.footer, Roo);
15944         fctr.insertFirst(this.el);
15945         
15946         // this is a bit insane - as the paging toolbar seems to detach the el..
15947 //        dom.parentNode.parentNode.parentNode
15948          // they get detached?
15949     }
15950     
15951     
15952     Roo.View.superclass.constructor.call(this);
15953     
15954     
15955 };
15956
15957 Roo.extend(Roo.View, Roo.util.Observable, {
15958     
15959      /**
15960      * @cfg {Roo.data.Store} store Data store to load data from.
15961      */
15962     store : false,
15963     
15964     /**
15965      * @cfg {String|Roo.Element} el The container element.
15966      */
15967     el : '',
15968     
15969     /**
15970      * @cfg {String|Roo.Template} tpl The template used by this View 
15971      */
15972     tpl : false,
15973     /**
15974      * @cfg {String} dataName the named area of the template to use as the data area
15975      *                          Works with domtemplates roo-name="name"
15976      */
15977     dataName: false,
15978     /**
15979      * @cfg {String} selectedClass The css class to add to selected nodes
15980      */
15981     selectedClass : "x-view-selected",
15982      /**
15983      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15984      */
15985     emptyText : "",
15986     
15987     /**
15988      * @cfg {String} text to display on mask (default Loading)
15989      */
15990     mask : false,
15991     /**
15992      * @cfg {Boolean} multiSelect Allow multiple selection
15993      */
15994     multiSelect : false,
15995     /**
15996      * @cfg {Boolean} singleSelect Allow single selection
15997      */
15998     singleSelect:  false,
15999     
16000     /**
16001      * @cfg {Boolean} toggleSelect - selecting 
16002      */
16003     toggleSelect : false,
16004     
16005     /**
16006      * @cfg {Boolean} tickable - selecting 
16007      */
16008     tickable : false,
16009     
16010     /**
16011      * Returns the element this view is bound to.
16012      * @return {Roo.Element}
16013      */
16014     getEl : function(){
16015         return this.wrapEl;
16016     },
16017     
16018     
16019
16020     /**
16021      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16022      */
16023     refresh : function(){
16024         //Roo.log('refresh');
16025         var t = this.tpl;
16026         
16027         // if we are using something like 'domtemplate', then
16028         // the what gets used is:
16029         // t.applySubtemplate(NAME, data, wrapping data..)
16030         // the outer template then get' applied with
16031         //     the store 'extra data'
16032         // and the body get's added to the
16033         //      roo-name="data" node?
16034         //      <span class='roo-tpl-{name}'></span> ?????
16035         
16036         
16037         
16038         this.clearSelections();
16039         this.el.update("");
16040         var html = [];
16041         var records = this.store.getRange();
16042         if(records.length < 1) {
16043             
16044             // is this valid??  = should it render a template??
16045             
16046             this.el.update(this.emptyText);
16047             return;
16048         }
16049         var el = this.el;
16050         if (this.dataName) {
16051             this.el.update(t.apply(this.store.meta)); //????
16052             el = this.el.child('.roo-tpl-' + this.dataName);
16053         }
16054         
16055         for(var i = 0, len = records.length; i < len; i++){
16056             var data = this.prepareData(records[i].data, i, records[i]);
16057             this.fireEvent("preparedata", this, data, i, records[i]);
16058             
16059             var d = Roo.apply({}, data);
16060             
16061             if(this.tickable){
16062                 Roo.apply(d, {'roo-id' : Roo.id()});
16063                 
16064                 var _this = this;
16065             
16066                 Roo.each(this.parent.item, function(item){
16067                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16068                         return;
16069                     }
16070                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16071                 });
16072             }
16073             
16074             html[html.length] = Roo.util.Format.trim(
16075                 this.dataName ?
16076                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16077                     t.apply(d)
16078             );
16079         }
16080         
16081         
16082         
16083         el.update(html.join(""));
16084         this.nodes = el.dom.childNodes;
16085         this.updateIndexes(0);
16086     },
16087     
16088
16089     /**
16090      * Function to override to reformat the data that is sent to
16091      * the template for each node.
16092      * DEPRICATED - use the preparedata event handler.
16093      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16094      * a JSON object for an UpdateManager bound view).
16095      */
16096     prepareData : function(data, index, record)
16097     {
16098         this.fireEvent("preparedata", this, data, index, record);
16099         return data;
16100     },
16101
16102     onUpdate : function(ds, record){
16103         // Roo.log('on update');   
16104         this.clearSelections();
16105         var index = this.store.indexOf(record);
16106         var n = this.nodes[index];
16107         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16108         n.parentNode.removeChild(n);
16109         this.updateIndexes(index, index);
16110     },
16111
16112     
16113     
16114 // --------- FIXME     
16115     onAdd : function(ds, records, index)
16116     {
16117         //Roo.log(['on Add', ds, records, index] );        
16118         this.clearSelections();
16119         if(this.nodes.length == 0){
16120             this.refresh();
16121             return;
16122         }
16123         var n = this.nodes[index];
16124         for(var i = 0, len = records.length; i < len; i++){
16125             var d = this.prepareData(records[i].data, i, records[i]);
16126             if(n){
16127                 this.tpl.insertBefore(n, d);
16128             }else{
16129                 
16130                 this.tpl.append(this.el, d);
16131             }
16132         }
16133         this.updateIndexes(index);
16134     },
16135
16136     onRemove : function(ds, record, index){
16137        // Roo.log('onRemove');
16138         this.clearSelections();
16139         var el = this.dataName  ?
16140             this.el.child('.roo-tpl-' + this.dataName) :
16141             this.el; 
16142         
16143         el.dom.removeChild(this.nodes[index]);
16144         this.updateIndexes(index);
16145     },
16146
16147     /**
16148      * Refresh an individual node.
16149      * @param {Number} index
16150      */
16151     refreshNode : function(index){
16152         this.onUpdate(this.store, this.store.getAt(index));
16153     },
16154
16155     updateIndexes : function(startIndex, endIndex){
16156         var ns = this.nodes;
16157         startIndex = startIndex || 0;
16158         endIndex = endIndex || ns.length - 1;
16159         for(var i = startIndex; i <= endIndex; i++){
16160             ns[i].nodeIndex = i;
16161         }
16162     },
16163
16164     /**
16165      * Changes the data store this view uses and refresh the view.
16166      * @param {Store} store
16167      */
16168     setStore : function(store, initial){
16169         if(!initial && this.store){
16170             this.store.un("datachanged", this.refresh);
16171             this.store.un("add", this.onAdd);
16172             this.store.un("remove", this.onRemove);
16173             this.store.un("update", this.onUpdate);
16174             this.store.un("clear", this.refresh);
16175             this.store.un("beforeload", this.onBeforeLoad);
16176             this.store.un("load", this.onLoad);
16177             this.store.un("loadexception", this.onLoad);
16178         }
16179         if(store){
16180           
16181             store.on("datachanged", this.refresh, this);
16182             store.on("add", this.onAdd, this);
16183             store.on("remove", this.onRemove, this);
16184             store.on("update", this.onUpdate, this);
16185             store.on("clear", this.refresh, this);
16186             store.on("beforeload", this.onBeforeLoad, this);
16187             store.on("load", this.onLoad, this);
16188             store.on("loadexception", this.onLoad, this);
16189         }
16190         
16191         if(store){
16192             this.refresh();
16193         }
16194     },
16195     /**
16196      * onbeforeLoad - masks the loading area.
16197      *
16198      */
16199     onBeforeLoad : function(store,opts)
16200     {
16201          //Roo.log('onBeforeLoad');   
16202         if (!opts.add) {
16203             this.el.update("");
16204         }
16205         this.el.mask(this.mask ? this.mask : "Loading" ); 
16206     },
16207     onLoad : function ()
16208     {
16209         this.el.unmask();
16210     },
16211     
16212
16213     /**
16214      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16215      * @param {HTMLElement} node
16216      * @return {HTMLElement} The template node
16217      */
16218     findItemFromChild : function(node){
16219         var el = this.dataName  ?
16220             this.el.child('.roo-tpl-' + this.dataName,true) :
16221             this.el.dom; 
16222         
16223         if(!node || node.parentNode == el){
16224                     return node;
16225             }
16226             var p = node.parentNode;
16227             while(p && p != el){
16228             if(p.parentNode == el){
16229                 return p;
16230             }
16231             p = p.parentNode;
16232         }
16233             return null;
16234     },
16235
16236     /** @ignore */
16237     onClick : function(e){
16238         var item = this.findItemFromChild(e.getTarget());
16239         if(item){
16240             var index = this.indexOf(item);
16241             if(this.onItemClick(item, index, e) !== false){
16242                 this.fireEvent("click", this, index, item, e);
16243             }
16244         }else{
16245             this.clearSelections();
16246         }
16247     },
16248
16249     /** @ignore */
16250     onContextMenu : function(e){
16251         var item = this.findItemFromChild(e.getTarget());
16252         if(item){
16253             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16254         }
16255     },
16256
16257     /** @ignore */
16258     onDblClick : function(e){
16259         var item = this.findItemFromChild(e.getTarget());
16260         if(item){
16261             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16262         }
16263     },
16264
16265     onItemClick : function(item, index, e)
16266     {
16267         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16268             return false;
16269         }
16270         if (this.toggleSelect) {
16271             var m = this.isSelected(item) ? 'unselect' : 'select';
16272             //Roo.log(m);
16273             var _t = this;
16274             _t[m](item, true, false);
16275             return true;
16276         }
16277         if(this.multiSelect || this.singleSelect){
16278             if(this.multiSelect && e.shiftKey && this.lastSelection){
16279                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16280             }else{
16281                 this.select(item, this.multiSelect && e.ctrlKey);
16282                 this.lastSelection = item;
16283             }
16284             
16285             if(!this.tickable){
16286                 e.preventDefault();
16287             }
16288             
16289         }
16290         return true;
16291     },
16292
16293     /**
16294      * Get the number of selected nodes.
16295      * @return {Number}
16296      */
16297     getSelectionCount : function(){
16298         return this.selections.length;
16299     },
16300
16301     /**
16302      * Get the currently selected nodes.
16303      * @return {Array} An array of HTMLElements
16304      */
16305     getSelectedNodes : function(){
16306         return this.selections;
16307     },
16308
16309     /**
16310      * Get the indexes of the selected nodes.
16311      * @return {Array}
16312      */
16313     getSelectedIndexes : function(){
16314         var indexes = [], s = this.selections;
16315         for(var i = 0, len = s.length; i < len; i++){
16316             indexes.push(s[i].nodeIndex);
16317         }
16318         return indexes;
16319     },
16320
16321     /**
16322      * Clear all selections
16323      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16324      */
16325     clearSelections : function(suppressEvent){
16326         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16327             this.cmp.elements = this.selections;
16328             this.cmp.removeClass(this.selectedClass);
16329             this.selections = [];
16330             if(!suppressEvent){
16331                 this.fireEvent("selectionchange", this, this.selections);
16332             }
16333         }
16334     },
16335
16336     /**
16337      * Returns true if the passed node is selected
16338      * @param {HTMLElement/Number} node The node or node index
16339      * @return {Boolean}
16340      */
16341     isSelected : function(node){
16342         var s = this.selections;
16343         if(s.length < 1){
16344             return false;
16345         }
16346         node = this.getNode(node);
16347         return s.indexOf(node) !== -1;
16348     },
16349
16350     /**
16351      * Selects nodes.
16352      * @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
16353      * @param {Boolean} keepExisting (optional) true to keep existing selections
16354      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16355      */
16356     select : function(nodeInfo, keepExisting, suppressEvent){
16357         if(nodeInfo instanceof Array){
16358             if(!keepExisting){
16359                 this.clearSelections(true);
16360             }
16361             for(var i = 0, len = nodeInfo.length; i < len; i++){
16362                 this.select(nodeInfo[i], true, true);
16363             }
16364             return;
16365         } 
16366         var node = this.getNode(nodeInfo);
16367         if(!node || this.isSelected(node)){
16368             return; // already selected.
16369         }
16370         if(!keepExisting){
16371             this.clearSelections(true);
16372         }
16373         
16374         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16375             Roo.fly(node).addClass(this.selectedClass);
16376             this.selections.push(node);
16377             if(!suppressEvent){
16378                 this.fireEvent("selectionchange", this, this.selections);
16379             }
16380         }
16381         
16382         
16383     },
16384       /**
16385      * Unselects nodes.
16386      * @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
16387      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16388      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16389      */
16390     unselect : function(nodeInfo, keepExisting, suppressEvent)
16391     {
16392         if(nodeInfo instanceof Array){
16393             Roo.each(this.selections, function(s) {
16394                 this.unselect(s, nodeInfo);
16395             }, this);
16396             return;
16397         }
16398         var node = this.getNode(nodeInfo);
16399         if(!node || !this.isSelected(node)){
16400             //Roo.log("not selected");
16401             return; // not selected.
16402         }
16403         // fireevent???
16404         var ns = [];
16405         Roo.each(this.selections, function(s) {
16406             if (s == node ) {
16407                 Roo.fly(node).removeClass(this.selectedClass);
16408
16409                 return;
16410             }
16411             ns.push(s);
16412         },this);
16413         
16414         this.selections= ns;
16415         this.fireEvent("selectionchange", this, this.selections);
16416     },
16417
16418     /**
16419      * Gets a template node.
16420      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16421      * @return {HTMLElement} The node or null if it wasn't found
16422      */
16423     getNode : function(nodeInfo){
16424         if(typeof nodeInfo == "string"){
16425             return document.getElementById(nodeInfo);
16426         }else if(typeof nodeInfo == "number"){
16427             return this.nodes[nodeInfo];
16428         }
16429         return nodeInfo;
16430     },
16431
16432     /**
16433      * Gets a range template nodes.
16434      * @param {Number} startIndex
16435      * @param {Number} endIndex
16436      * @return {Array} An array of nodes
16437      */
16438     getNodes : function(start, end){
16439         var ns = this.nodes;
16440         start = start || 0;
16441         end = typeof end == "undefined" ? ns.length - 1 : end;
16442         var nodes = [];
16443         if(start <= end){
16444             for(var i = start; i <= end; i++){
16445                 nodes.push(ns[i]);
16446             }
16447         } else{
16448             for(var i = start; i >= end; i--){
16449                 nodes.push(ns[i]);
16450             }
16451         }
16452         return nodes;
16453     },
16454
16455     /**
16456      * Finds the index of the passed node
16457      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16458      * @return {Number} The index of the node or -1
16459      */
16460     indexOf : function(node){
16461         node = this.getNode(node);
16462         if(typeof node.nodeIndex == "number"){
16463             return node.nodeIndex;
16464         }
16465         var ns = this.nodes;
16466         for(var i = 0, len = ns.length; i < len; i++){
16467             if(ns[i] == node){
16468                 return i;
16469             }
16470         }
16471         return -1;
16472     }
16473 });
16474 /*
16475  * - LGPL
16476  *
16477  * based on jquery fullcalendar
16478  * 
16479  */
16480
16481 Roo.bootstrap = Roo.bootstrap || {};
16482 /**
16483  * @class Roo.bootstrap.Calendar
16484  * @extends Roo.bootstrap.Component
16485  * Bootstrap Calendar class
16486  * @cfg {Boolean} loadMask (true|false) default false
16487  * @cfg {Object} header generate the user specific header of the calendar, default false
16488
16489  * @constructor
16490  * Create a new Container
16491  * @param {Object} config The config object
16492  */
16493
16494
16495
16496 Roo.bootstrap.Calendar = function(config){
16497     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16498      this.addEvents({
16499         /**
16500              * @event select
16501              * Fires when a date is selected
16502              * @param {DatePicker} this
16503              * @param {Date} date The selected date
16504              */
16505         'select': true,
16506         /**
16507              * @event monthchange
16508              * Fires when the displayed month changes 
16509              * @param {DatePicker} this
16510              * @param {Date} date The selected month
16511              */
16512         'monthchange': true,
16513         /**
16514              * @event evententer
16515              * Fires when mouse over an event
16516              * @param {Calendar} this
16517              * @param {event} Event
16518              */
16519         'evententer': true,
16520         /**
16521              * @event eventleave
16522              * Fires when the mouse leaves an
16523              * @param {Calendar} this
16524              * @param {event}
16525              */
16526         'eventleave': true,
16527         /**
16528              * @event eventclick
16529              * Fires when the mouse click an
16530              * @param {Calendar} this
16531              * @param {event}
16532              */
16533         'eventclick': true
16534         
16535     });
16536
16537 };
16538
16539 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16540     
16541      /**
16542      * @cfg {Number} startDay
16543      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16544      */
16545     startDay : 0,
16546     
16547     loadMask : false,
16548     
16549     header : false,
16550       
16551     getAutoCreate : function(){
16552         
16553         
16554         var fc_button = function(name, corner, style, content ) {
16555             return Roo.apply({},{
16556                 tag : 'span',
16557                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16558                          (corner.length ?
16559                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16560                             ''
16561                         ),
16562                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16563                 unselectable: 'on'
16564             });
16565         };
16566         
16567         var header = {};
16568         
16569         if(!this.header){
16570             header = {
16571                 tag : 'table',
16572                 cls : 'fc-header',
16573                 style : 'width:100%',
16574                 cn : [
16575                     {
16576                         tag: 'tr',
16577                         cn : [
16578                             {
16579                                 tag : 'td',
16580                                 cls : 'fc-header-left',
16581                                 cn : [
16582                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16583                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16584                                     { tag: 'span', cls: 'fc-header-space' },
16585                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16586
16587
16588                                 ]
16589                             },
16590
16591                             {
16592                                 tag : 'td',
16593                                 cls : 'fc-header-center',
16594                                 cn : [
16595                                     {
16596                                         tag: 'span',
16597                                         cls: 'fc-header-title',
16598                                         cn : {
16599                                             tag: 'H2',
16600                                             html : 'month / year'
16601                                         }
16602                                     }
16603
16604                                 ]
16605                             },
16606                             {
16607                                 tag : 'td',
16608                                 cls : 'fc-header-right',
16609                                 cn : [
16610                               /*      fc_button('month', 'left', '', 'month' ),
16611                                     fc_button('week', '', '', 'week' ),
16612                                     fc_button('day', 'right', '', 'day' )
16613                                 */    
16614
16615                                 ]
16616                             }
16617
16618                         ]
16619                     }
16620                 ]
16621             };
16622         }
16623         
16624         header = this.header;
16625         
16626        
16627         var cal_heads = function() {
16628             var ret = [];
16629             // fixme - handle this.
16630             
16631             for (var i =0; i < Date.dayNames.length; i++) {
16632                 var d = Date.dayNames[i];
16633                 ret.push({
16634                     tag: 'th',
16635                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16636                     html : d.substring(0,3)
16637                 });
16638                 
16639             }
16640             ret[0].cls += ' fc-first';
16641             ret[6].cls += ' fc-last';
16642             return ret;
16643         };
16644         var cal_cell = function(n) {
16645             return  {
16646                 tag: 'td',
16647                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16648                 cn : [
16649                     {
16650                         cn : [
16651                             {
16652                                 cls: 'fc-day-number',
16653                                 html: 'D'
16654                             },
16655                             {
16656                                 cls: 'fc-day-content',
16657                              
16658                                 cn : [
16659                                      {
16660                                         style: 'position: relative;' // height: 17px;
16661                                     }
16662                                 ]
16663                             }
16664                             
16665                             
16666                         ]
16667                     }
16668                 ]
16669                 
16670             }
16671         };
16672         var cal_rows = function() {
16673             
16674             var ret = [];
16675             for (var r = 0; r < 6; r++) {
16676                 var row= {
16677                     tag : 'tr',
16678                     cls : 'fc-week',
16679                     cn : []
16680                 };
16681                 
16682                 for (var i =0; i < Date.dayNames.length; i++) {
16683                     var d = Date.dayNames[i];
16684                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16685
16686                 }
16687                 row.cn[0].cls+=' fc-first';
16688                 row.cn[0].cn[0].style = 'min-height:90px';
16689                 row.cn[6].cls+=' fc-last';
16690                 ret.push(row);
16691                 
16692             }
16693             ret[0].cls += ' fc-first';
16694             ret[4].cls += ' fc-prev-last';
16695             ret[5].cls += ' fc-last';
16696             return ret;
16697             
16698         };
16699         
16700         var cal_table = {
16701             tag: 'table',
16702             cls: 'fc-border-separate',
16703             style : 'width:100%',
16704             cellspacing  : 0,
16705             cn : [
16706                 { 
16707                     tag: 'thead',
16708                     cn : [
16709                         { 
16710                             tag: 'tr',
16711                             cls : 'fc-first fc-last',
16712                             cn : cal_heads()
16713                         }
16714                     ]
16715                 },
16716                 { 
16717                     tag: 'tbody',
16718                     cn : cal_rows()
16719                 }
16720                   
16721             ]
16722         };
16723          
16724          var cfg = {
16725             cls : 'fc fc-ltr',
16726             cn : [
16727                 header,
16728                 {
16729                     cls : 'fc-content',
16730                     style : "position: relative;",
16731                     cn : [
16732                         {
16733                             cls : 'fc-view fc-view-month fc-grid',
16734                             style : 'position: relative',
16735                             unselectable : 'on',
16736                             cn : [
16737                                 {
16738                                     cls : 'fc-event-container',
16739                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16740                                 },
16741                                 cal_table
16742                             ]
16743                         }
16744                     ]
16745     
16746                 }
16747            ] 
16748             
16749         };
16750         
16751          
16752         
16753         return cfg;
16754     },
16755     
16756     
16757     initEvents : function()
16758     {
16759         if(!this.store){
16760             throw "can not find store for calendar";
16761         }
16762         
16763         var mark = {
16764             tag: "div",
16765             cls:"x-dlg-mask",
16766             style: "text-align:center",
16767             cn: [
16768                 {
16769                     tag: "div",
16770                     style: "background-color:white;width:50%;margin:250 auto",
16771                     cn: [
16772                         {
16773                             tag: "img",
16774                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16775                         },
16776                         {
16777                             tag: "span",
16778                             html: "Loading"
16779                         }
16780                         
16781                     ]
16782                 }
16783             ]
16784         };
16785         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16786         
16787         var size = this.el.select('.fc-content', true).first().getSize();
16788         this.maskEl.setSize(size.width, size.height);
16789         this.maskEl.enableDisplayMode("block");
16790         if(!this.loadMask){
16791             this.maskEl.hide();
16792         }
16793         
16794         this.store = Roo.factory(this.store, Roo.data);
16795         this.store.on('load', this.onLoad, this);
16796         this.store.on('beforeload', this.onBeforeLoad, this);
16797         
16798         this.resize();
16799         
16800         this.cells = this.el.select('.fc-day',true);
16801         //Roo.log(this.cells);
16802         this.textNodes = this.el.query('.fc-day-number');
16803         this.cells.addClassOnOver('fc-state-hover');
16804         
16805         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16806         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16807         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16808         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16809         
16810         this.on('monthchange', this.onMonthChange, this);
16811         
16812         this.update(new Date().clearTime());
16813     },
16814     
16815     resize : function() {
16816         var sz  = this.el.getSize();
16817         
16818         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16819         this.el.select('.fc-day-content div',true).setHeight(34);
16820     },
16821     
16822     
16823     // private
16824     showPrevMonth : function(e){
16825         this.update(this.activeDate.add("mo", -1));
16826     },
16827     showToday : function(e){
16828         this.update(new Date().clearTime());
16829     },
16830     // private
16831     showNextMonth : function(e){
16832         this.update(this.activeDate.add("mo", 1));
16833     },
16834
16835     // private
16836     showPrevYear : function(){
16837         this.update(this.activeDate.add("y", -1));
16838     },
16839
16840     // private
16841     showNextYear : function(){
16842         this.update(this.activeDate.add("y", 1));
16843     },
16844
16845     
16846    // private
16847     update : function(date)
16848     {
16849         var vd = this.activeDate;
16850         this.activeDate = date;
16851 //        if(vd && this.el){
16852 //            var t = date.getTime();
16853 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16854 //                Roo.log('using add remove');
16855 //                
16856 //                this.fireEvent('monthchange', this, date);
16857 //                
16858 //                this.cells.removeClass("fc-state-highlight");
16859 //                this.cells.each(function(c){
16860 //                   if(c.dateValue == t){
16861 //                       c.addClass("fc-state-highlight");
16862 //                       setTimeout(function(){
16863 //                            try{c.dom.firstChild.focus();}catch(e){}
16864 //                       }, 50);
16865 //                       return false;
16866 //                   }
16867 //                   return true;
16868 //                });
16869 //                return;
16870 //            }
16871 //        }
16872         
16873         var days = date.getDaysInMonth();
16874         
16875         var firstOfMonth = date.getFirstDateOfMonth();
16876         var startingPos = firstOfMonth.getDay()-this.startDay;
16877         
16878         if(startingPos < this.startDay){
16879             startingPos += 7;
16880         }
16881         
16882         var pm = date.add(Date.MONTH, -1);
16883         var prevStart = pm.getDaysInMonth()-startingPos;
16884 //        
16885         this.cells = this.el.select('.fc-day',true);
16886         this.textNodes = this.el.query('.fc-day-number');
16887         this.cells.addClassOnOver('fc-state-hover');
16888         
16889         var cells = this.cells.elements;
16890         var textEls = this.textNodes;
16891         
16892         Roo.each(cells, function(cell){
16893             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16894         });
16895         
16896         days += startingPos;
16897
16898         // convert everything to numbers so it's fast
16899         var day = 86400000;
16900         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16901         //Roo.log(d);
16902         //Roo.log(pm);
16903         //Roo.log(prevStart);
16904         
16905         var today = new Date().clearTime().getTime();
16906         var sel = date.clearTime().getTime();
16907         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16908         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16909         var ddMatch = this.disabledDatesRE;
16910         var ddText = this.disabledDatesText;
16911         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16912         var ddaysText = this.disabledDaysText;
16913         var format = this.format;
16914         
16915         var setCellClass = function(cal, cell){
16916             cell.row = 0;
16917             cell.events = [];
16918             cell.more = [];
16919             //Roo.log('set Cell Class');
16920             cell.title = "";
16921             var t = d.getTime();
16922             
16923             //Roo.log(d);
16924             
16925             cell.dateValue = t;
16926             if(t == today){
16927                 cell.className += " fc-today";
16928                 cell.className += " fc-state-highlight";
16929                 cell.title = cal.todayText;
16930             }
16931             if(t == sel){
16932                 // disable highlight in other month..
16933                 //cell.className += " fc-state-highlight";
16934                 
16935             }
16936             // disabling
16937             if(t < min) {
16938                 cell.className = " fc-state-disabled";
16939                 cell.title = cal.minText;
16940                 return;
16941             }
16942             if(t > max) {
16943                 cell.className = " fc-state-disabled";
16944                 cell.title = cal.maxText;
16945                 return;
16946             }
16947             if(ddays){
16948                 if(ddays.indexOf(d.getDay()) != -1){
16949                     cell.title = ddaysText;
16950                     cell.className = " fc-state-disabled";
16951                 }
16952             }
16953             if(ddMatch && format){
16954                 var fvalue = d.dateFormat(format);
16955                 if(ddMatch.test(fvalue)){
16956                     cell.title = ddText.replace("%0", fvalue);
16957                     cell.className = " fc-state-disabled";
16958                 }
16959             }
16960             
16961             if (!cell.initialClassName) {
16962                 cell.initialClassName = cell.dom.className;
16963             }
16964             
16965             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16966         };
16967
16968         var i = 0;
16969         
16970         for(; i < startingPos; i++) {
16971             textEls[i].innerHTML = (++prevStart);
16972             d.setDate(d.getDate()+1);
16973             
16974             cells[i].className = "fc-past fc-other-month";
16975             setCellClass(this, cells[i]);
16976         }
16977         
16978         var intDay = 0;
16979         
16980         for(; i < days; i++){
16981             intDay = i - startingPos + 1;
16982             textEls[i].innerHTML = (intDay);
16983             d.setDate(d.getDate()+1);
16984             
16985             cells[i].className = ''; // "x-date-active";
16986             setCellClass(this, cells[i]);
16987         }
16988         var extraDays = 0;
16989         
16990         for(; i < 42; i++) {
16991             textEls[i].innerHTML = (++extraDays);
16992             d.setDate(d.getDate()+1);
16993             
16994             cells[i].className = "fc-future fc-other-month";
16995             setCellClass(this, cells[i]);
16996         }
16997         
16998         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16999         
17000         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17001         
17002         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17003         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17004         
17005         if(totalRows != 6){
17006             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17007             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17008         }
17009         
17010         this.fireEvent('monthchange', this, date);
17011         
17012         
17013         /*
17014         if(!this.internalRender){
17015             var main = this.el.dom.firstChild;
17016             var w = main.offsetWidth;
17017             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17018             Roo.fly(main).setWidth(w);
17019             this.internalRender = true;
17020             // opera does not respect the auto grow header center column
17021             // then, after it gets a width opera refuses to recalculate
17022             // without a second pass
17023             if(Roo.isOpera && !this.secondPass){
17024                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17025                 this.secondPass = true;
17026                 this.update.defer(10, this, [date]);
17027             }
17028         }
17029         */
17030         
17031     },
17032     
17033     findCell : function(dt) {
17034         dt = dt.clearTime().getTime();
17035         var ret = false;
17036         this.cells.each(function(c){
17037             //Roo.log("check " +c.dateValue + '?=' + dt);
17038             if(c.dateValue == dt){
17039                 ret = c;
17040                 return false;
17041             }
17042             return true;
17043         });
17044         
17045         return ret;
17046     },
17047     
17048     findCells : function(ev) {
17049         var s = ev.start.clone().clearTime().getTime();
17050        // Roo.log(s);
17051         var e= ev.end.clone().clearTime().getTime();
17052        // Roo.log(e);
17053         var ret = [];
17054         this.cells.each(function(c){
17055              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17056             
17057             if(c.dateValue > e){
17058                 return ;
17059             }
17060             if(c.dateValue < s){
17061                 return ;
17062             }
17063             ret.push(c);
17064         });
17065         
17066         return ret;    
17067     },
17068     
17069 //    findBestRow: function(cells)
17070 //    {
17071 //        var ret = 0;
17072 //        
17073 //        for (var i =0 ; i < cells.length;i++) {
17074 //            ret  = Math.max(cells[i].rows || 0,ret);
17075 //        }
17076 //        return ret;
17077 //        
17078 //    },
17079     
17080     
17081     addItem : function(ev)
17082     {
17083         // look for vertical location slot in
17084         var cells = this.findCells(ev);
17085         
17086 //        ev.row = this.findBestRow(cells);
17087         
17088         // work out the location.
17089         
17090         var crow = false;
17091         var rows = [];
17092         for(var i =0; i < cells.length; i++) {
17093             
17094             cells[i].row = cells[0].row;
17095             
17096             if(i == 0){
17097                 cells[i].row = cells[i].row + 1;
17098             }
17099             
17100             if (!crow) {
17101                 crow = {
17102                     start : cells[i],
17103                     end :  cells[i]
17104                 };
17105                 continue;
17106             }
17107             if (crow.start.getY() == cells[i].getY()) {
17108                 // on same row.
17109                 crow.end = cells[i];
17110                 continue;
17111             }
17112             // different row.
17113             rows.push(crow);
17114             crow = {
17115                 start: cells[i],
17116                 end : cells[i]
17117             };
17118             
17119         }
17120         
17121         rows.push(crow);
17122         ev.els = [];
17123         ev.rows = rows;
17124         ev.cells = cells;
17125         
17126         cells[0].events.push(ev);
17127         
17128         this.calevents.push(ev);
17129     },
17130     
17131     clearEvents: function() {
17132         
17133         if(!this.calevents){
17134             return;
17135         }
17136         
17137         Roo.each(this.cells.elements, function(c){
17138             c.row = 0;
17139             c.events = [];
17140             c.more = [];
17141         });
17142         
17143         Roo.each(this.calevents, function(e) {
17144             Roo.each(e.els, function(el) {
17145                 el.un('mouseenter' ,this.onEventEnter, this);
17146                 el.un('mouseleave' ,this.onEventLeave, this);
17147                 el.remove();
17148             },this);
17149         },this);
17150         
17151         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17152             e.remove();
17153         });
17154         
17155     },
17156     
17157     renderEvents: function()
17158     {   
17159         var _this = this;
17160         
17161         this.cells.each(function(c) {
17162             
17163             if(c.row < 5){
17164                 return;
17165             }
17166             
17167             var ev = c.events;
17168             
17169             var r = 4;
17170             if(c.row != c.events.length){
17171                 r = 4 - (4 - (c.row - c.events.length));
17172             }
17173             
17174             c.events = ev.slice(0, r);
17175             c.more = ev.slice(r);
17176             
17177             if(c.more.length && c.more.length == 1){
17178                 c.events.push(c.more.pop());
17179             }
17180             
17181             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17182             
17183         });
17184             
17185         this.cells.each(function(c) {
17186             
17187             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17188             
17189             
17190             for (var e = 0; e < c.events.length; e++){
17191                 var ev = c.events[e];
17192                 var rows = ev.rows;
17193                 
17194                 for(var i = 0; i < rows.length; i++) {
17195                 
17196                     // how many rows should it span..
17197
17198                     var  cfg = {
17199                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17200                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17201
17202                         unselectable : "on",
17203                         cn : [
17204                             {
17205                                 cls: 'fc-event-inner',
17206                                 cn : [
17207     //                                {
17208     //                                  tag:'span',
17209     //                                  cls: 'fc-event-time',
17210     //                                  html : cells.length > 1 ? '' : ev.time
17211     //                                },
17212                                     {
17213                                       tag:'span',
17214                                       cls: 'fc-event-title',
17215                                       html : String.format('{0}', ev.title)
17216                                     }
17217
17218
17219                                 ]
17220                             },
17221                             {
17222                                 cls: 'ui-resizable-handle ui-resizable-e',
17223                                 html : '&nbsp;&nbsp;&nbsp'
17224                             }
17225
17226                         ]
17227                     };
17228
17229                     if (i == 0) {
17230                         cfg.cls += ' fc-event-start';
17231                     }
17232                     if ((i+1) == rows.length) {
17233                         cfg.cls += ' fc-event-end';
17234                     }
17235
17236                     var ctr = _this.el.select('.fc-event-container',true).first();
17237                     var cg = ctr.createChild(cfg);
17238
17239                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17240                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17241
17242                     var r = (c.more.length) ? 1 : 0;
17243                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17244                     cg.setWidth(ebox.right - sbox.x -2);
17245
17246                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17247                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17248                     cg.on('click', _this.onEventClick, _this, ev);
17249
17250                     ev.els.push(cg);
17251                     
17252                 }
17253                 
17254             }
17255             
17256             
17257             if(c.more.length){
17258                 var  cfg = {
17259                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17260                     style : 'position: absolute',
17261                     unselectable : "on",
17262                     cn : [
17263                         {
17264                             cls: 'fc-event-inner',
17265                             cn : [
17266                                 {
17267                                   tag:'span',
17268                                   cls: 'fc-event-title',
17269                                   html : 'More'
17270                                 }
17271
17272
17273                             ]
17274                         },
17275                         {
17276                             cls: 'ui-resizable-handle ui-resizable-e',
17277                             html : '&nbsp;&nbsp;&nbsp'
17278                         }
17279
17280                     ]
17281                 };
17282
17283                 var ctr = _this.el.select('.fc-event-container',true).first();
17284                 var cg = ctr.createChild(cfg);
17285
17286                 var sbox = c.select('.fc-day-content',true).first().getBox();
17287                 var ebox = c.select('.fc-day-content',true).first().getBox();
17288                 //Roo.log(cg);
17289                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17290                 cg.setWidth(ebox.right - sbox.x -2);
17291
17292                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17293                 
17294             }
17295             
17296         });
17297         
17298         
17299         
17300     },
17301     
17302     onEventEnter: function (e, el,event,d) {
17303         this.fireEvent('evententer', this, el, event);
17304     },
17305     
17306     onEventLeave: function (e, el,event,d) {
17307         this.fireEvent('eventleave', this, el, event);
17308     },
17309     
17310     onEventClick: function (e, el,event,d) {
17311         this.fireEvent('eventclick', this, el, event);
17312     },
17313     
17314     onMonthChange: function () {
17315         this.store.load();
17316     },
17317     
17318     onMoreEventClick: function(e, el, more)
17319     {
17320         var _this = this;
17321         
17322         this.calpopover.placement = 'right';
17323         this.calpopover.setTitle('More');
17324         
17325         this.calpopover.setContent('');
17326         
17327         var ctr = this.calpopover.el.select('.popover-content', true).first();
17328         
17329         Roo.each(more, function(m){
17330             var cfg = {
17331                 cls : 'fc-event-hori fc-event-draggable',
17332                 html : m.title
17333             };
17334             var cg = ctr.createChild(cfg);
17335             
17336             cg.on('click', _this.onEventClick, _this, m);
17337         });
17338         
17339         this.calpopover.show(el);
17340         
17341         
17342     },
17343     
17344     onLoad: function () 
17345     {   
17346         this.calevents = [];
17347         var cal = this;
17348         
17349         if(this.store.getCount() > 0){
17350             this.store.data.each(function(d){
17351                cal.addItem({
17352                     id : d.data.id,
17353                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17354                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17355                     time : d.data.start_time,
17356                     title : d.data.title,
17357                     description : d.data.description,
17358                     venue : d.data.venue
17359                 });
17360             });
17361         }
17362         
17363         this.renderEvents();
17364         
17365         if(this.calevents.length && this.loadMask){
17366             this.maskEl.hide();
17367         }
17368     },
17369     
17370     onBeforeLoad: function()
17371     {
17372         this.clearEvents();
17373         if(this.loadMask){
17374             this.maskEl.show();
17375         }
17376     }
17377 });
17378
17379  
17380  /*
17381  * - LGPL
17382  *
17383  * element
17384  * 
17385  */
17386
17387 /**
17388  * @class Roo.bootstrap.Popover
17389  * @extends Roo.bootstrap.Component
17390  * Bootstrap Popover class
17391  * @cfg {String} html contents of the popover   (or false to use children..)
17392  * @cfg {String} title of popover (or false to hide)
17393  * @cfg {String} placement how it is placed
17394  * @cfg {String} trigger click || hover (or false to trigger manually)
17395  * @cfg {String} over what (parent or false to trigger manually.)
17396  * @cfg {Number} delay - delay before showing
17397  
17398  * @constructor
17399  * Create a new Popover
17400  * @param {Object} config The config object
17401  */
17402
17403 Roo.bootstrap.Popover = function(config){
17404     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17405     
17406     this.addEvents({
17407         // raw events
17408          /**
17409          * @event show
17410          * After the popover show
17411          * 
17412          * @param {Roo.bootstrap.Popover} this
17413          */
17414         "show" : true,
17415         /**
17416          * @event hide
17417          * After the popover hide
17418          * 
17419          * @param {Roo.bootstrap.Popover} this
17420          */
17421         "hide" : true
17422     });
17423 };
17424
17425 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17426     
17427     title: 'Fill in a title',
17428     html: false,
17429     
17430     placement : 'right',
17431     trigger : 'hover', // hover
17432     
17433     delay : 0,
17434     
17435     over: 'parent',
17436     
17437     can_build_overlaid : false,
17438     
17439     getChildContainer : function()
17440     {
17441         return this.el.select('.popover-content',true).first();
17442     },
17443     
17444     getAutoCreate : function(){
17445          
17446         var cfg = {
17447            cls : 'popover roo-dynamic',
17448            style: 'display:block',
17449            cn : [
17450                 {
17451                     cls : 'arrow'
17452                 },
17453                 {
17454                     cls : 'popover-inner',
17455                     cn : [
17456                         {
17457                             tag: 'h3',
17458                             cls: 'popover-title',
17459                             html : this.title
17460                         },
17461                         {
17462                             cls : 'popover-content',
17463                             html : this.html
17464                         }
17465                     ]
17466                     
17467                 }
17468            ]
17469         };
17470         
17471         return cfg;
17472     },
17473     setTitle: function(str)
17474     {
17475         this.title = str;
17476         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17477     },
17478     setContent: function(str)
17479     {
17480         this.html = str;
17481         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17482     },
17483     // as it get's added to the bottom of the page.
17484     onRender : function(ct, position)
17485     {
17486         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17487         if(!this.el){
17488             var cfg = Roo.apply({},  this.getAutoCreate());
17489             cfg.id = Roo.id();
17490             
17491             if (this.cls) {
17492                 cfg.cls += ' ' + this.cls;
17493             }
17494             if (this.style) {
17495                 cfg.style = this.style;
17496             }
17497             //Roo.log("adding to ");
17498             this.el = Roo.get(document.body).createChild(cfg, position);
17499 //            Roo.log(this.el);
17500         }
17501         this.initEvents();
17502     },
17503     
17504     initEvents : function()
17505     {
17506         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17507         this.el.enableDisplayMode('block');
17508         this.el.hide();
17509         if (this.over === false) {
17510             return; 
17511         }
17512         if (this.triggers === false) {
17513             return;
17514         }
17515         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17516         var triggers = this.trigger ? this.trigger.split(' ') : [];
17517         Roo.each(triggers, function(trigger) {
17518         
17519             if (trigger == 'click') {
17520                 on_el.on('click', this.toggle, this);
17521             } else if (trigger != 'manual') {
17522                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17523                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17524       
17525                 on_el.on(eventIn  ,this.enter, this);
17526                 on_el.on(eventOut, this.leave, this);
17527             }
17528         }, this);
17529         
17530     },
17531     
17532     
17533     // private
17534     timeout : null,
17535     hoverState : null,
17536     
17537     toggle : function () {
17538         this.hoverState == 'in' ? this.leave() : this.enter();
17539     },
17540     
17541     enter : function () {
17542         
17543         clearTimeout(this.timeout);
17544     
17545         this.hoverState = 'in';
17546     
17547         if (!this.delay || !this.delay.show) {
17548             this.show();
17549             return;
17550         }
17551         var _t = this;
17552         this.timeout = setTimeout(function () {
17553             if (_t.hoverState == 'in') {
17554                 _t.show();
17555             }
17556         }, this.delay.show)
17557     },
17558     
17559     leave : function() {
17560         clearTimeout(this.timeout);
17561     
17562         this.hoverState = 'out';
17563     
17564         if (!this.delay || !this.delay.hide) {
17565             this.hide();
17566             return;
17567         }
17568         var _t = this;
17569         this.timeout = setTimeout(function () {
17570             if (_t.hoverState == 'out') {
17571                 _t.hide();
17572             }
17573         }, this.delay.hide)
17574     },
17575     
17576     show : function (on_el)
17577     {
17578         if (!on_el) {
17579             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17580         }
17581         
17582         // set content.
17583         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17584         if (this.html !== false) {
17585             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17586         }
17587         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17588         if (!this.title.length) {
17589             this.el.select('.popover-title',true).hide();
17590         }
17591         
17592         var placement = typeof this.placement == 'function' ?
17593             this.placement.call(this, this.el, on_el) :
17594             this.placement;
17595             
17596         var autoToken = /\s?auto?\s?/i;
17597         var autoPlace = autoToken.test(placement);
17598         if (autoPlace) {
17599             placement = placement.replace(autoToken, '') || 'top';
17600         }
17601         
17602         //this.el.detach()
17603         //this.el.setXY([0,0]);
17604         this.el.show();
17605         this.el.dom.style.display='block';
17606         this.el.addClass(placement);
17607         
17608         //this.el.appendTo(on_el);
17609         
17610         var p = this.getPosition();
17611         var box = this.el.getBox();
17612         
17613         if (autoPlace) {
17614             // fixme..
17615         }
17616         var align = Roo.bootstrap.Popover.alignment[placement];
17617         
17618 //        Roo.log(align);
17619         this.el.alignTo(on_el, align[0],align[1]);
17620         //var arrow = this.el.select('.arrow',true).first();
17621         //arrow.set(align[2], 
17622         
17623         this.el.addClass('in');
17624         
17625         
17626         if (this.el.hasClass('fade')) {
17627             // fade it?
17628         }
17629         
17630         this.hoverState = 'in';
17631         
17632         this.fireEvent('show', this);
17633         
17634     },
17635     hide : function()
17636     {
17637         this.el.setXY([0,0]);
17638         this.el.removeClass('in');
17639         this.el.hide();
17640         this.hoverState = null;
17641         
17642         this.fireEvent('hide', this);
17643     }
17644     
17645 });
17646
17647 Roo.bootstrap.Popover.alignment = {
17648     'left' : ['r-l', [-10,0], 'right'],
17649     'right' : ['l-r', [10,0], 'left'],
17650     'bottom' : ['t-b', [0,10], 'top'],
17651     'top' : [ 'b-t', [0,-10], 'bottom']
17652 };
17653
17654  /*
17655  * - LGPL
17656  *
17657  * Progress
17658  * 
17659  */
17660
17661 /**
17662  * @class Roo.bootstrap.Progress
17663  * @extends Roo.bootstrap.Component
17664  * Bootstrap Progress class
17665  * @cfg {Boolean} striped striped of the progress bar
17666  * @cfg {Boolean} active animated of the progress bar
17667  * 
17668  * 
17669  * @constructor
17670  * Create a new Progress
17671  * @param {Object} config The config object
17672  */
17673
17674 Roo.bootstrap.Progress = function(config){
17675     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17676 };
17677
17678 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17679     
17680     striped : false,
17681     active: false,
17682     
17683     getAutoCreate : function(){
17684         var cfg = {
17685             tag: 'div',
17686             cls: 'progress'
17687         };
17688         
17689         
17690         if(this.striped){
17691             cfg.cls += ' progress-striped';
17692         }
17693       
17694         if(this.active){
17695             cfg.cls += ' active';
17696         }
17697         
17698         
17699         return cfg;
17700     }
17701    
17702 });
17703
17704  
17705
17706  /*
17707  * - LGPL
17708  *
17709  * ProgressBar
17710  * 
17711  */
17712
17713 /**
17714  * @class Roo.bootstrap.ProgressBar
17715  * @extends Roo.bootstrap.Component
17716  * Bootstrap ProgressBar class
17717  * @cfg {Number} aria_valuenow aria-value now
17718  * @cfg {Number} aria_valuemin aria-value min
17719  * @cfg {Number} aria_valuemax aria-value max
17720  * @cfg {String} label label for the progress bar
17721  * @cfg {String} panel (success | info | warning | danger )
17722  * @cfg {String} role role of the progress bar
17723  * @cfg {String} sr_only text
17724  * 
17725  * 
17726  * @constructor
17727  * Create a new ProgressBar
17728  * @param {Object} config The config object
17729  */
17730
17731 Roo.bootstrap.ProgressBar = function(config){
17732     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17733 };
17734
17735 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17736     
17737     aria_valuenow : 0,
17738     aria_valuemin : 0,
17739     aria_valuemax : 100,
17740     label : false,
17741     panel : false,
17742     role : false,
17743     sr_only: false,
17744     
17745     getAutoCreate : function()
17746     {
17747         
17748         var cfg = {
17749             tag: 'div',
17750             cls: 'progress-bar',
17751             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17752         };
17753         
17754         if(this.sr_only){
17755             cfg.cn = {
17756                 tag: 'span',
17757                 cls: 'sr-only',
17758                 html: this.sr_only
17759             }
17760         }
17761         
17762         if(this.role){
17763             cfg.role = this.role;
17764         }
17765         
17766         if(this.aria_valuenow){
17767             cfg['aria-valuenow'] = this.aria_valuenow;
17768         }
17769         
17770         if(this.aria_valuemin){
17771             cfg['aria-valuemin'] = this.aria_valuemin;
17772         }
17773         
17774         if(this.aria_valuemax){
17775             cfg['aria-valuemax'] = this.aria_valuemax;
17776         }
17777         
17778         if(this.label && !this.sr_only){
17779             cfg.html = this.label;
17780         }
17781         
17782         if(this.panel){
17783             cfg.cls += ' progress-bar-' + this.panel;
17784         }
17785         
17786         return cfg;
17787     },
17788     
17789     update : function(aria_valuenow)
17790     {
17791         this.aria_valuenow = aria_valuenow;
17792         
17793         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17794     }
17795    
17796 });
17797
17798  
17799
17800  /*
17801  * - LGPL
17802  *
17803  * column
17804  * 
17805  */
17806
17807 /**
17808  * @class Roo.bootstrap.TabGroup
17809  * @extends Roo.bootstrap.Column
17810  * Bootstrap Column class
17811  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17812  * @cfg {Boolean} carousel true to make the group behave like a carousel
17813  * @cfg {Boolean} bullets show bullets for the panels
17814  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17815  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17816  * @cfg {Boolean} showarrow (true|false) show arrow default true
17817  * 
17818  * @constructor
17819  * Create a new TabGroup
17820  * @param {Object} config The config object
17821  */
17822
17823 Roo.bootstrap.TabGroup = function(config){
17824     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17825     if (!this.navId) {
17826         this.navId = Roo.id();
17827     }
17828     this.tabs = [];
17829     Roo.bootstrap.TabGroup.register(this);
17830     
17831 };
17832
17833 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17834     
17835     carousel : false,
17836     transition : false,
17837     bullets : 0,
17838     timer : 0,
17839     autoslide : false,
17840     slideFn : false,
17841     slideOnTouch : false,
17842     showarrow : true,
17843     
17844     getAutoCreate : function()
17845     {
17846         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17847         
17848         cfg.cls += ' tab-content';
17849         
17850         if (this.carousel) {
17851             cfg.cls += ' carousel slide';
17852             
17853             cfg.cn = [{
17854                cls : 'carousel-inner',
17855                cn : []
17856             }];
17857         
17858             if(this.bullets  && !Roo.isTouch){
17859                 
17860                 var bullets = {
17861                     cls : 'carousel-bullets',
17862                     cn : []
17863                 };
17864                
17865                 if(this.bullets_cls){
17866                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17867                 }
17868                 
17869                 bullets.cn.push({
17870                     cls : 'clear'
17871                 });
17872                 
17873                 cfg.cn[0].cn.push(bullets);
17874             }
17875             
17876             if(this.showarrow){
17877                 cfg.cn[0].cn.push({
17878                     tag : 'div',
17879                     class : 'carousel-arrow',
17880                     cn : [
17881                         {
17882                             tag : 'div',
17883                             class : 'carousel-prev',
17884                             cn : [
17885                                 {
17886                                     tag : 'i',
17887                                     class : 'fa fa-chevron-left'
17888                                 }
17889                             ]
17890                         },
17891                         {
17892                             tag : 'div',
17893                             class : 'carousel-next',
17894                             cn : [
17895                                 {
17896                                     tag : 'i',
17897                                     class : 'fa fa-chevron-right'
17898                                 }
17899                             ]
17900                         }
17901                     ]
17902                 });
17903             }
17904             
17905         }
17906         
17907         return cfg;
17908     },
17909     
17910     initEvents:  function()
17911     {
17912 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17913 //            this.el.on("touchstart", this.onTouchStart, this);
17914 //        }
17915         
17916         if(this.autoslide){
17917             var _this = this;
17918             
17919             this.slideFn = window.setInterval(function() {
17920                 _this.showPanelNext();
17921             }, this.timer);
17922         }
17923         
17924         if(this.showarrow){
17925             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17926             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17927         }
17928         
17929         
17930     },
17931     
17932 //    onTouchStart : function(e, el, o)
17933 //    {
17934 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17935 //            return;
17936 //        }
17937 //        
17938 //        this.showPanelNext();
17939 //    },
17940     
17941     
17942     getChildContainer : function()
17943     {
17944         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17945     },
17946     
17947     /**
17948     * register a Navigation item
17949     * @param {Roo.bootstrap.NavItem} the navitem to add
17950     */
17951     register : function(item)
17952     {
17953         this.tabs.push( item);
17954         item.navId = this.navId; // not really needed..
17955         this.addBullet();
17956     
17957     },
17958     
17959     getActivePanel : function()
17960     {
17961         var r = false;
17962         Roo.each(this.tabs, function(t) {
17963             if (t.active) {
17964                 r = t;
17965                 return false;
17966             }
17967             return null;
17968         });
17969         return r;
17970         
17971     },
17972     getPanelByName : function(n)
17973     {
17974         var r = false;
17975         Roo.each(this.tabs, function(t) {
17976             if (t.tabId == n) {
17977                 r = t;
17978                 return false;
17979             }
17980             return null;
17981         });
17982         return r;
17983     },
17984     indexOfPanel : function(p)
17985     {
17986         var r = false;
17987         Roo.each(this.tabs, function(t,i) {
17988             if (t.tabId == p.tabId) {
17989                 r = i;
17990                 return false;
17991             }
17992             return null;
17993         });
17994         return r;
17995     },
17996     /**
17997      * show a specific panel
17998      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17999      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18000      */
18001     showPanel : function (pan)
18002     {
18003         if(this.transition || typeof(pan) == 'undefined'){
18004             Roo.log("waiting for the transitionend");
18005             return;
18006         }
18007         
18008         if (typeof(pan) == 'number') {
18009             pan = this.tabs[pan];
18010         }
18011         
18012         if (typeof(pan) == 'string') {
18013             pan = this.getPanelByName(pan);
18014         }
18015         
18016         var cur = this.getActivePanel();
18017         
18018         if(!pan || !cur){
18019             Roo.log('pan or acitve pan is undefined');
18020             return false;
18021         }
18022         
18023         if (pan.tabId == this.getActivePanel().tabId) {
18024             return true;
18025         }
18026         
18027         if (false === cur.fireEvent('beforedeactivate')) {
18028             return false;
18029         }
18030         
18031         if(this.bullets > 0 && !Roo.isTouch){
18032             this.setActiveBullet(this.indexOfPanel(pan));
18033         }
18034         
18035         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18036             
18037             this.transition = true;
18038             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18039             var lr = dir == 'next' ? 'left' : 'right';
18040             pan.el.addClass(dir); // or prev
18041             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18042             cur.el.addClass(lr); // or right
18043             pan.el.addClass(lr);
18044             
18045             var _this = this;
18046             cur.el.on('transitionend', function() {
18047                 Roo.log("trans end?");
18048                 
18049                 pan.el.removeClass([lr,dir]);
18050                 pan.setActive(true);
18051                 
18052                 cur.el.removeClass([lr]);
18053                 cur.setActive(false);
18054                 
18055                 _this.transition = false;
18056                 
18057             }, this, { single:  true } );
18058             
18059             return true;
18060         }
18061         
18062         cur.setActive(false);
18063         pan.setActive(true);
18064         
18065         return true;
18066         
18067     },
18068     showPanelNext : function()
18069     {
18070         var i = this.indexOfPanel(this.getActivePanel());
18071         
18072         if (i >= this.tabs.length - 1 && !this.autoslide) {
18073             return;
18074         }
18075         
18076         if (i >= this.tabs.length - 1 && this.autoslide) {
18077             i = -1;
18078         }
18079         
18080         this.showPanel(this.tabs[i+1]);
18081     },
18082     
18083     showPanelPrev : function()
18084     {
18085         var i = this.indexOfPanel(this.getActivePanel());
18086         
18087         if (i  < 1 && !this.autoslide) {
18088             return;
18089         }
18090         
18091         if (i < 1 && this.autoslide) {
18092             i = this.tabs.length;
18093         }
18094         
18095         this.showPanel(this.tabs[i-1]);
18096     },
18097     
18098     
18099     addBullet: function()
18100     {
18101         if(!this.bullets || Roo.isTouch){
18102             return;
18103         }
18104         var ctr = this.el.select('.carousel-bullets',true).first();
18105         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18106         var bullet = ctr.createChild({
18107             cls : 'bullet bullet-' + i
18108         },ctr.dom.lastChild);
18109         
18110         
18111         var _this = this;
18112         
18113         bullet.on('click', (function(e, el, o, ii, t){
18114
18115             e.preventDefault();
18116
18117             this.showPanel(ii);
18118
18119             if(this.autoslide && this.slideFn){
18120                 clearInterval(this.slideFn);
18121                 this.slideFn = window.setInterval(function() {
18122                     _this.showPanelNext();
18123                 }, this.timer);
18124             }
18125
18126         }).createDelegate(this, [i, bullet], true));
18127                 
18128         
18129     },
18130      
18131     setActiveBullet : function(i)
18132     {
18133         if(Roo.isTouch){
18134             return;
18135         }
18136         
18137         Roo.each(this.el.select('.bullet', true).elements, function(el){
18138             el.removeClass('selected');
18139         });
18140
18141         var bullet = this.el.select('.bullet-' + i, true).first();
18142         
18143         if(!bullet){
18144             return;
18145         }
18146         
18147         bullet.addClass('selected');
18148     }
18149     
18150     
18151   
18152 });
18153
18154  
18155
18156  
18157  
18158 Roo.apply(Roo.bootstrap.TabGroup, {
18159     
18160     groups: {},
18161      /**
18162     * register a Navigation Group
18163     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18164     */
18165     register : function(navgrp)
18166     {
18167         this.groups[navgrp.navId] = navgrp;
18168         
18169     },
18170     /**
18171     * fetch a Navigation Group based on the navigation ID
18172     * if one does not exist , it will get created.
18173     * @param {string} the navgroup to add
18174     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18175     */
18176     get: function(navId) {
18177         if (typeof(this.groups[navId]) == 'undefined') {
18178             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18179         }
18180         return this.groups[navId] ;
18181     }
18182     
18183     
18184     
18185 });
18186
18187  /*
18188  * - LGPL
18189  *
18190  * TabPanel
18191  * 
18192  */
18193
18194 /**
18195  * @class Roo.bootstrap.TabPanel
18196  * @extends Roo.bootstrap.Component
18197  * Bootstrap TabPanel class
18198  * @cfg {Boolean} active panel active
18199  * @cfg {String} html panel content
18200  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18201  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18202  * @cfg {String} href click to link..
18203  * 
18204  * 
18205  * @constructor
18206  * Create a new TabPanel
18207  * @param {Object} config The config object
18208  */
18209
18210 Roo.bootstrap.TabPanel = function(config){
18211     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18212     this.addEvents({
18213         /**
18214              * @event changed
18215              * Fires when the active status changes
18216              * @param {Roo.bootstrap.TabPanel} this
18217              * @param {Boolean} state the new state
18218             
18219          */
18220         'changed': true,
18221         /**
18222              * @event beforedeactivate
18223              * Fires before a tab is de-activated - can be used to do validation on a form.
18224              * @param {Roo.bootstrap.TabPanel} this
18225              * @return {Boolean} false if there is an error
18226             
18227          */
18228         'beforedeactivate': true
18229      });
18230     
18231     this.tabId = this.tabId || Roo.id();
18232   
18233 };
18234
18235 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18236     
18237     active: false,
18238     html: false,
18239     tabId: false,
18240     navId : false,
18241     href : '',
18242     
18243     getAutoCreate : function(){
18244         var cfg = {
18245             tag: 'div',
18246             // item is needed for carousel - not sure if it has any effect otherwise
18247             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18248             html: this.html || ''
18249         };
18250         
18251         if(this.active){
18252             cfg.cls += ' active';
18253         }
18254         
18255         if(this.tabId){
18256             cfg.tabId = this.tabId;
18257         }
18258         
18259         
18260         return cfg;
18261     },
18262     
18263     initEvents:  function()
18264     {
18265         var p = this.parent();
18266         
18267         this.navId = this.navId || p.navId;
18268         
18269         if (typeof(this.navId) != 'undefined') {
18270             // not really needed.. but just in case.. parent should be a NavGroup.
18271             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18272             
18273             tg.register(this);
18274             
18275             var i = tg.tabs.length - 1;
18276             
18277             if(this.active && tg.bullets > 0 && i < tg.bullets){
18278                 tg.setActiveBullet(i);
18279             }
18280         }
18281         
18282         this.el.on('click', this.onClick, this);
18283         
18284         if(Roo.isTouch){
18285             this.el.on("touchstart", this.onTouchStart, this);
18286             this.el.on("touchmove", this.onTouchMove, this);
18287             this.el.on("touchend", this.onTouchEnd, this);
18288         }
18289         
18290     },
18291     
18292     onRender : function(ct, position)
18293     {
18294         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18295     },
18296     
18297     setActive : function(state)
18298     {
18299         Roo.log("panel - set active " + this.tabId + "=" + state);
18300         
18301         this.active = state;
18302         if (!state) {
18303             this.el.removeClass('active');
18304             
18305         } else  if (!this.el.hasClass('active')) {
18306             this.el.addClass('active');
18307         }
18308         
18309         this.fireEvent('changed', this, state);
18310     },
18311     
18312     onClick : function(e)
18313     {
18314         e.preventDefault();
18315         
18316         if(!this.href.length){
18317             return;
18318         }
18319         
18320         window.location.href = this.href;
18321     },
18322     
18323     startX : 0,
18324     startY : 0,
18325     endX : 0,
18326     endY : 0,
18327     swiping : false,
18328     
18329     onTouchStart : function(e)
18330     {
18331         this.swiping = false;
18332         
18333         this.startX = e.browserEvent.touches[0].clientX;
18334         this.startY = e.browserEvent.touches[0].clientY;
18335     },
18336     
18337     onTouchMove : function(e)
18338     {
18339         this.swiping = true;
18340         
18341         this.endX = e.browserEvent.touches[0].clientX;
18342         this.endY = e.browserEvent.touches[0].clientY;
18343     },
18344     
18345     onTouchEnd : function(e)
18346     {
18347         if(!this.swiping){
18348             this.onClick(e);
18349             return;
18350         }
18351         
18352         var tabGroup = this.parent();
18353         
18354         if(this.endX > this.startX){ // swiping right
18355             tabGroup.showPanelPrev();
18356             return;
18357         }
18358         
18359         if(this.startX > this.endX){ // swiping left
18360             tabGroup.showPanelNext();
18361             return;
18362         }
18363     }
18364     
18365     
18366 });
18367  
18368
18369  
18370
18371  /*
18372  * - LGPL
18373  *
18374  * DateField
18375  * 
18376  */
18377
18378 /**
18379  * @class Roo.bootstrap.DateField
18380  * @extends Roo.bootstrap.Input
18381  * Bootstrap DateField class
18382  * @cfg {Number} weekStart default 0
18383  * @cfg {String} viewMode default empty, (months|years)
18384  * @cfg {String} minViewMode default empty, (months|years)
18385  * @cfg {Number} startDate default -Infinity
18386  * @cfg {Number} endDate default Infinity
18387  * @cfg {Boolean} todayHighlight default false
18388  * @cfg {Boolean} todayBtn default false
18389  * @cfg {Boolean} calendarWeeks default false
18390  * @cfg {Object} daysOfWeekDisabled default empty
18391  * @cfg {Boolean} singleMode default false (true | false)
18392  * 
18393  * @cfg {Boolean} keyboardNavigation default true
18394  * @cfg {String} language default en
18395  * 
18396  * @constructor
18397  * Create a new DateField
18398  * @param {Object} config The config object
18399  */
18400
18401 Roo.bootstrap.DateField = function(config){
18402     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18403      this.addEvents({
18404             /**
18405              * @event show
18406              * Fires when this field show.
18407              * @param {Roo.bootstrap.DateField} this
18408              * @param {Mixed} date The date value
18409              */
18410             show : true,
18411             /**
18412              * @event show
18413              * Fires when this field hide.
18414              * @param {Roo.bootstrap.DateField} this
18415              * @param {Mixed} date The date value
18416              */
18417             hide : true,
18418             /**
18419              * @event select
18420              * Fires when select a date.
18421              * @param {Roo.bootstrap.DateField} this
18422              * @param {Mixed} date The date value
18423              */
18424             select : true,
18425             /**
18426              * @event beforeselect
18427              * Fires when before select a date.
18428              * @param {Roo.bootstrap.DateField} this
18429              * @param {Mixed} date The date value
18430              */
18431             beforeselect : true
18432         });
18433 };
18434
18435 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18436     
18437     /**
18438      * @cfg {String} format
18439      * The default date format string which can be overriden for localization support.  The format must be
18440      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18441      */
18442     format : "m/d/y",
18443     /**
18444      * @cfg {String} altFormats
18445      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18446      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18447      */
18448     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18449     
18450     weekStart : 0,
18451     
18452     viewMode : '',
18453     
18454     minViewMode : '',
18455     
18456     todayHighlight : false,
18457     
18458     todayBtn: false,
18459     
18460     language: 'en',
18461     
18462     keyboardNavigation: true,
18463     
18464     calendarWeeks: false,
18465     
18466     startDate: -Infinity,
18467     
18468     endDate: Infinity,
18469     
18470     daysOfWeekDisabled: [],
18471     
18472     _events: [],
18473     
18474     singleMode : false,
18475     
18476     UTCDate: function()
18477     {
18478         return new Date(Date.UTC.apply(Date, arguments));
18479     },
18480     
18481     UTCToday: function()
18482     {
18483         var today = new Date();
18484         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18485     },
18486     
18487     getDate: function() {
18488             var d = this.getUTCDate();
18489             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18490     },
18491     
18492     getUTCDate: function() {
18493             return this.date;
18494     },
18495     
18496     setDate: function(d) {
18497             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18498     },
18499     
18500     setUTCDate: function(d) {
18501             this.date = d;
18502             this.setValue(this.formatDate(this.date));
18503     },
18504         
18505     onRender: function(ct, position)
18506     {
18507         
18508         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18509         
18510         this.language = this.language || 'en';
18511         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18512         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18513         
18514         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18515         this.format = this.format || 'm/d/y';
18516         this.isInline = false;
18517         this.isInput = true;
18518         this.component = this.el.select('.add-on', true).first() || false;
18519         this.component = (this.component && this.component.length === 0) ? false : this.component;
18520         this.hasInput = this.component && this.inputEl().length;
18521         
18522         if (typeof(this.minViewMode === 'string')) {
18523             switch (this.minViewMode) {
18524                 case 'months':
18525                     this.minViewMode = 1;
18526                     break;
18527                 case 'years':
18528                     this.minViewMode = 2;
18529                     break;
18530                 default:
18531                     this.minViewMode = 0;
18532                     break;
18533             }
18534         }
18535         
18536         if (typeof(this.viewMode === 'string')) {
18537             switch (this.viewMode) {
18538                 case 'months':
18539                     this.viewMode = 1;
18540                     break;
18541                 case 'years':
18542                     this.viewMode = 2;
18543                     break;
18544                 default:
18545                     this.viewMode = 0;
18546                     break;
18547             }
18548         }
18549                 
18550         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18551         
18552 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18553         
18554         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18555         
18556         this.picker().on('mousedown', this.onMousedown, this);
18557         this.picker().on('click', this.onClick, this);
18558         
18559         this.picker().addClass('datepicker-dropdown');
18560         
18561         this.startViewMode = this.viewMode;
18562         
18563         if(this.singleMode){
18564             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18565                 v.setVisibilityMode(Roo.Element.DISPLAY);
18566                 v.hide();
18567             });
18568             
18569             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18570                 v.setStyle('width', '189px');
18571             });
18572         }
18573         
18574         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18575             if(!this.calendarWeeks){
18576                 v.remove();
18577                 return;
18578             }
18579             
18580             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18581             v.attr('colspan', function(i, val){
18582                 return parseInt(val) + 1;
18583             });
18584         });
18585                         
18586         
18587         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18588         
18589         this.setStartDate(this.startDate);
18590         this.setEndDate(this.endDate);
18591         
18592         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18593         
18594         this.fillDow();
18595         this.fillMonths();
18596         this.update();
18597         this.showMode();
18598         
18599         if(this.isInline) {
18600             this.showPopup();
18601         }
18602     },
18603     
18604     picker : function()
18605     {
18606         return this.pickerEl;
18607 //        return this.el.select('.datepicker', true).first();
18608     },
18609     
18610     fillDow: function()
18611     {
18612         var dowCnt = this.weekStart;
18613         
18614         var dow = {
18615             tag: 'tr',
18616             cn: [
18617                 
18618             ]
18619         };
18620         
18621         if(this.calendarWeeks){
18622             dow.cn.push({
18623                 tag: 'th',
18624                 cls: 'cw',
18625                 html: '&nbsp;'
18626             })
18627         }
18628         
18629         while (dowCnt < this.weekStart + 7) {
18630             dow.cn.push({
18631                 tag: 'th',
18632                 cls: 'dow',
18633                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18634             });
18635         }
18636         
18637         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18638     },
18639     
18640     fillMonths: function()
18641     {    
18642         var i = 0;
18643         var months = this.picker().select('>.datepicker-months td', true).first();
18644         
18645         months.dom.innerHTML = '';
18646         
18647         while (i < 12) {
18648             var month = {
18649                 tag: 'span',
18650                 cls: 'month',
18651                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18652             };
18653             
18654             months.createChild(month);
18655         }
18656         
18657     },
18658     
18659     update: function()
18660     {
18661         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;
18662         
18663         if (this.date < this.startDate) {
18664             this.viewDate = new Date(this.startDate);
18665         } else if (this.date > this.endDate) {
18666             this.viewDate = new Date(this.endDate);
18667         } else {
18668             this.viewDate = new Date(this.date);
18669         }
18670         
18671         this.fill();
18672     },
18673     
18674     fill: function() 
18675     {
18676         var d = new Date(this.viewDate),
18677                 year = d.getUTCFullYear(),
18678                 month = d.getUTCMonth(),
18679                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18680                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18681                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18682                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18683                 currentDate = this.date && this.date.valueOf(),
18684                 today = this.UTCToday();
18685         
18686         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18687         
18688 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18689         
18690 //        this.picker.select('>tfoot th.today').
18691 //                                              .text(dates[this.language].today)
18692 //                                              .toggle(this.todayBtn !== false);
18693     
18694         this.updateNavArrows();
18695         this.fillMonths();
18696                                                 
18697         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18698         
18699         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18700          
18701         prevMonth.setUTCDate(day);
18702         
18703         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18704         
18705         var nextMonth = new Date(prevMonth);
18706         
18707         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18708         
18709         nextMonth = nextMonth.valueOf();
18710         
18711         var fillMonths = false;
18712         
18713         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18714         
18715         while(prevMonth.valueOf() <= nextMonth) {
18716             var clsName = '';
18717             
18718             if (prevMonth.getUTCDay() === this.weekStart) {
18719                 if(fillMonths){
18720                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18721                 }
18722                     
18723                 fillMonths = {
18724                     tag: 'tr',
18725                     cn: []
18726                 };
18727                 
18728                 if(this.calendarWeeks){
18729                     // ISO 8601: First week contains first thursday.
18730                     // ISO also states week starts on Monday, but we can be more abstract here.
18731                     var
18732                     // Start of current week: based on weekstart/current date
18733                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18734                     // Thursday of this week
18735                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18736                     // First Thursday of year, year from thursday
18737                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18738                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18739                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18740                     
18741                     fillMonths.cn.push({
18742                         tag: 'td',
18743                         cls: 'cw',
18744                         html: calWeek
18745                     });
18746                 }
18747             }
18748             
18749             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18750                 clsName += ' old';
18751             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18752                 clsName += ' new';
18753             }
18754             if (this.todayHighlight &&
18755                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18756                 prevMonth.getUTCMonth() == today.getMonth() &&
18757                 prevMonth.getUTCDate() == today.getDate()) {
18758                 clsName += ' today';
18759             }
18760             
18761             if (currentDate && prevMonth.valueOf() === currentDate) {
18762                 clsName += ' active';
18763             }
18764             
18765             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18766                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18767                     clsName += ' disabled';
18768             }
18769             
18770             fillMonths.cn.push({
18771                 tag: 'td',
18772                 cls: 'day ' + clsName,
18773                 html: prevMonth.getDate()
18774             });
18775             
18776             prevMonth.setDate(prevMonth.getDate()+1);
18777         }
18778           
18779         var currentYear = this.date && this.date.getUTCFullYear();
18780         var currentMonth = this.date && this.date.getUTCMonth();
18781         
18782         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18783         
18784         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18785             v.removeClass('active');
18786             
18787             if(currentYear === year && k === currentMonth){
18788                 v.addClass('active');
18789             }
18790             
18791             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18792                 v.addClass('disabled');
18793             }
18794             
18795         });
18796         
18797         
18798         year = parseInt(year/10, 10) * 10;
18799         
18800         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18801         
18802         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18803         
18804         year -= 1;
18805         for (var i = -1; i < 11; i++) {
18806             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18807                 tag: 'span',
18808                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18809                 html: year
18810             });
18811             
18812             year += 1;
18813         }
18814     },
18815     
18816     showMode: function(dir) 
18817     {
18818         if (dir) {
18819             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18820         }
18821         
18822         Roo.each(this.picker().select('>div',true).elements, function(v){
18823             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18824             v.hide();
18825         });
18826         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18827     },
18828     
18829     place: function()
18830     {
18831         if(this.isInline) {
18832             return;
18833         }
18834         
18835         this.picker().removeClass(['bottom', 'top']);
18836         
18837         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18838             /*
18839              * place to the top of element!
18840              *
18841              */
18842             
18843             this.picker().addClass('top');
18844             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18845             
18846             return;
18847         }
18848         
18849         this.picker().addClass('bottom');
18850         
18851         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18852     },
18853     
18854     parseDate : function(value)
18855     {
18856         if(!value || value instanceof Date){
18857             return value;
18858         }
18859         var v = Date.parseDate(value, this.format);
18860         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18861             v = Date.parseDate(value, 'Y-m-d');
18862         }
18863         if(!v && this.altFormats){
18864             if(!this.altFormatsArray){
18865                 this.altFormatsArray = this.altFormats.split("|");
18866             }
18867             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18868                 v = Date.parseDate(value, this.altFormatsArray[i]);
18869             }
18870         }
18871         return v;
18872     },
18873     
18874     formatDate : function(date, fmt)
18875     {   
18876         return (!date || !(date instanceof Date)) ?
18877         date : date.dateFormat(fmt || this.format);
18878     },
18879     
18880     onFocus : function()
18881     {
18882         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18883         this.showPopup();
18884     },
18885     
18886     onBlur : function()
18887     {
18888         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18889         
18890         var d = this.inputEl().getValue();
18891         
18892         this.setValue(d);
18893                 
18894         this.hidePopup();
18895     },
18896     
18897     showPopup : function()
18898     {
18899         this.picker().show();
18900         this.update();
18901         this.place();
18902         
18903         this.fireEvent('showpopup', this, this.date);
18904     },
18905     
18906     hidePopup : function()
18907     {
18908         if(this.isInline) {
18909             return;
18910         }
18911         this.picker().hide();
18912         this.viewMode = this.startViewMode;
18913         this.showMode();
18914         
18915         this.fireEvent('hidepopup', this, this.date);
18916         
18917     },
18918     
18919     onMousedown: function(e)
18920     {
18921         e.stopPropagation();
18922         e.preventDefault();
18923     },
18924     
18925     keyup: function(e)
18926     {
18927         Roo.bootstrap.DateField.superclass.keyup.call(this);
18928         this.update();
18929     },
18930
18931     setValue: function(v)
18932     {
18933         if(this.fireEvent('beforeselect', this, v) !== false){
18934             var d = new Date(this.parseDate(v) ).clearTime();
18935         
18936             if(isNaN(d.getTime())){
18937                 this.date = this.viewDate = '';
18938                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18939                 return;
18940             }
18941
18942             v = this.formatDate(d);
18943
18944             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18945
18946             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18947
18948             this.update();
18949
18950             this.fireEvent('select', this, this.date);
18951         }
18952     },
18953     
18954     getValue: function()
18955     {
18956         return this.formatDate(this.date);
18957     },
18958     
18959     fireKey: function(e)
18960     {
18961         if (!this.picker().isVisible()){
18962             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18963                 this.showPopup();
18964             }
18965             return;
18966         }
18967         
18968         var dateChanged = false,
18969         dir, day, month,
18970         newDate, newViewDate;
18971         
18972         switch(e.keyCode){
18973             case 27: // escape
18974                 this.hidePopup();
18975                 e.preventDefault();
18976                 break;
18977             case 37: // left
18978             case 39: // right
18979                 if (!this.keyboardNavigation) {
18980                     break;
18981                 }
18982                 dir = e.keyCode == 37 ? -1 : 1;
18983                 
18984                 if (e.ctrlKey){
18985                     newDate = this.moveYear(this.date, dir);
18986                     newViewDate = this.moveYear(this.viewDate, dir);
18987                 } else if (e.shiftKey){
18988                     newDate = this.moveMonth(this.date, dir);
18989                     newViewDate = this.moveMonth(this.viewDate, dir);
18990                 } else {
18991                     newDate = new Date(this.date);
18992                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18993                     newViewDate = new Date(this.viewDate);
18994                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18995                 }
18996                 if (this.dateWithinRange(newDate)){
18997                     this.date = newDate;
18998                     this.viewDate = newViewDate;
18999                     this.setValue(this.formatDate(this.date));
19000 //                    this.update();
19001                     e.preventDefault();
19002                     dateChanged = true;
19003                 }
19004                 break;
19005             case 38: // up
19006             case 40: // down
19007                 if (!this.keyboardNavigation) {
19008                     break;
19009                 }
19010                 dir = e.keyCode == 38 ? -1 : 1;
19011                 if (e.ctrlKey){
19012                     newDate = this.moveYear(this.date, dir);
19013                     newViewDate = this.moveYear(this.viewDate, dir);
19014                 } else if (e.shiftKey){
19015                     newDate = this.moveMonth(this.date, dir);
19016                     newViewDate = this.moveMonth(this.viewDate, dir);
19017                 } else {
19018                     newDate = new Date(this.date);
19019                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19020                     newViewDate = new Date(this.viewDate);
19021                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19022                 }
19023                 if (this.dateWithinRange(newDate)){
19024                     this.date = newDate;
19025                     this.viewDate = newViewDate;
19026                     this.setValue(this.formatDate(this.date));
19027 //                    this.update();
19028                     e.preventDefault();
19029                     dateChanged = true;
19030                 }
19031                 break;
19032             case 13: // enter
19033                 this.setValue(this.formatDate(this.date));
19034                 this.hidePopup();
19035                 e.preventDefault();
19036                 break;
19037             case 9: // tab
19038                 this.setValue(this.formatDate(this.date));
19039                 this.hidePopup();
19040                 break;
19041             case 16: // shift
19042             case 17: // ctrl
19043             case 18: // alt
19044                 break;
19045             default :
19046                 this.hide();
19047                 
19048         }
19049     },
19050     
19051     
19052     onClick: function(e) 
19053     {
19054         e.stopPropagation();
19055         e.preventDefault();
19056         
19057         var target = e.getTarget();
19058         
19059         if(target.nodeName.toLowerCase() === 'i'){
19060             target = Roo.get(target).dom.parentNode;
19061         }
19062         
19063         var nodeName = target.nodeName;
19064         var className = target.className;
19065         var html = target.innerHTML;
19066         //Roo.log(nodeName);
19067         
19068         switch(nodeName.toLowerCase()) {
19069             case 'th':
19070                 switch(className) {
19071                     case 'switch':
19072                         this.showMode(1);
19073                         break;
19074                     case 'prev':
19075                     case 'next':
19076                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19077                         switch(this.viewMode){
19078                                 case 0:
19079                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19080                                         break;
19081                                 case 1:
19082                                 case 2:
19083                                         this.viewDate = this.moveYear(this.viewDate, dir);
19084                                         break;
19085                         }
19086                         this.fill();
19087                         break;
19088                     case 'today':
19089                         var date = new Date();
19090                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19091 //                        this.fill()
19092                         this.setValue(this.formatDate(this.date));
19093                         
19094                         this.hidePopup();
19095                         break;
19096                 }
19097                 break;
19098             case 'span':
19099                 if (className.indexOf('disabled') < 0) {
19100                     this.viewDate.setUTCDate(1);
19101                     if (className.indexOf('month') > -1) {
19102                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19103                     } else {
19104                         var year = parseInt(html, 10) || 0;
19105                         this.viewDate.setUTCFullYear(year);
19106                         
19107                     }
19108                     
19109                     if(this.singleMode){
19110                         this.setValue(this.formatDate(this.viewDate));
19111                         this.hidePopup();
19112                         return;
19113                     }
19114                     
19115                     this.showMode(-1);
19116                     this.fill();
19117                 }
19118                 break;
19119                 
19120             case 'td':
19121                 //Roo.log(className);
19122                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19123                     var day = parseInt(html, 10) || 1;
19124                     var year = this.viewDate.getUTCFullYear(),
19125                         month = this.viewDate.getUTCMonth();
19126
19127                     if (className.indexOf('old') > -1) {
19128                         if(month === 0 ){
19129                             month = 11;
19130                             year -= 1;
19131                         }else{
19132                             month -= 1;
19133                         }
19134                     } else if (className.indexOf('new') > -1) {
19135                         if (month == 11) {
19136                             month = 0;
19137                             year += 1;
19138                         } else {
19139                             month += 1;
19140                         }
19141                     }
19142                     //Roo.log([year,month,day]);
19143                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19144                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19145 //                    this.fill();
19146                     //Roo.log(this.formatDate(this.date));
19147                     this.setValue(this.formatDate(this.date));
19148                     this.hidePopup();
19149                 }
19150                 break;
19151         }
19152     },
19153     
19154     setStartDate: function(startDate)
19155     {
19156         this.startDate = startDate || -Infinity;
19157         if (this.startDate !== -Infinity) {
19158             this.startDate = this.parseDate(this.startDate);
19159         }
19160         this.update();
19161         this.updateNavArrows();
19162     },
19163
19164     setEndDate: function(endDate)
19165     {
19166         this.endDate = endDate || Infinity;
19167         if (this.endDate !== Infinity) {
19168             this.endDate = this.parseDate(this.endDate);
19169         }
19170         this.update();
19171         this.updateNavArrows();
19172     },
19173     
19174     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19175     {
19176         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19177         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19178             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19179         }
19180         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19181             return parseInt(d, 10);
19182         });
19183         this.update();
19184         this.updateNavArrows();
19185     },
19186     
19187     updateNavArrows: function() 
19188     {
19189         if(this.singleMode){
19190             return;
19191         }
19192         
19193         var d = new Date(this.viewDate),
19194         year = d.getUTCFullYear(),
19195         month = d.getUTCMonth();
19196         
19197         Roo.each(this.picker().select('.prev', true).elements, function(v){
19198             v.show();
19199             switch (this.viewMode) {
19200                 case 0:
19201
19202                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19203                         v.hide();
19204                     }
19205                     break;
19206                 case 1:
19207                 case 2:
19208                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19209                         v.hide();
19210                     }
19211                     break;
19212             }
19213         });
19214         
19215         Roo.each(this.picker().select('.next', true).elements, function(v){
19216             v.show();
19217             switch (this.viewMode) {
19218                 case 0:
19219
19220                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19221                         v.hide();
19222                     }
19223                     break;
19224                 case 1:
19225                 case 2:
19226                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19227                         v.hide();
19228                     }
19229                     break;
19230             }
19231         })
19232     },
19233     
19234     moveMonth: function(date, dir)
19235     {
19236         if (!dir) {
19237             return date;
19238         }
19239         var new_date = new Date(date.valueOf()),
19240         day = new_date.getUTCDate(),
19241         month = new_date.getUTCMonth(),
19242         mag = Math.abs(dir),
19243         new_month, test;
19244         dir = dir > 0 ? 1 : -1;
19245         if (mag == 1){
19246             test = dir == -1
19247             // If going back one month, make sure month is not current month
19248             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19249             ? function(){
19250                 return new_date.getUTCMonth() == month;
19251             }
19252             // If going forward one month, make sure month is as expected
19253             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19254             : function(){
19255                 return new_date.getUTCMonth() != new_month;
19256             };
19257             new_month = month + dir;
19258             new_date.setUTCMonth(new_month);
19259             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19260             if (new_month < 0 || new_month > 11) {
19261                 new_month = (new_month + 12) % 12;
19262             }
19263         } else {
19264             // For magnitudes >1, move one month at a time...
19265             for (var i=0; i<mag; i++) {
19266                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19267                 new_date = this.moveMonth(new_date, dir);
19268             }
19269             // ...then reset the day, keeping it in the new month
19270             new_month = new_date.getUTCMonth();
19271             new_date.setUTCDate(day);
19272             test = function(){
19273                 return new_month != new_date.getUTCMonth();
19274             };
19275         }
19276         // Common date-resetting loop -- if date is beyond end of month, make it
19277         // end of month
19278         while (test()){
19279             new_date.setUTCDate(--day);
19280             new_date.setUTCMonth(new_month);
19281         }
19282         return new_date;
19283     },
19284
19285     moveYear: function(date, dir)
19286     {
19287         return this.moveMonth(date, dir*12);
19288     },
19289
19290     dateWithinRange: function(date)
19291     {
19292         return date >= this.startDate && date <= this.endDate;
19293     },
19294
19295     
19296     remove: function() 
19297     {
19298         this.picker().remove();
19299     },
19300     
19301     validateValue : function(value)
19302     {
19303         if(this.getVisibilityEl().hasClass('hidden')){
19304             return true;
19305         }
19306         
19307         if(value.length < 1)  {
19308             if(this.allowBlank){
19309                 return true;
19310             }
19311             return false;
19312         }
19313         
19314         if(value.length < this.minLength){
19315             return false;
19316         }
19317         if(value.length > this.maxLength){
19318             return false;
19319         }
19320         if(this.vtype){
19321             var vt = Roo.form.VTypes;
19322             if(!vt[this.vtype](value, this)){
19323                 return false;
19324             }
19325         }
19326         if(typeof this.validator == "function"){
19327             var msg = this.validator(value);
19328             if(msg !== true){
19329                 return false;
19330             }
19331         }
19332         
19333         if(this.regex && !this.regex.test(value)){
19334             return false;
19335         }
19336         
19337         if(typeof(this.parseDate(value)) == 'undefined'){
19338             return false;
19339         }
19340         
19341         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19342             return false;
19343         }      
19344         
19345         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19346             return false;
19347         } 
19348         
19349         
19350         return true;
19351     },
19352     
19353     reset : function()
19354     {
19355         this.date = this.viewDate = '';
19356         
19357         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19358     }
19359    
19360 });
19361
19362 Roo.apply(Roo.bootstrap.DateField,  {
19363     
19364     head : {
19365         tag: 'thead',
19366         cn: [
19367         {
19368             tag: 'tr',
19369             cn: [
19370             {
19371                 tag: 'th',
19372                 cls: 'prev',
19373                 html: '<i class="fa fa-arrow-left"/>'
19374             },
19375             {
19376                 tag: 'th',
19377                 cls: 'switch',
19378                 colspan: '5'
19379             },
19380             {
19381                 tag: 'th',
19382                 cls: 'next',
19383                 html: '<i class="fa fa-arrow-right"/>'
19384             }
19385
19386             ]
19387         }
19388         ]
19389     },
19390     
19391     content : {
19392         tag: 'tbody',
19393         cn: [
19394         {
19395             tag: 'tr',
19396             cn: [
19397             {
19398                 tag: 'td',
19399                 colspan: '7'
19400             }
19401             ]
19402         }
19403         ]
19404     },
19405     
19406     footer : {
19407         tag: 'tfoot',
19408         cn: [
19409         {
19410             tag: 'tr',
19411             cn: [
19412             {
19413                 tag: 'th',
19414                 colspan: '7',
19415                 cls: 'today'
19416             }
19417                     
19418             ]
19419         }
19420         ]
19421     },
19422     
19423     dates:{
19424         en: {
19425             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19426             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19427             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19428             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19429             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19430             today: "Today"
19431         }
19432     },
19433     
19434     modes: [
19435     {
19436         clsName: 'days',
19437         navFnc: 'Month',
19438         navStep: 1
19439     },
19440     {
19441         clsName: 'months',
19442         navFnc: 'FullYear',
19443         navStep: 1
19444     },
19445     {
19446         clsName: 'years',
19447         navFnc: 'FullYear',
19448         navStep: 10
19449     }]
19450 });
19451
19452 Roo.apply(Roo.bootstrap.DateField,  {
19453   
19454     template : {
19455         tag: 'div',
19456         cls: 'datepicker dropdown-menu roo-dynamic',
19457         cn: [
19458         {
19459             tag: 'div',
19460             cls: 'datepicker-days',
19461             cn: [
19462             {
19463                 tag: 'table',
19464                 cls: 'table-condensed',
19465                 cn:[
19466                 Roo.bootstrap.DateField.head,
19467                 {
19468                     tag: 'tbody'
19469                 },
19470                 Roo.bootstrap.DateField.footer
19471                 ]
19472             }
19473             ]
19474         },
19475         {
19476             tag: 'div',
19477             cls: 'datepicker-months',
19478             cn: [
19479             {
19480                 tag: 'table',
19481                 cls: 'table-condensed',
19482                 cn:[
19483                 Roo.bootstrap.DateField.head,
19484                 Roo.bootstrap.DateField.content,
19485                 Roo.bootstrap.DateField.footer
19486                 ]
19487             }
19488             ]
19489         },
19490         {
19491             tag: 'div',
19492             cls: 'datepicker-years',
19493             cn: [
19494             {
19495                 tag: 'table',
19496                 cls: 'table-condensed',
19497                 cn:[
19498                 Roo.bootstrap.DateField.head,
19499                 Roo.bootstrap.DateField.content,
19500                 Roo.bootstrap.DateField.footer
19501                 ]
19502             }
19503             ]
19504         }
19505         ]
19506     }
19507 });
19508
19509  
19510
19511  /*
19512  * - LGPL
19513  *
19514  * TimeField
19515  * 
19516  */
19517
19518 /**
19519  * @class Roo.bootstrap.TimeField
19520  * @extends Roo.bootstrap.Input
19521  * Bootstrap DateField class
19522  * 
19523  * 
19524  * @constructor
19525  * Create a new TimeField
19526  * @param {Object} config The config object
19527  */
19528
19529 Roo.bootstrap.TimeField = function(config){
19530     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19531     this.addEvents({
19532             /**
19533              * @event show
19534              * Fires when this field show.
19535              * @param {Roo.bootstrap.DateField} thisthis
19536              * @param {Mixed} date The date value
19537              */
19538             show : true,
19539             /**
19540              * @event show
19541              * Fires when this field hide.
19542              * @param {Roo.bootstrap.DateField} this
19543              * @param {Mixed} date The date value
19544              */
19545             hide : true,
19546             /**
19547              * @event select
19548              * Fires when select a date.
19549              * @param {Roo.bootstrap.DateField} this
19550              * @param {Mixed} date The date value
19551              */
19552             select : true
19553         });
19554 };
19555
19556 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19557     
19558     /**
19559      * @cfg {String} format
19560      * The default time format string which can be overriden for localization support.  The format must be
19561      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19562      */
19563     format : "H:i",
19564        
19565     onRender: function(ct, position)
19566     {
19567         
19568         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19569                 
19570         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19571         
19572         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19573         
19574         this.pop = this.picker().select('>.datepicker-time',true).first();
19575         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19576         
19577         this.picker().on('mousedown', this.onMousedown, this);
19578         this.picker().on('click', this.onClick, this);
19579         
19580         this.picker().addClass('datepicker-dropdown');
19581     
19582         this.fillTime();
19583         this.update();
19584             
19585         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19586         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19587         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19588         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19589         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19590         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19591
19592     },
19593     
19594     fireKey: function(e){
19595         if (!this.picker().isVisible()){
19596             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19597                 this.show();
19598             }
19599             return;
19600         }
19601
19602         e.preventDefault();
19603         
19604         switch(e.keyCode){
19605             case 27: // escape
19606                 this.hide();
19607                 break;
19608             case 37: // left
19609             case 39: // right
19610                 this.onTogglePeriod();
19611                 break;
19612             case 38: // up
19613                 this.onIncrementMinutes();
19614                 break;
19615             case 40: // down
19616                 this.onDecrementMinutes();
19617                 break;
19618             case 13: // enter
19619             case 9: // tab
19620                 this.setTime();
19621                 break;
19622         }
19623     },
19624     
19625     onClick: function(e) {
19626         e.stopPropagation();
19627         e.preventDefault();
19628     },
19629     
19630     picker : function()
19631     {
19632         return this.el.select('.datepicker', true).first();
19633     },
19634     
19635     fillTime: function()
19636     {    
19637         var time = this.pop.select('tbody', true).first();
19638         
19639         time.dom.innerHTML = '';
19640         
19641         time.createChild({
19642             tag: 'tr',
19643             cn: [
19644                 {
19645                     tag: 'td',
19646                     cn: [
19647                         {
19648                             tag: 'a',
19649                             href: '#',
19650                             cls: 'btn',
19651                             cn: [
19652                                 {
19653                                     tag: 'span',
19654                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19655                                 }
19656                             ]
19657                         } 
19658                     ]
19659                 },
19660                 {
19661                     tag: 'td',
19662                     cls: 'separator'
19663                 },
19664                 {
19665                     tag: 'td',
19666                     cn: [
19667                         {
19668                             tag: 'a',
19669                             href: '#',
19670                             cls: 'btn',
19671                             cn: [
19672                                 {
19673                                     tag: 'span',
19674                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19675                                 }
19676                             ]
19677                         }
19678                     ]
19679                 },
19680                 {
19681                     tag: 'td',
19682                     cls: 'separator'
19683                 }
19684             ]
19685         });
19686         
19687         time.createChild({
19688             tag: 'tr',
19689             cn: [
19690                 {
19691                     tag: 'td',
19692                     cn: [
19693                         {
19694                             tag: 'span',
19695                             cls: 'timepicker-hour',
19696                             html: '00'
19697                         }  
19698                     ]
19699                 },
19700                 {
19701                     tag: 'td',
19702                     cls: 'separator',
19703                     html: ':'
19704                 },
19705                 {
19706                     tag: 'td',
19707                     cn: [
19708                         {
19709                             tag: 'span',
19710                             cls: 'timepicker-minute',
19711                             html: '00'
19712                         }  
19713                     ]
19714                 },
19715                 {
19716                     tag: 'td',
19717                     cls: 'separator'
19718                 },
19719                 {
19720                     tag: 'td',
19721                     cn: [
19722                         {
19723                             tag: 'button',
19724                             type: 'button',
19725                             cls: 'btn btn-primary period',
19726                             html: 'AM'
19727                             
19728                         }
19729                     ]
19730                 }
19731             ]
19732         });
19733         
19734         time.createChild({
19735             tag: 'tr',
19736             cn: [
19737                 {
19738                     tag: 'td',
19739                     cn: [
19740                         {
19741                             tag: 'a',
19742                             href: '#',
19743                             cls: 'btn',
19744                             cn: [
19745                                 {
19746                                     tag: 'span',
19747                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19748                                 }
19749                             ]
19750                         }
19751                     ]
19752                 },
19753                 {
19754                     tag: 'td',
19755                     cls: 'separator'
19756                 },
19757                 {
19758                     tag: 'td',
19759                     cn: [
19760                         {
19761                             tag: 'a',
19762                             href: '#',
19763                             cls: 'btn',
19764                             cn: [
19765                                 {
19766                                     tag: 'span',
19767                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19768                                 }
19769                             ]
19770                         }
19771                     ]
19772                 },
19773                 {
19774                     tag: 'td',
19775                     cls: 'separator'
19776                 }
19777             ]
19778         });
19779         
19780     },
19781     
19782     update: function()
19783     {
19784         
19785         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19786         
19787         this.fill();
19788     },
19789     
19790     fill: function() 
19791     {
19792         var hours = this.time.getHours();
19793         var minutes = this.time.getMinutes();
19794         var period = 'AM';
19795         
19796         if(hours > 11){
19797             period = 'PM';
19798         }
19799         
19800         if(hours == 0){
19801             hours = 12;
19802         }
19803         
19804         
19805         if(hours > 12){
19806             hours = hours - 12;
19807         }
19808         
19809         if(hours < 10){
19810             hours = '0' + hours;
19811         }
19812         
19813         if(minutes < 10){
19814             minutes = '0' + minutes;
19815         }
19816         
19817         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19818         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19819         this.pop.select('button', true).first().dom.innerHTML = period;
19820         
19821     },
19822     
19823     place: function()
19824     {   
19825         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19826         
19827         var cls = ['bottom'];
19828         
19829         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19830             cls.pop();
19831             cls.push('top');
19832         }
19833         
19834         cls.push('right');
19835         
19836         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19837             cls.pop();
19838             cls.push('left');
19839         }
19840         
19841         this.picker().addClass(cls.join('-'));
19842         
19843         var _this = this;
19844         
19845         Roo.each(cls, function(c){
19846             if(c == 'bottom'){
19847                 _this.picker().setTop(_this.inputEl().getHeight());
19848                 return;
19849             }
19850             if(c == 'top'){
19851                 _this.picker().setTop(0 - _this.picker().getHeight());
19852                 return;
19853             }
19854             
19855             if(c == 'left'){
19856                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19857                 return;
19858             }
19859             if(c == 'right'){
19860                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19861                 return;
19862             }
19863         });
19864         
19865     },
19866   
19867     onFocus : function()
19868     {
19869         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19870         this.show();
19871     },
19872     
19873     onBlur : function()
19874     {
19875         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19876         this.hide();
19877     },
19878     
19879     show : function()
19880     {
19881         this.picker().show();
19882         this.pop.show();
19883         this.update();
19884         this.place();
19885         
19886         this.fireEvent('show', this, this.date);
19887     },
19888     
19889     hide : function()
19890     {
19891         this.picker().hide();
19892         this.pop.hide();
19893         
19894         this.fireEvent('hide', this, this.date);
19895     },
19896     
19897     setTime : function()
19898     {
19899         this.hide();
19900         this.setValue(this.time.format(this.format));
19901         
19902         this.fireEvent('select', this, this.date);
19903         
19904         
19905     },
19906     
19907     onMousedown: function(e){
19908         e.stopPropagation();
19909         e.preventDefault();
19910     },
19911     
19912     onIncrementHours: function()
19913     {
19914         Roo.log('onIncrementHours');
19915         this.time = this.time.add(Date.HOUR, 1);
19916         this.update();
19917         
19918     },
19919     
19920     onDecrementHours: function()
19921     {
19922         Roo.log('onDecrementHours');
19923         this.time = this.time.add(Date.HOUR, -1);
19924         this.update();
19925     },
19926     
19927     onIncrementMinutes: function()
19928     {
19929         Roo.log('onIncrementMinutes');
19930         this.time = this.time.add(Date.MINUTE, 1);
19931         this.update();
19932     },
19933     
19934     onDecrementMinutes: function()
19935     {
19936         Roo.log('onDecrementMinutes');
19937         this.time = this.time.add(Date.MINUTE, -1);
19938         this.update();
19939     },
19940     
19941     onTogglePeriod: function()
19942     {
19943         Roo.log('onTogglePeriod');
19944         this.time = this.time.add(Date.HOUR, 12);
19945         this.update();
19946     }
19947     
19948    
19949 });
19950
19951 Roo.apply(Roo.bootstrap.TimeField,  {
19952     
19953     content : {
19954         tag: 'tbody',
19955         cn: [
19956             {
19957                 tag: 'tr',
19958                 cn: [
19959                 {
19960                     tag: 'td',
19961                     colspan: '7'
19962                 }
19963                 ]
19964             }
19965         ]
19966     },
19967     
19968     footer : {
19969         tag: 'tfoot',
19970         cn: [
19971             {
19972                 tag: 'tr',
19973                 cn: [
19974                 {
19975                     tag: 'th',
19976                     colspan: '7',
19977                     cls: '',
19978                     cn: [
19979                         {
19980                             tag: 'button',
19981                             cls: 'btn btn-info ok',
19982                             html: 'OK'
19983                         }
19984                     ]
19985                 }
19986
19987                 ]
19988             }
19989         ]
19990     }
19991 });
19992
19993 Roo.apply(Roo.bootstrap.TimeField,  {
19994   
19995     template : {
19996         tag: 'div',
19997         cls: 'datepicker dropdown-menu',
19998         cn: [
19999             {
20000                 tag: 'div',
20001                 cls: 'datepicker-time',
20002                 cn: [
20003                 {
20004                     tag: 'table',
20005                     cls: 'table-condensed',
20006                     cn:[
20007                     Roo.bootstrap.TimeField.content,
20008                     Roo.bootstrap.TimeField.footer
20009                     ]
20010                 }
20011                 ]
20012             }
20013         ]
20014     }
20015 });
20016
20017  
20018
20019  /*
20020  * - LGPL
20021  *
20022  * MonthField
20023  * 
20024  */
20025
20026 /**
20027  * @class Roo.bootstrap.MonthField
20028  * @extends Roo.bootstrap.Input
20029  * Bootstrap MonthField class
20030  * 
20031  * @cfg {String} language default en
20032  * 
20033  * @constructor
20034  * Create a new MonthField
20035  * @param {Object} config The config object
20036  */
20037
20038 Roo.bootstrap.MonthField = function(config){
20039     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20040     
20041     this.addEvents({
20042         /**
20043          * @event show
20044          * Fires when this field show.
20045          * @param {Roo.bootstrap.MonthField} this
20046          * @param {Mixed} date The date value
20047          */
20048         show : true,
20049         /**
20050          * @event show
20051          * Fires when this field hide.
20052          * @param {Roo.bootstrap.MonthField} this
20053          * @param {Mixed} date The date value
20054          */
20055         hide : true,
20056         /**
20057          * @event select
20058          * Fires when select a date.
20059          * @param {Roo.bootstrap.MonthField} this
20060          * @param {String} oldvalue The old value
20061          * @param {String} newvalue The new value
20062          */
20063         select : true
20064     });
20065 };
20066
20067 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20068     
20069     onRender: function(ct, position)
20070     {
20071         
20072         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20073         
20074         this.language = this.language || 'en';
20075         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20076         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20077         
20078         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20079         this.isInline = false;
20080         this.isInput = true;
20081         this.component = this.el.select('.add-on', true).first() || false;
20082         this.component = (this.component && this.component.length === 0) ? false : this.component;
20083         this.hasInput = this.component && this.inputEL().length;
20084         
20085         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20086         
20087         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20088         
20089         this.picker().on('mousedown', this.onMousedown, this);
20090         this.picker().on('click', this.onClick, this);
20091         
20092         this.picker().addClass('datepicker-dropdown');
20093         
20094         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20095             v.setStyle('width', '189px');
20096         });
20097         
20098         this.fillMonths();
20099         
20100         this.update();
20101         
20102         if(this.isInline) {
20103             this.show();
20104         }
20105         
20106     },
20107     
20108     setValue: function(v, suppressEvent)
20109     {   
20110         var o = this.getValue();
20111         
20112         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20113         
20114         this.update();
20115
20116         if(suppressEvent !== true){
20117             this.fireEvent('select', this, o, v);
20118         }
20119         
20120     },
20121     
20122     getValue: function()
20123     {
20124         return this.value;
20125     },
20126     
20127     onClick: function(e) 
20128     {
20129         e.stopPropagation();
20130         e.preventDefault();
20131         
20132         var target = e.getTarget();
20133         
20134         if(target.nodeName.toLowerCase() === 'i'){
20135             target = Roo.get(target).dom.parentNode;
20136         }
20137         
20138         var nodeName = target.nodeName;
20139         var className = target.className;
20140         var html = target.innerHTML;
20141         
20142         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20143             return;
20144         }
20145         
20146         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20147         
20148         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20149         
20150         this.hide();
20151                         
20152     },
20153     
20154     picker : function()
20155     {
20156         return this.pickerEl;
20157     },
20158     
20159     fillMonths: function()
20160     {    
20161         var i = 0;
20162         var months = this.picker().select('>.datepicker-months td', true).first();
20163         
20164         months.dom.innerHTML = '';
20165         
20166         while (i < 12) {
20167             var month = {
20168                 tag: 'span',
20169                 cls: 'month',
20170                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20171             };
20172             
20173             months.createChild(month);
20174         }
20175         
20176     },
20177     
20178     update: function()
20179     {
20180         var _this = this;
20181         
20182         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20183             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20184         }
20185         
20186         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20187             e.removeClass('active');
20188             
20189             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20190                 e.addClass('active');
20191             }
20192         })
20193     },
20194     
20195     place: function()
20196     {
20197         if(this.isInline) {
20198             return;
20199         }
20200         
20201         this.picker().removeClass(['bottom', 'top']);
20202         
20203         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20204             /*
20205              * place to the top of element!
20206              *
20207              */
20208             
20209             this.picker().addClass('top');
20210             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20211             
20212             return;
20213         }
20214         
20215         this.picker().addClass('bottom');
20216         
20217         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20218     },
20219     
20220     onFocus : function()
20221     {
20222         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20223         this.show();
20224     },
20225     
20226     onBlur : function()
20227     {
20228         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20229         
20230         var d = this.inputEl().getValue();
20231         
20232         this.setValue(d);
20233                 
20234         this.hide();
20235     },
20236     
20237     show : function()
20238     {
20239         this.picker().show();
20240         this.picker().select('>.datepicker-months', true).first().show();
20241         this.update();
20242         this.place();
20243         
20244         this.fireEvent('show', this, this.date);
20245     },
20246     
20247     hide : function()
20248     {
20249         if(this.isInline) {
20250             return;
20251         }
20252         this.picker().hide();
20253         this.fireEvent('hide', this, this.date);
20254         
20255     },
20256     
20257     onMousedown: function(e)
20258     {
20259         e.stopPropagation();
20260         e.preventDefault();
20261     },
20262     
20263     keyup: function(e)
20264     {
20265         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20266         this.update();
20267     },
20268
20269     fireKey: function(e)
20270     {
20271         if (!this.picker().isVisible()){
20272             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20273                 this.show();
20274             }
20275             return;
20276         }
20277         
20278         var dir;
20279         
20280         switch(e.keyCode){
20281             case 27: // escape
20282                 this.hide();
20283                 e.preventDefault();
20284                 break;
20285             case 37: // left
20286             case 39: // right
20287                 dir = e.keyCode == 37 ? -1 : 1;
20288                 
20289                 this.vIndex = this.vIndex + dir;
20290                 
20291                 if(this.vIndex < 0){
20292                     this.vIndex = 0;
20293                 }
20294                 
20295                 if(this.vIndex > 11){
20296                     this.vIndex = 11;
20297                 }
20298                 
20299                 if(isNaN(this.vIndex)){
20300                     this.vIndex = 0;
20301                 }
20302                 
20303                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20304                 
20305                 break;
20306             case 38: // up
20307             case 40: // down
20308                 
20309                 dir = e.keyCode == 38 ? -1 : 1;
20310                 
20311                 this.vIndex = this.vIndex + dir * 4;
20312                 
20313                 if(this.vIndex < 0){
20314                     this.vIndex = 0;
20315                 }
20316                 
20317                 if(this.vIndex > 11){
20318                     this.vIndex = 11;
20319                 }
20320                 
20321                 if(isNaN(this.vIndex)){
20322                     this.vIndex = 0;
20323                 }
20324                 
20325                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20326                 break;
20327                 
20328             case 13: // enter
20329                 
20330                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20331                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20332                 }
20333                 
20334                 this.hide();
20335                 e.preventDefault();
20336                 break;
20337             case 9: // tab
20338                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20339                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20340                 }
20341                 this.hide();
20342                 break;
20343             case 16: // shift
20344             case 17: // ctrl
20345             case 18: // alt
20346                 break;
20347             default :
20348                 this.hide();
20349                 
20350         }
20351     },
20352     
20353     remove: function() 
20354     {
20355         this.picker().remove();
20356     }
20357    
20358 });
20359
20360 Roo.apply(Roo.bootstrap.MonthField,  {
20361     
20362     content : {
20363         tag: 'tbody',
20364         cn: [
20365         {
20366             tag: 'tr',
20367             cn: [
20368             {
20369                 tag: 'td',
20370                 colspan: '7'
20371             }
20372             ]
20373         }
20374         ]
20375     },
20376     
20377     dates:{
20378         en: {
20379             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20380             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20381         }
20382     }
20383 });
20384
20385 Roo.apply(Roo.bootstrap.MonthField,  {
20386   
20387     template : {
20388         tag: 'div',
20389         cls: 'datepicker dropdown-menu roo-dynamic',
20390         cn: [
20391             {
20392                 tag: 'div',
20393                 cls: 'datepicker-months',
20394                 cn: [
20395                 {
20396                     tag: 'table',
20397                     cls: 'table-condensed',
20398                     cn:[
20399                         Roo.bootstrap.DateField.content
20400                     ]
20401                 }
20402                 ]
20403             }
20404         ]
20405     }
20406 });
20407
20408  
20409
20410  
20411  /*
20412  * - LGPL
20413  *
20414  * CheckBox
20415  * 
20416  */
20417
20418 /**
20419  * @class Roo.bootstrap.CheckBox
20420  * @extends Roo.bootstrap.Input
20421  * Bootstrap CheckBox class
20422  * 
20423  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20424  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20425  * @cfg {String} boxLabel The text that appears beside the checkbox
20426  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20427  * @cfg {Boolean} checked initnal the element
20428  * @cfg {Boolean} inline inline the element (default false)
20429  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20430  * @cfg {String} tooltip label tooltip
20431  * 
20432  * @constructor
20433  * Create a new CheckBox
20434  * @param {Object} config The config object
20435  */
20436
20437 Roo.bootstrap.CheckBox = function(config){
20438     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20439    
20440     this.addEvents({
20441         /**
20442         * @event check
20443         * Fires when the element is checked or unchecked.
20444         * @param {Roo.bootstrap.CheckBox} this This input
20445         * @param {Boolean} checked The new checked value
20446         */
20447        check : true,
20448        /**
20449         * @event click
20450         * Fires when the element is click.
20451         * @param {Roo.bootstrap.CheckBox} this This input
20452         */
20453        click : true
20454     });
20455     
20456 };
20457
20458 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20459   
20460     inputType: 'checkbox',
20461     inputValue: 1,
20462     valueOff: 0,
20463     boxLabel: false,
20464     checked: false,
20465     weight : false,
20466     inline: false,
20467     tooltip : '',
20468     
20469     getAutoCreate : function()
20470     {
20471         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20472         
20473         var id = Roo.id();
20474         
20475         var cfg = {};
20476         
20477         cfg.cls = 'form-group ' + this.inputType; //input-group
20478         
20479         if(this.inline){
20480             cfg.cls += ' ' + this.inputType + '-inline';
20481         }
20482         
20483         var input =  {
20484             tag: 'input',
20485             id : id,
20486             type : this.inputType,
20487             value : this.inputValue,
20488             cls : 'roo-' + this.inputType, //'form-box',
20489             placeholder : this.placeholder || ''
20490             
20491         };
20492         
20493         if(this.inputType != 'radio'){
20494             var hidden =  {
20495                 tag: 'input',
20496                 type : 'hidden',
20497                 cls : 'roo-hidden-value',
20498                 value : this.checked ? this.inputValue : this.valueOff
20499             };
20500         }
20501         
20502             
20503         if (this.weight) { // Validity check?
20504             cfg.cls += " " + this.inputType + "-" + this.weight;
20505         }
20506         
20507         if (this.disabled) {
20508             input.disabled=true;
20509         }
20510         
20511         if(this.checked){
20512             input.checked = this.checked;
20513         }
20514         
20515         if (this.name) {
20516             
20517             input.name = this.name;
20518             
20519             if(this.inputType != 'radio'){
20520                 hidden.name = this.name;
20521                 input.name = '_hidden_' + this.name;
20522             }
20523         }
20524         
20525         if (this.size) {
20526             input.cls += ' input-' + this.size;
20527         }
20528         
20529         var settings=this;
20530         
20531         ['xs','sm','md','lg'].map(function(size){
20532             if (settings[size]) {
20533                 cfg.cls += ' col-' + size + '-' + settings[size];
20534             }
20535         });
20536         
20537         var inputblock = input;
20538          
20539         if (this.before || this.after) {
20540             
20541             inputblock = {
20542                 cls : 'input-group',
20543                 cn :  [] 
20544             };
20545             
20546             if (this.before) {
20547                 inputblock.cn.push({
20548                     tag :'span',
20549                     cls : 'input-group-addon',
20550                     html : this.before
20551                 });
20552             }
20553             
20554             inputblock.cn.push(input);
20555             
20556             if(this.inputType != 'radio'){
20557                 inputblock.cn.push(hidden);
20558             }
20559             
20560             if (this.after) {
20561                 inputblock.cn.push({
20562                     tag :'span',
20563                     cls : 'input-group-addon',
20564                     html : this.after
20565                 });
20566             }
20567             
20568         }
20569         
20570         if (align ==='left' && this.fieldLabel.length) {
20571 //                Roo.log("left and has label");
20572             cfg.cn = [
20573                 {
20574                     tag: 'label',
20575                     'for' :  id,
20576                     cls : 'control-label',
20577                     html : this.fieldLabel
20578                 },
20579                 {
20580                     cls : "", 
20581                     cn: [
20582                         inputblock
20583                     ]
20584                 }
20585             ];
20586             
20587             if(this.labelWidth > 12){
20588                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20589             }
20590             
20591             if(this.labelWidth < 13 && this.labelmd == 0){
20592                 this.labelmd = this.labelWidth;
20593             }
20594             
20595             if(this.labellg > 0){
20596                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20597                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20598             }
20599             
20600             if(this.labelmd > 0){
20601                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20602                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20603             }
20604             
20605             if(this.labelsm > 0){
20606                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20607                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20608             }
20609             
20610             if(this.labelxs > 0){
20611                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20612                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20613             }
20614             
20615         } else if ( this.fieldLabel.length) {
20616 //                Roo.log(" label");
20617                 cfg.cn = [
20618                    
20619                     {
20620                         tag: this.boxLabel ? 'span' : 'label',
20621                         'for': id,
20622                         cls: 'control-label box-input-label',
20623                         //cls : 'input-group-addon',
20624                         html : this.fieldLabel
20625                     },
20626                     
20627                     inputblock
20628                     
20629                 ];
20630
20631         } else {
20632             
20633 //                Roo.log(" no label && no align");
20634                 cfg.cn = [  inputblock ] ;
20635                 
20636                 
20637         }
20638         
20639         if(this.boxLabel){
20640              var boxLabelCfg = {
20641                 tag: 'label',
20642                 //'for': id, // box label is handled by onclick - so no for...
20643                 cls: 'box-label',
20644                 html: this.boxLabel
20645             };
20646             
20647             if(this.tooltip){
20648                 boxLabelCfg.tooltip = this.tooltip;
20649             }
20650              
20651             cfg.cn.push(boxLabelCfg);
20652         }
20653         
20654         if(this.inputType != 'radio'){
20655             cfg.cn.push(hidden);
20656         }
20657         
20658         return cfg;
20659         
20660     },
20661     
20662     /**
20663      * return the real input element.
20664      */
20665     inputEl: function ()
20666     {
20667         return this.el.select('input.roo-' + this.inputType,true).first();
20668     },
20669     hiddenEl: function ()
20670     {
20671         return this.el.select('input.roo-hidden-value',true).first();
20672     },
20673     
20674     labelEl: function()
20675     {
20676         return this.el.select('label.control-label',true).first();
20677     },
20678     /* depricated... */
20679     
20680     label: function()
20681     {
20682         return this.labelEl();
20683     },
20684     
20685     boxLabelEl: function()
20686     {
20687         return this.el.select('label.box-label',true).first();
20688     },
20689     
20690     initEvents : function()
20691     {
20692 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20693         
20694         this.inputEl().on('click', this.onClick,  this);
20695         
20696         if (this.boxLabel) { 
20697             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20698         }
20699         
20700         this.startValue = this.getValue();
20701         
20702         if(this.groupId){
20703             Roo.bootstrap.CheckBox.register(this);
20704         }
20705     },
20706     
20707     onClick : function(e)
20708     {   
20709         if(this.fireEvent('click', this, e) !== false){
20710             this.setChecked(!this.checked);
20711         }
20712         
20713     },
20714     
20715     setChecked : function(state,suppressEvent)
20716     {
20717         this.startValue = this.getValue();
20718
20719         if(this.inputType == 'radio'){
20720             
20721             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20722                 e.dom.checked = false;
20723             });
20724             
20725             this.inputEl().dom.checked = true;
20726             
20727             this.inputEl().dom.value = this.inputValue;
20728             
20729             if(suppressEvent !== true){
20730                 this.fireEvent('check', this, true);
20731             }
20732             
20733             this.validate();
20734             
20735             return;
20736         }
20737         
20738         this.checked = state;
20739         
20740         this.inputEl().dom.checked = state;
20741         
20742         
20743         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20744         
20745         if(suppressEvent !== true){
20746             this.fireEvent('check', this, state);
20747         }
20748         
20749         this.validate();
20750     },
20751     
20752     getValue : function()
20753     {
20754         if(this.inputType == 'radio'){
20755             return this.getGroupValue();
20756         }
20757         
20758         return this.hiddenEl().dom.value;
20759         
20760     },
20761     
20762     getGroupValue : function()
20763     {
20764         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20765             return '';
20766         }
20767         
20768         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20769     },
20770     
20771     setValue : function(v,suppressEvent)
20772     {
20773         if(this.inputType == 'radio'){
20774             this.setGroupValue(v, suppressEvent);
20775             return;
20776         }
20777         
20778         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20779         
20780         this.validate();
20781     },
20782     
20783     setGroupValue : function(v, suppressEvent)
20784     {
20785         this.startValue = this.getValue();
20786         
20787         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20788             e.dom.checked = false;
20789             
20790             if(e.dom.value == v){
20791                 e.dom.checked = true;
20792             }
20793         });
20794         
20795         if(suppressEvent !== true){
20796             this.fireEvent('check', this, true);
20797         }
20798
20799         this.validate();
20800         
20801         return;
20802     },
20803     
20804     validate : function()
20805     {
20806         if(this.getVisibilityEl().hasClass('hidden')){
20807             return true;
20808         }
20809         
20810         if(
20811                 this.disabled || 
20812                 (this.inputType == 'radio' && this.validateRadio()) ||
20813                 (this.inputType == 'checkbox' && this.validateCheckbox())
20814         ){
20815             this.markValid();
20816             return true;
20817         }
20818         
20819         this.markInvalid();
20820         return false;
20821     },
20822     
20823     validateRadio : function()
20824     {
20825         if(this.getVisibilityEl().hasClass('hidden')){
20826             return true;
20827         }
20828         
20829         if(this.allowBlank){
20830             return true;
20831         }
20832         
20833         var valid = false;
20834         
20835         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20836             if(!e.dom.checked){
20837                 return;
20838             }
20839             
20840             valid = true;
20841             
20842             return false;
20843         });
20844         
20845         return valid;
20846     },
20847     
20848     validateCheckbox : function()
20849     {
20850         if(!this.groupId){
20851             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20852             //return (this.getValue() == this.inputValue) ? true : false;
20853         }
20854         
20855         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20856         
20857         if(!group){
20858             return false;
20859         }
20860         
20861         var r = false;
20862         
20863         for(var i in group){
20864             if(group[i].el.isVisible(true)){
20865                 r = false;
20866                 break;
20867             }
20868             
20869             r = true;
20870         }
20871         
20872         for(var i in group){
20873             if(r){
20874                 break;
20875             }
20876             
20877             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20878         }
20879         
20880         return r;
20881     },
20882     
20883     /**
20884      * Mark this field as valid
20885      */
20886     markValid : function()
20887     {
20888         var _this = this;
20889         
20890         this.fireEvent('valid', this);
20891         
20892         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20893         
20894         if(this.groupId){
20895             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20896         }
20897         
20898         if(label){
20899             label.markValid();
20900         }
20901
20902         if(this.inputType == 'radio'){
20903             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20904                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20905                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20906             });
20907             
20908             return;
20909         }
20910
20911         if(!this.groupId){
20912             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20913             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20914             return;
20915         }
20916         
20917         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20918         
20919         if(!group){
20920             return;
20921         }
20922         
20923         for(var i in group){
20924             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20925             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20926         }
20927     },
20928     
20929      /**
20930      * Mark this field as invalid
20931      * @param {String} msg The validation message
20932      */
20933     markInvalid : function(msg)
20934     {
20935         if(this.allowBlank){
20936             return;
20937         }
20938         
20939         var _this = this;
20940         
20941         this.fireEvent('invalid', this, msg);
20942         
20943         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20944         
20945         if(this.groupId){
20946             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20947         }
20948         
20949         if(label){
20950             label.markInvalid();
20951         }
20952             
20953         if(this.inputType == 'radio'){
20954             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20955                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20956                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20957             });
20958             
20959             return;
20960         }
20961         
20962         if(!this.groupId){
20963             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20964             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20965             return;
20966         }
20967         
20968         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20969         
20970         if(!group){
20971             return;
20972         }
20973         
20974         for(var i in group){
20975             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20976             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20977         }
20978         
20979     },
20980     
20981     clearInvalid : function()
20982     {
20983         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20984         
20985         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20986         
20987         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20988         
20989         if (label && label.iconEl) {
20990             label.iconEl.removeClass(label.validClass);
20991             label.iconEl.removeClass(label.invalidClass);
20992         }
20993     },
20994     
20995     disable : function()
20996     {
20997         if(this.inputType != 'radio'){
20998             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20999             return;
21000         }
21001         
21002         var _this = this;
21003         
21004         if(this.rendered){
21005             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21006                 _this.getActionEl().addClass(this.disabledClass);
21007                 e.dom.disabled = true;
21008             });
21009         }
21010         
21011         this.disabled = true;
21012         this.fireEvent("disable", this);
21013         return this;
21014     },
21015
21016     enable : function()
21017     {
21018         if(this.inputType != 'radio'){
21019             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21020             return;
21021         }
21022         
21023         var _this = this;
21024         
21025         if(this.rendered){
21026             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21027                 _this.getActionEl().removeClass(this.disabledClass);
21028                 e.dom.disabled = false;
21029             });
21030         }
21031         
21032         this.disabled = false;
21033         this.fireEvent("enable", this);
21034         return this;
21035     },
21036     
21037     setBoxLabel : function(v)
21038     {
21039         this.boxLabel = v;
21040         
21041         if(this.rendered){
21042             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21043         }
21044     }
21045
21046 });
21047
21048 Roo.apply(Roo.bootstrap.CheckBox, {
21049     
21050     groups: {},
21051     
21052      /**
21053     * register a CheckBox Group
21054     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21055     */
21056     register : function(checkbox)
21057     {
21058         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21059             this.groups[checkbox.groupId] = {};
21060         }
21061         
21062         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21063             return;
21064         }
21065         
21066         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21067         
21068     },
21069     /**
21070     * fetch a CheckBox Group based on the group ID
21071     * @param {string} the group ID
21072     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21073     */
21074     get: function(groupId) {
21075         if (typeof(this.groups[groupId]) == 'undefined') {
21076             return false;
21077         }
21078         
21079         return this.groups[groupId] ;
21080     }
21081     
21082     
21083 });
21084 /*
21085  * - LGPL
21086  *
21087  * RadioItem
21088  * 
21089  */
21090
21091 /**
21092  * @class Roo.bootstrap.Radio
21093  * @extends Roo.bootstrap.Component
21094  * Bootstrap Radio class
21095  * @cfg {String} boxLabel - the label associated
21096  * @cfg {String} value - the value of radio
21097  * 
21098  * @constructor
21099  * Create a new Radio
21100  * @param {Object} config The config object
21101  */
21102 Roo.bootstrap.Radio = function(config){
21103     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21104     
21105 };
21106
21107 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21108     
21109     boxLabel : '',
21110     
21111     value : '',
21112     
21113     getAutoCreate : function()
21114     {
21115         var cfg = {
21116             tag : 'div',
21117             cls : 'form-group radio',
21118             cn : [
21119                 {
21120                     tag : 'label',
21121                     cls : 'box-label',
21122                     html : this.boxLabel
21123                 }
21124             ]
21125         };
21126         
21127         return cfg;
21128     },
21129     
21130     initEvents : function() 
21131     {
21132         this.parent().register(this);
21133         
21134         this.el.on('click', this.onClick, this);
21135         
21136     },
21137     
21138     onClick : function(e)
21139     {
21140         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21141             this.setChecked(true);
21142         }
21143     },
21144     
21145     setChecked : function(state, suppressEvent)
21146     {
21147         this.parent().setValue(this.value, suppressEvent);
21148         
21149     },
21150     
21151     setBoxLabel : function(v)
21152     {
21153         this.boxLabel = v;
21154         
21155         if(this.rendered){
21156             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21157         }
21158     }
21159     
21160 });
21161  
21162
21163  /*
21164  * - LGPL
21165  *
21166  * Input
21167  * 
21168  */
21169
21170 /**
21171  * @class Roo.bootstrap.SecurePass
21172  * @extends Roo.bootstrap.Input
21173  * Bootstrap SecurePass class
21174  *
21175  * 
21176  * @constructor
21177  * Create a new SecurePass
21178  * @param {Object} config The config object
21179  */
21180  
21181 Roo.bootstrap.SecurePass = function (config) {
21182     // these go here, so the translation tool can replace them..
21183     this.errors = {
21184         PwdEmpty: "Please type a password, and then retype it to confirm.",
21185         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21186         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21187         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21188         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21189         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21190         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21191         TooWeak: "Your password is Too Weak."
21192     },
21193     this.meterLabel = "Password strength:";
21194     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21195     this.meterClass = [
21196         "roo-password-meter-tooweak", 
21197         "roo-password-meter-weak", 
21198         "roo-password-meter-medium", 
21199         "roo-password-meter-strong", 
21200         "roo-password-meter-grey"
21201     ];
21202     
21203     this.errors = {};
21204     
21205     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21206 }
21207
21208 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21209     /**
21210      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21211      * {
21212      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21213      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21214      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21215      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21216      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21217      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21218      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21219      * })
21220      */
21221     // private
21222     
21223     meterWidth: 300,
21224     errorMsg :'',    
21225     errors: false,
21226     imageRoot: '/',
21227     /**
21228      * @cfg {String/Object} Label for the strength meter (defaults to
21229      * 'Password strength:')
21230      */
21231     // private
21232     meterLabel: '',
21233     /**
21234      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21235      * ['Weak', 'Medium', 'Strong'])
21236      */
21237     // private    
21238     pwdStrengths: false,    
21239     // private
21240     strength: 0,
21241     // private
21242     _lastPwd: null,
21243     // private
21244     kCapitalLetter: 0,
21245     kSmallLetter: 1,
21246     kDigit: 2,
21247     kPunctuation: 3,
21248     
21249     insecure: false,
21250     // private
21251     initEvents: function ()
21252     {
21253         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21254
21255         if (this.el.is('input[type=password]') && Roo.isSafari) {
21256             this.el.on('keydown', this.SafariOnKeyDown, this);
21257         }
21258
21259         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21260     },
21261     // private
21262     onRender: function (ct, position)
21263     {
21264         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21265         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21266         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21267
21268         this.trigger.createChild({
21269                    cn: [
21270                     {
21271                     //id: 'PwdMeter',
21272                     tag: 'div',
21273                     cls: 'roo-password-meter-grey col-xs-12',
21274                     style: {
21275                         //width: 0,
21276                         //width: this.meterWidth + 'px'                                                
21277                         }
21278                     },
21279                     {                            
21280                          cls: 'roo-password-meter-text'                          
21281                     }
21282                 ]            
21283         });
21284
21285          
21286         if (this.hideTrigger) {
21287             this.trigger.setDisplayed(false);
21288         }
21289         this.setSize(this.width || '', this.height || '');
21290     },
21291     // private
21292     onDestroy: function ()
21293     {
21294         if (this.trigger) {
21295             this.trigger.removeAllListeners();
21296             this.trigger.remove();
21297         }
21298         if (this.wrap) {
21299             this.wrap.remove();
21300         }
21301         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21302     },
21303     // private
21304     checkStrength: function ()
21305     {
21306         var pwd = this.inputEl().getValue();
21307         if (pwd == this._lastPwd) {
21308             return;
21309         }
21310
21311         var strength;
21312         if (this.ClientSideStrongPassword(pwd)) {
21313             strength = 3;
21314         } else if (this.ClientSideMediumPassword(pwd)) {
21315             strength = 2;
21316         } else if (this.ClientSideWeakPassword(pwd)) {
21317             strength = 1;
21318         } else {
21319             strength = 0;
21320         }
21321         
21322         Roo.log('strength1: ' + strength);
21323         
21324         //var pm = this.trigger.child('div/div/div').dom;
21325         var pm = this.trigger.child('div/div');
21326         pm.removeClass(this.meterClass);
21327         pm.addClass(this.meterClass[strength]);
21328                 
21329         
21330         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21331                 
21332         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21333         
21334         this._lastPwd = pwd;
21335     },
21336     reset: function ()
21337     {
21338         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21339         
21340         this._lastPwd = '';
21341         
21342         var pm = this.trigger.child('div/div');
21343         pm.removeClass(this.meterClass);
21344         pm.addClass('roo-password-meter-grey');        
21345         
21346         
21347         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21348         
21349         pt.innerHTML = '';
21350         this.inputEl().dom.type='password';
21351     },
21352     // private
21353     validateValue: function (value)
21354     {
21355         
21356         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21357             return false;
21358         }
21359         if (value.length == 0) {
21360             if (this.allowBlank) {
21361                 this.clearInvalid();
21362                 return true;
21363             }
21364
21365             this.markInvalid(this.errors.PwdEmpty);
21366             this.errorMsg = this.errors.PwdEmpty;
21367             return false;
21368         }
21369         
21370         if(this.insecure){
21371             return true;
21372         }
21373         
21374         if ('[\x21-\x7e]*'.match(value)) {
21375             this.markInvalid(this.errors.PwdBadChar);
21376             this.errorMsg = this.errors.PwdBadChar;
21377             return false;
21378         }
21379         if (value.length < 6) {
21380             this.markInvalid(this.errors.PwdShort);
21381             this.errorMsg = this.errors.PwdShort;
21382             return false;
21383         }
21384         if (value.length > 16) {
21385             this.markInvalid(this.errors.PwdLong);
21386             this.errorMsg = this.errors.PwdLong;
21387             return false;
21388         }
21389         var strength;
21390         if (this.ClientSideStrongPassword(value)) {
21391             strength = 3;
21392         } else if (this.ClientSideMediumPassword(value)) {
21393             strength = 2;
21394         } else if (this.ClientSideWeakPassword(value)) {
21395             strength = 1;
21396         } else {
21397             strength = 0;
21398         }
21399
21400         
21401         if (strength < 2) {
21402             //this.markInvalid(this.errors.TooWeak);
21403             this.errorMsg = this.errors.TooWeak;
21404             //return false;
21405         }
21406         
21407         
21408         console.log('strength2: ' + strength);
21409         
21410         //var pm = this.trigger.child('div/div/div').dom;
21411         
21412         var pm = this.trigger.child('div/div');
21413         pm.removeClass(this.meterClass);
21414         pm.addClass(this.meterClass[strength]);
21415                 
21416         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21417                 
21418         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21419         
21420         this.errorMsg = ''; 
21421         return true;
21422     },
21423     // private
21424     CharacterSetChecks: function (type)
21425     {
21426         this.type = type;
21427         this.fResult = false;
21428     },
21429     // private
21430     isctype: function (character, type)
21431     {
21432         switch (type) {  
21433             case this.kCapitalLetter:
21434                 if (character >= 'A' && character <= 'Z') {
21435                     return true;
21436                 }
21437                 break;
21438             
21439             case this.kSmallLetter:
21440                 if (character >= 'a' && character <= 'z') {
21441                     return true;
21442                 }
21443                 break;
21444             
21445             case this.kDigit:
21446                 if (character >= '0' && character <= '9') {
21447                     return true;
21448                 }
21449                 break;
21450             
21451             case this.kPunctuation:
21452                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21453                     return true;
21454                 }
21455                 break;
21456             
21457             default:
21458                 return false;
21459         }
21460
21461     },
21462     // private
21463     IsLongEnough: function (pwd, size)
21464     {
21465         return !(pwd == null || isNaN(size) || pwd.length < size);
21466     },
21467     // private
21468     SpansEnoughCharacterSets: function (word, nb)
21469     {
21470         if (!this.IsLongEnough(word, nb))
21471         {
21472             return false;
21473         }
21474
21475         var characterSetChecks = new Array(
21476             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21477             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21478         );
21479         
21480         for (var index = 0; index < word.length; ++index) {
21481             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21482                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21483                     characterSetChecks[nCharSet].fResult = true;
21484                     break;
21485                 }
21486             }
21487         }
21488
21489         var nCharSets = 0;
21490         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21491             if (characterSetChecks[nCharSet].fResult) {
21492                 ++nCharSets;
21493             }
21494         }
21495
21496         if (nCharSets < nb) {
21497             return false;
21498         }
21499         return true;
21500     },
21501     // private
21502     ClientSideStrongPassword: function (pwd)
21503     {
21504         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21505     },
21506     // private
21507     ClientSideMediumPassword: function (pwd)
21508     {
21509         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21510     },
21511     // private
21512     ClientSideWeakPassword: function (pwd)
21513     {
21514         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21515     }
21516           
21517 })//<script type="text/javascript">
21518
21519 /*
21520  * Based  Ext JS Library 1.1.1
21521  * Copyright(c) 2006-2007, Ext JS, LLC.
21522  * LGPL
21523  *
21524  */
21525  
21526 /**
21527  * @class Roo.HtmlEditorCore
21528  * @extends Roo.Component
21529  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21530  *
21531  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21532  */
21533
21534 Roo.HtmlEditorCore = function(config){
21535     
21536     
21537     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21538     
21539     
21540     this.addEvents({
21541         /**
21542          * @event initialize
21543          * Fires when the editor is fully initialized (including the iframe)
21544          * @param {Roo.HtmlEditorCore} this
21545          */
21546         initialize: true,
21547         /**
21548          * @event activate
21549          * Fires when the editor is first receives the focus. Any insertion must wait
21550          * until after this event.
21551          * @param {Roo.HtmlEditorCore} this
21552          */
21553         activate: true,
21554          /**
21555          * @event beforesync
21556          * Fires before the textarea is updated with content from the editor iframe. Return false
21557          * to cancel the sync.
21558          * @param {Roo.HtmlEditorCore} this
21559          * @param {String} html
21560          */
21561         beforesync: true,
21562          /**
21563          * @event beforepush
21564          * Fires before the iframe editor is updated with content from the textarea. Return false
21565          * to cancel the push.
21566          * @param {Roo.HtmlEditorCore} this
21567          * @param {String} html
21568          */
21569         beforepush: true,
21570          /**
21571          * @event sync
21572          * Fires when the textarea is updated with content from the editor iframe.
21573          * @param {Roo.HtmlEditorCore} this
21574          * @param {String} html
21575          */
21576         sync: true,
21577          /**
21578          * @event push
21579          * Fires when the iframe editor is updated with content from the textarea.
21580          * @param {Roo.HtmlEditorCore} this
21581          * @param {String} html
21582          */
21583         push: true,
21584         
21585         /**
21586          * @event editorevent
21587          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21588          * @param {Roo.HtmlEditorCore} this
21589          */
21590         editorevent: true
21591         
21592     });
21593     
21594     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21595     
21596     // defaults : white / black...
21597     this.applyBlacklists();
21598     
21599     
21600     
21601 };
21602
21603
21604 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21605
21606
21607      /**
21608      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21609      */
21610     
21611     owner : false,
21612     
21613      /**
21614      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21615      *                        Roo.resizable.
21616      */
21617     resizable : false,
21618      /**
21619      * @cfg {Number} height (in pixels)
21620      */   
21621     height: 300,
21622    /**
21623      * @cfg {Number} width (in pixels)
21624      */   
21625     width: 500,
21626     
21627     /**
21628      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21629      * 
21630      */
21631     stylesheets: false,
21632     
21633     // id of frame..
21634     frameId: false,
21635     
21636     // private properties
21637     validationEvent : false,
21638     deferHeight: true,
21639     initialized : false,
21640     activated : false,
21641     sourceEditMode : false,
21642     onFocus : Roo.emptyFn,
21643     iframePad:3,
21644     hideMode:'offsets',
21645     
21646     clearUp: true,
21647     
21648     // blacklist + whitelisted elements..
21649     black: false,
21650     white: false,
21651      
21652     bodyCls : '',
21653
21654     /**
21655      * Protected method that will not generally be called directly. It
21656      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21657      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21658      */
21659     getDocMarkup : function(){
21660         // body styles..
21661         var st = '';
21662         
21663         // inherit styels from page...?? 
21664         if (this.stylesheets === false) {
21665             
21666             Roo.get(document.head).select('style').each(function(node) {
21667                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21668             });
21669             
21670             Roo.get(document.head).select('link').each(function(node) { 
21671                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21672             });
21673             
21674         } else if (!this.stylesheets.length) {
21675                 // simple..
21676                 st = '<style type="text/css">' +
21677                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21678                    '</style>';
21679         } else { 
21680             st = '<style type="text/css">' +
21681                     this.stylesheets +
21682                 '</style>';
21683         }
21684         
21685         st +=  '<style type="text/css">' +
21686             'IMG { cursor: pointer } ' +
21687         '</style>';
21688
21689         var cls = 'roo-htmleditor-body';
21690         
21691         if(this.bodyCls.length){
21692             cls += ' ' + this.bodyCls;
21693         }
21694         
21695         return '<html><head>' + st  +
21696             //<style type="text/css">' +
21697             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21698             //'</style>' +
21699             ' </head><body class="' +  cls + '"></body></html>';
21700     },
21701
21702     // private
21703     onRender : function(ct, position)
21704     {
21705         var _t = this;
21706         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21707         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21708         
21709         
21710         this.el.dom.style.border = '0 none';
21711         this.el.dom.setAttribute('tabIndex', -1);
21712         this.el.addClass('x-hidden hide');
21713         
21714         
21715         
21716         if(Roo.isIE){ // fix IE 1px bogus margin
21717             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21718         }
21719        
21720         
21721         this.frameId = Roo.id();
21722         
21723          
21724         
21725         var iframe = this.owner.wrap.createChild({
21726             tag: 'iframe',
21727             cls: 'form-control', // bootstrap..
21728             id: this.frameId,
21729             name: this.frameId,
21730             frameBorder : 'no',
21731             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21732         }, this.el
21733         );
21734         
21735         
21736         this.iframe = iframe.dom;
21737
21738          this.assignDocWin();
21739         
21740         this.doc.designMode = 'on';
21741        
21742         this.doc.open();
21743         this.doc.write(this.getDocMarkup());
21744         this.doc.close();
21745
21746         
21747         var task = { // must defer to wait for browser to be ready
21748             run : function(){
21749                 //console.log("run task?" + this.doc.readyState);
21750                 this.assignDocWin();
21751                 if(this.doc.body || this.doc.readyState == 'complete'){
21752                     try {
21753                         this.doc.designMode="on";
21754                     } catch (e) {
21755                         return;
21756                     }
21757                     Roo.TaskMgr.stop(task);
21758                     this.initEditor.defer(10, this);
21759                 }
21760             },
21761             interval : 10,
21762             duration: 10000,
21763             scope: this
21764         };
21765         Roo.TaskMgr.start(task);
21766
21767     },
21768
21769     // private
21770     onResize : function(w, h)
21771     {
21772          Roo.log('resize: ' +w + ',' + h );
21773         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21774         if(!this.iframe){
21775             return;
21776         }
21777         if(typeof w == 'number'){
21778             
21779             this.iframe.style.width = w + 'px';
21780         }
21781         if(typeof h == 'number'){
21782             
21783             this.iframe.style.height = h + 'px';
21784             if(this.doc){
21785                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21786             }
21787         }
21788         
21789     },
21790
21791     /**
21792      * Toggles the editor between standard and source edit mode.
21793      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21794      */
21795     toggleSourceEdit : function(sourceEditMode){
21796         
21797         this.sourceEditMode = sourceEditMode === true;
21798         
21799         if(this.sourceEditMode){
21800  
21801             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21802             
21803         }else{
21804             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21805             //this.iframe.className = '';
21806             this.deferFocus();
21807         }
21808         //this.setSize(this.owner.wrap.getSize());
21809         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21810     },
21811
21812     
21813   
21814
21815     /**
21816      * Protected method that will not generally be called directly. If you need/want
21817      * custom HTML cleanup, this is the method you should override.
21818      * @param {String} html The HTML to be cleaned
21819      * return {String} The cleaned HTML
21820      */
21821     cleanHtml : function(html){
21822         html = String(html);
21823         if(html.length > 5){
21824             if(Roo.isSafari){ // strip safari nonsense
21825                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21826             }
21827         }
21828         if(html == '&nbsp;'){
21829             html = '';
21830         }
21831         return html;
21832     },
21833
21834     /**
21835      * HTML Editor -> Textarea
21836      * Protected method that will not generally be called directly. Syncs the contents
21837      * of the editor iframe with the textarea.
21838      */
21839     syncValue : function(){
21840         if(this.initialized){
21841             var bd = (this.doc.body || this.doc.documentElement);
21842             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21843             var html = bd.innerHTML;
21844             if(Roo.isSafari){
21845                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21846                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21847                 if(m && m[1]){
21848                     html = '<div style="'+m[0]+'">' + html + '</div>';
21849                 }
21850             }
21851             html = this.cleanHtml(html);
21852             // fix up the special chars.. normaly like back quotes in word...
21853             // however we do not want to do this with chinese..
21854             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21855                 var cc = b.charCodeAt();
21856                 if (
21857                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21858                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21859                     (cc >= 0xf900 && cc < 0xfb00 )
21860                 ) {
21861                         return b;
21862                 }
21863                 return "&#"+cc+";" 
21864             });
21865             if(this.owner.fireEvent('beforesync', this, html) !== false){
21866                 this.el.dom.value = html;
21867                 this.owner.fireEvent('sync', this, html);
21868             }
21869         }
21870     },
21871
21872     /**
21873      * Protected method that will not generally be called directly. Pushes the value of the textarea
21874      * into the iframe editor.
21875      */
21876     pushValue : function(){
21877         if(this.initialized){
21878             var v = this.el.dom.value.trim();
21879             
21880 //            if(v.length < 1){
21881 //                v = '&#160;';
21882 //            }
21883             
21884             if(this.owner.fireEvent('beforepush', this, v) !== false){
21885                 var d = (this.doc.body || this.doc.documentElement);
21886                 d.innerHTML = v;
21887                 this.cleanUpPaste();
21888                 this.el.dom.value = d.innerHTML;
21889                 this.owner.fireEvent('push', this, v);
21890             }
21891         }
21892     },
21893
21894     // private
21895     deferFocus : function(){
21896         this.focus.defer(10, this);
21897     },
21898
21899     // doc'ed in Field
21900     focus : function(){
21901         if(this.win && !this.sourceEditMode){
21902             this.win.focus();
21903         }else{
21904             this.el.focus();
21905         }
21906     },
21907     
21908     assignDocWin: function()
21909     {
21910         var iframe = this.iframe;
21911         
21912          if(Roo.isIE){
21913             this.doc = iframe.contentWindow.document;
21914             this.win = iframe.contentWindow;
21915         } else {
21916 //            if (!Roo.get(this.frameId)) {
21917 //                return;
21918 //            }
21919 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21920 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21921             
21922             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21923                 return;
21924             }
21925             
21926             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21927             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21928         }
21929     },
21930     
21931     // private
21932     initEditor : function(){
21933         //console.log("INIT EDITOR");
21934         this.assignDocWin();
21935         
21936         
21937         
21938         this.doc.designMode="on";
21939         this.doc.open();
21940         this.doc.write(this.getDocMarkup());
21941         this.doc.close();
21942         
21943         var dbody = (this.doc.body || this.doc.documentElement);
21944         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21945         // this copies styles from the containing element into thsi one..
21946         // not sure why we need all of this..
21947         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21948         
21949         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21950         //ss['background-attachment'] = 'fixed'; // w3c
21951         dbody.bgProperties = 'fixed'; // ie
21952         //Roo.DomHelper.applyStyles(dbody, ss);
21953         Roo.EventManager.on(this.doc, {
21954             //'mousedown': this.onEditorEvent,
21955             'mouseup': this.onEditorEvent,
21956             'dblclick': this.onEditorEvent,
21957             'click': this.onEditorEvent,
21958             'keyup': this.onEditorEvent,
21959             buffer:100,
21960             scope: this
21961         });
21962         if(Roo.isGecko){
21963             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21964         }
21965         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21966             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21967         }
21968         this.initialized = true;
21969
21970         this.owner.fireEvent('initialize', this);
21971         this.pushValue();
21972     },
21973
21974     // private
21975     onDestroy : function(){
21976         
21977         
21978         
21979         if(this.rendered){
21980             
21981             //for (var i =0; i < this.toolbars.length;i++) {
21982             //    // fixme - ask toolbars for heights?
21983             //    this.toolbars[i].onDestroy();
21984            // }
21985             
21986             //this.wrap.dom.innerHTML = '';
21987             //this.wrap.remove();
21988         }
21989     },
21990
21991     // private
21992     onFirstFocus : function(){
21993         
21994         this.assignDocWin();
21995         
21996         
21997         this.activated = true;
21998          
21999     
22000         if(Roo.isGecko){ // prevent silly gecko errors
22001             this.win.focus();
22002             var s = this.win.getSelection();
22003             if(!s.focusNode || s.focusNode.nodeType != 3){
22004                 var r = s.getRangeAt(0);
22005                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22006                 r.collapse(true);
22007                 this.deferFocus();
22008             }
22009             try{
22010                 this.execCmd('useCSS', true);
22011                 this.execCmd('styleWithCSS', false);
22012             }catch(e){}
22013         }
22014         this.owner.fireEvent('activate', this);
22015     },
22016
22017     // private
22018     adjustFont: function(btn){
22019         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22020         //if(Roo.isSafari){ // safari
22021         //    adjust *= 2;
22022        // }
22023         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22024         if(Roo.isSafari){ // safari
22025             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22026             v =  (v < 10) ? 10 : v;
22027             v =  (v > 48) ? 48 : v;
22028             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22029             
22030         }
22031         
22032         
22033         v = Math.max(1, v+adjust);
22034         
22035         this.execCmd('FontSize', v  );
22036     },
22037
22038     onEditorEvent : function(e)
22039     {
22040         this.owner.fireEvent('editorevent', this, e);
22041       //  this.updateToolbar();
22042         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22043     },
22044
22045     insertTag : function(tg)
22046     {
22047         // could be a bit smarter... -> wrap the current selected tRoo..
22048         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22049             
22050             range = this.createRange(this.getSelection());
22051             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22052             wrappingNode.appendChild(range.extractContents());
22053             range.insertNode(wrappingNode);
22054
22055             return;
22056             
22057             
22058             
22059         }
22060         this.execCmd("formatblock",   tg);
22061         
22062     },
22063     
22064     insertText : function(txt)
22065     {
22066         
22067         
22068         var range = this.createRange();
22069         range.deleteContents();
22070                //alert(Sender.getAttribute('label'));
22071                
22072         range.insertNode(this.doc.createTextNode(txt));
22073     } ,
22074     
22075      
22076
22077     /**
22078      * Executes a Midas editor command on the editor document and performs necessary focus and
22079      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22080      * @param {String} cmd The Midas command
22081      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22082      */
22083     relayCmd : function(cmd, value){
22084         this.win.focus();
22085         this.execCmd(cmd, value);
22086         this.owner.fireEvent('editorevent', this);
22087         //this.updateToolbar();
22088         this.owner.deferFocus();
22089     },
22090
22091     /**
22092      * Executes a Midas editor command directly on the editor document.
22093      * For visual commands, you should use {@link #relayCmd} instead.
22094      * <b>This should only be called after the editor is initialized.</b>
22095      * @param {String} cmd The Midas command
22096      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22097      */
22098     execCmd : function(cmd, value){
22099         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22100         this.syncValue();
22101     },
22102  
22103  
22104    
22105     /**
22106      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22107      * to insert tRoo.
22108      * @param {String} text | dom node.. 
22109      */
22110     insertAtCursor : function(text)
22111     {
22112         
22113         if(!this.activated){
22114             return;
22115         }
22116         /*
22117         if(Roo.isIE){
22118             this.win.focus();
22119             var r = this.doc.selection.createRange();
22120             if(r){
22121                 r.collapse(true);
22122                 r.pasteHTML(text);
22123                 this.syncValue();
22124                 this.deferFocus();
22125             
22126             }
22127             return;
22128         }
22129         */
22130         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22131             this.win.focus();
22132             
22133             
22134             // from jquery ui (MIT licenced)
22135             var range, node;
22136             var win = this.win;
22137             
22138             if (win.getSelection && win.getSelection().getRangeAt) {
22139                 range = win.getSelection().getRangeAt(0);
22140                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22141                 range.insertNode(node);
22142             } else if (win.document.selection && win.document.selection.createRange) {
22143                 // no firefox support
22144                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22145                 win.document.selection.createRange().pasteHTML(txt);
22146             } else {
22147                 // no firefox support
22148                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22149                 this.execCmd('InsertHTML', txt);
22150             } 
22151             
22152             this.syncValue();
22153             
22154             this.deferFocus();
22155         }
22156     },
22157  // private
22158     mozKeyPress : function(e){
22159         if(e.ctrlKey){
22160             var c = e.getCharCode(), cmd;
22161           
22162             if(c > 0){
22163                 c = String.fromCharCode(c).toLowerCase();
22164                 switch(c){
22165                     case 'b':
22166                         cmd = 'bold';
22167                         break;
22168                     case 'i':
22169                         cmd = 'italic';
22170                         break;
22171                     
22172                     case 'u':
22173                         cmd = 'underline';
22174                         break;
22175                     
22176                     case 'v':
22177                         this.cleanUpPaste.defer(100, this);
22178                         return;
22179                         
22180                 }
22181                 if(cmd){
22182                     this.win.focus();
22183                     this.execCmd(cmd);
22184                     this.deferFocus();
22185                     e.preventDefault();
22186                 }
22187                 
22188             }
22189         }
22190     },
22191
22192     // private
22193     fixKeys : function(){ // load time branching for fastest keydown performance
22194         if(Roo.isIE){
22195             return function(e){
22196                 var k = e.getKey(), r;
22197                 if(k == e.TAB){
22198                     e.stopEvent();
22199                     r = this.doc.selection.createRange();
22200                     if(r){
22201                         r.collapse(true);
22202                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22203                         this.deferFocus();
22204                     }
22205                     return;
22206                 }
22207                 
22208                 if(k == e.ENTER){
22209                     r = this.doc.selection.createRange();
22210                     if(r){
22211                         var target = r.parentElement();
22212                         if(!target || target.tagName.toLowerCase() != 'li'){
22213                             e.stopEvent();
22214                             r.pasteHTML('<br />');
22215                             r.collapse(false);
22216                             r.select();
22217                         }
22218                     }
22219                 }
22220                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22221                     this.cleanUpPaste.defer(100, this);
22222                     return;
22223                 }
22224                 
22225                 
22226             };
22227         }else if(Roo.isOpera){
22228             return function(e){
22229                 var k = e.getKey();
22230                 if(k == e.TAB){
22231                     e.stopEvent();
22232                     this.win.focus();
22233                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22234                     this.deferFocus();
22235                 }
22236                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22237                     this.cleanUpPaste.defer(100, this);
22238                     return;
22239                 }
22240                 
22241             };
22242         }else if(Roo.isSafari){
22243             return function(e){
22244                 var k = e.getKey();
22245                 
22246                 if(k == e.TAB){
22247                     e.stopEvent();
22248                     this.execCmd('InsertText','\t');
22249                     this.deferFocus();
22250                     return;
22251                 }
22252                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22253                     this.cleanUpPaste.defer(100, this);
22254                     return;
22255                 }
22256                 
22257              };
22258         }
22259     }(),
22260     
22261     getAllAncestors: function()
22262     {
22263         var p = this.getSelectedNode();
22264         var a = [];
22265         if (!p) {
22266             a.push(p); // push blank onto stack..
22267             p = this.getParentElement();
22268         }
22269         
22270         
22271         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22272             a.push(p);
22273             p = p.parentNode;
22274         }
22275         a.push(this.doc.body);
22276         return a;
22277     },
22278     lastSel : false,
22279     lastSelNode : false,
22280     
22281     
22282     getSelection : function() 
22283     {
22284         this.assignDocWin();
22285         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22286     },
22287     
22288     getSelectedNode: function() 
22289     {
22290         // this may only work on Gecko!!!
22291         
22292         // should we cache this!!!!
22293         
22294         
22295         
22296          
22297         var range = this.createRange(this.getSelection()).cloneRange();
22298         
22299         if (Roo.isIE) {
22300             var parent = range.parentElement();
22301             while (true) {
22302                 var testRange = range.duplicate();
22303                 testRange.moveToElementText(parent);
22304                 if (testRange.inRange(range)) {
22305                     break;
22306                 }
22307                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22308                     break;
22309                 }
22310                 parent = parent.parentElement;
22311             }
22312             return parent;
22313         }
22314         
22315         // is ancestor a text element.
22316         var ac =  range.commonAncestorContainer;
22317         if (ac.nodeType == 3) {
22318             ac = ac.parentNode;
22319         }
22320         
22321         var ar = ac.childNodes;
22322          
22323         var nodes = [];
22324         var other_nodes = [];
22325         var has_other_nodes = false;
22326         for (var i=0;i<ar.length;i++) {
22327             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22328                 continue;
22329             }
22330             // fullly contained node.
22331             
22332             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22333                 nodes.push(ar[i]);
22334                 continue;
22335             }
22336             
22337             // probably selected..
22338             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22339                 other_nodes.push(ar[i]);
22340                 continue;
22341             }
22342             // outer..
22343             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22344                 continue;
22345             }
22346             
22347             
22348             has_other_nodes = true;
22349         }
22350         if (!nodes.length && other_nodes.length) {
22351             nodes= other_nodes;
22352         }
22353         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22354             return false;
22355         }
22356         
22357         return nodes[0];
22358     },
22359     createRange: function(sel)
22360     {
22361         // this has strange effects when using with 
22362         // top toolbar - not sure if it's a great idea.
22363         //this.editor.contentWindow.focus();
22364         if (typeof sel != "undefined") {
22365             try {
22366                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22367             } catch(e) {
22368                 return this.doc.createRange();
22369             }
22370         } else {
22371             return this.doc.createRange();
22372         }
22373     },
22374     getParentElement: function()
22375     {
22376         
22377         this.assignDocWin();
22378         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22379         
22380         var range = this.createRange(sel);
22381          
22382         try {
22383             var p = range.commonAncestorContainer;
22384             while (p.nodeType == 3) { // text node
22385                 p = p.parentNode;
22386             }
22387             return p;
22388         } catch (e) {
22389             return null;
22390         }
22391     
22392     },
22393     /***
22394      *
22395      * Range intersection.. the hard stuff...
22396      *  '-1' = before
22397      *  '0' = hits..
22398      *  '1' = after.
22399      *         [ -- selected range --- ]
22400      *   [fail]                        [fail]
22401      *
22402      *    basically..
22403      *      if end is before start or  hits it. fail.
22404      *      if start is after end or hits it fail.
22405      *
22406      *   if either hits (but other is outside. - then it's not 
22407      *   
22408      *    
22409      **/
22410     
22411     
22412     // @see http://www.thismuchiknow.co.uk/?p=64.
22413     rangeIntersectsNode : function(range, node)
22414     {
22415         var nodeRange = node.ownerDocument.createRange();
22416         try {
22417             nodeRange.selectNode(node);
22418         } catch (e) {
22419             nodeRange.selectNodeContents(node);
22420         }
22421     
22422         var rangeStartRange = range.cloneRange();
22423         rangeStartRange.collapse(true);
22424     
22425         var rangeEndRange = range.cloneRange();
22426         rangeEndRange.collapse(false);
22427     
22428         var nodeStartRange = nodeRange.cloneRange();
22429         nodeStartRange.collapse(true);
22430     
22431         var nodeEndRange = nodeRange.cloneRange();
22432         nodeEndRange.collapse(false);
22433     
22434         return rangeStartRange.compareBoundaryPoints(
22435                  Range.START_TO_START, nodeEndRange) == -1 &&
22436                rangeEndRange.compareBoundaryPoints(
22437                  Range.START_TO_START, nodeStartRange) == 1;
22438         
22439          
22440     },
22441     rangeCompareNode : function(range, node)
22442     {
22443         var nodeRange = node.ownerDocument.createRange();
22444         try {
22445             nodeRange.selectNode(node);
22446         } catch (e) {
22447             nodeRange.selectNodeContents(node);
22448         }
22449         
22450         
22451         range.collapse(true);
22452     
22453         nodeRange.collapse(true);
22454      
22455         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22456         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22457          
22458         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22459         
22460         var nodeIsBefore   =  ss == 1;
22461         var nodeIsAfter    = ee == -1;
22462         
22463         if (nodeIsBefore && nodeIsAfter) {
22464             return 0; // outer
22465         }
22466         if (!nodeIsBefore && nodeIsAfter) {
22467             return 1; //right trailed.
22468         }
22469         
22470         if (nodeIsBefore && !nodeIsAfter) {
22471             return 2;  // left trailed.
22472         }
22473         // fully contined.
22474         return 3;
22475     },
22476
22477     // private? - in a new class?
22478     cleanUpPaste :  function()
22479     {
22480         // cleans up the whole document..
22481         Roo.log('cleanuppaste');
22482         
22483         this.cleanUpChildren(this.doc.body);
22484         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22485         if (clean != this.doc.body.innerHTML) {
22486             this.doc.body.innerHTML = clean;
22487         }
22488         
22489     },
22490     
22491     cleanWordChars : function(input) {// change the chars to hex code
22492         var he = Roo.HtmlEditorCore;
22493         
22494         var output = input;
22495         Roo.each(he.swapCodes, function(sw) { 
22496             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22497             
22498             output = output.replace(swapper, sw[1]);
22499         });
22500         
22501         return output;
22502     },
22503     
22504     
22505     cleanUpChildren : function (n)
22506     {
22507         if (!n.childNodes.length) {
22508             return;
22509         }
22510         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22511            this.cleanUpChild(n.childNodes[i]);
22512         }
22513     },
22514     
22515     
22516         
22517     
22518     cleanUpChild : function (node)
22519     {
22520         var ed = this;
22521         //console.log(node);
22522         if (node.nodeName == "#text") {
22523             // clean up silly Windows -- stuff?
22524             return; 
22525         }
22526         if (node.nodeName == "#comment") {
22527             node.parentNode.removeChild(node);
22528             // clean up silly Windows -- stuff?
22529             return; 
22530         }
22531         var lcname = node.tagName.toLowerCase();
22532         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22533         // whitelist of tags..
22534         
22535         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22536             // remove node.
22537             node.parentNode.removeChild(node);
22538             return;
22539             
22540         }
22541         
22542         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22543         
22544         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22545         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22546         
22547         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22548         //    remove_keep_children = true;
22549         //}
22550         
22551         if (remove_keep_children) {
22552             this.cleanUpChildren(node);
22553             // inserts everything just before this node...
22554             while (node.childNodes.length) {
22555                 var cn = node.childNodes[0];
22556                 node.removeChild(cn);
22557                 node.parentNode.insertBefore(cn, node);
22558             }
22559             node.parentNode.removeChild(node);
22560             return;
22561         }
22562         
22563         if (!node.attributes || !node.attributes.length) {
22564             this.cleanUpChildren(node);
22565             return;
22566         }
22567         
22568         function cleanAttr(n,v)
22569         {
22570             
22571             if (v.match(/^\./) || v.match(/^\//)) {
22572                 return;
22573             }
22574             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22575                 return;
22576             }
22577             if (v.match(/^#/)) {
22578                 return;
22579             }
22580 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22581             node.removeAttribute(n);
22582             
22583         }
22584         
22585         var cwhite = this.cwhite;
22586         var cblack = this.cblack;
22587             
22588         function cleanStyle(n,v)
22589         {
22590             if (v.match(/expression/)) { //XSS?? should we even bother..
22591                 node.removeAttribute(n);
22592                 return;
22593             }
22594             
22595             var parts = v.split(/;/);
22596             var clean = [];
22597             
22598             Roo.each(parts, function(p) {
22599                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22600                 if (!p.length) {
22601                     return true;
22602                 }
22603                 var l = p.split(':').shift().replace(/\s+/g,'');
22604                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22605                 
22606                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22607 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22608                     //node.removeAttribute(n);
22609                     return true;
22610                 }
22611                 //Roo.log()
22612                 // only allow 'c whitelisted system attributes'
22613                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22614 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22615                     //node.removeAttribute(n);
22616                     return true;
22617                 }
22618                 
22619                 
22620                  
22621                 
22622                 clean.push(p);
22623                 return true;
22624             });
22625             if (clean.length) { 
22626                 node.setAttribute(n, clean.join(';'));
22627             } else {
22628                 node.removeAttribute(n);
22629             }
22630             
22631         }
22632         
22633         
22634         for (var i = node.attributes.length-1; i > -1 ; i--) {
22635             var a = node.attributes[i];
22636             //console.log(a);
22637             
22638             if (a.name.toLowerCase().substr(0,2)=='on')  {
22639                 node.removeAttribute(a.name);
22640                 continue;
22641             }
22642             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22643                 node.removeAttribute(a.name);
22644                 continue;
22645             }
22646             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22647                 cleanAttr(a.name,a.value); // fixme..
22648                 continue;
22649             }
22650             if (a.name == 'style') {
22651                 cleanStyle(a.name,a.value);
22652                 continue;
22653             }
22654             /// clean up MS crap..
22655             // tecnically this should be a list of valid class'es..
22656             
22657             
22658             if (a.name == 'class') {
22659                 if (a.value.match(/^Mso/)) {
22660                     node.className = '';
22661                 }
22662                 
22663                 if (a.value.match(/^body$/)) {
22664                     node.className = '';
22665                 }
22666                 continue;
22667             }
22668             
22669             // style cleanup!?
22670             // class cleanup?
22671             
22672         }
22673         
22674         
22675         this.cleanUpChildren(node);
22676         
22677         
22678     },
22679     
22680     /**
22681      * Clean up MS wordisms...
22682      */
22683     cleanWord : function(node)
22684     {
22685         
22686         
22687         if (!node) {
22688             this.cleanWord(this.doc.body);
22689             return;
22690         }
22691         if (node.nodeName == "#text") {
22692             // clean up silly Windows -- stuff?
22693             return; 
22694         }
22695         if (node.nodeName == "#comment") {
22696             node.parentNode.removeChild(node);
22697             // clean up silly Windows -- stuff?
22698             return; 
22699         }
22700         
22701         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22702             node.parentNode.removeChild(node);
22703             return;
22704         }
22705         
22706         // remove - but keep children..
22707         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22708             while (node.childNodes.length) {
22709                 var cn = node.childNodes[0];
22710                 node.removeChild(cn);
22711                 node.parentNode.insertBefore(cn, node);
22712             }
22713             node.parentNode.removeChild(node);
22714             this.iterateChildren(node, this.cleanWord);
22715             return;
22716         }
22717         // clean styles
22718         if (node.className.length) {
22719             
22720             var cn = node.className.split(/\W+/);
22721             var cna = [];
22722             Roo.each(cn, function(cls) {
22723                 if (cls.match(/Mso[a-zA-Z]+/)) {
22724                     return;
22725                 }
22726                 cna.push(cls);
22727             });
22728             node.className = cna.length ? cna.join(' ') : '';
22729             if (!cna.length) {
22730                 node.removeAttribute("class");
22731             }
22732         }
22733         
22734         if (node.hasAttribute("lang")) {
22735             node.removeAttribute("lang");
22736         }
22737         
22738         if (node.hasAttribute("style")) {
22739             
22740             var styles = node.getAttribute("style").split(";");
22741             var nstyle = [];
22742             Roo.each(styles, function(s) {
22743                 if (!s.match(/:/)) {
22744                     return;
22745                 }
22746                 var kv = s.split(":");
22747                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22748                     return;
22749                 }
22750                 // what ever is left... we allow.
22751                 nstyle.push(s);
22752             });
22753             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22754             if (!nstyle.length) {
22755                 node.removeAttribute('style');
22756             }
22757         }
22758         this.iterateChildren(node, this.cleanWord);
22759         
22760         
22761         
22762     },
22763     /**
22764      * iterateChildren of a Node, calling fn each time, using this as the scole..
22765      * @param {DomNode} node node to iterate children of.
22766      * @param {Function} fn method of this class to call on each item.
22767      */
22768     iterateChildren : function(node, fn)
22769     {
22770         if (!node.childNodes.length) {
22771                 return;
22772         }
22773         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22774            fn.call(this, node.childNodes[i])
22775         }
22776     },
22777     
22778     
22779     /**
22780      * cleanTableWidths.
22781      *
22782      * Quite often pasting from word etc.. results in tables with column and widths.
22783      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22784      *
22785      */
22786     cleanTableWidths : function(node)
22787     {
22788          
22789          
22790         if (!node) {
22791             this.cleanTableWidths(this.doc.body);
22792             return;
22793         }
22794         
22795         // ignore list...
22796         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22797             return; 
22798         }
22799         Roo.log(node.tagName);
22800         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22801             this.iterateChildren(node, this.cleanTableWidths);
22802             return;
22803         }
22804         if (node.hasAttribute('width')) {
22805             node.removeAttribute('width');
22806         }
22807         
22808          
22809         if (node.hasAttribute("style")) {
22810             // pretty basic...
22811             
22812             var styles = node.getAttribute("style").split(";");
22813             var nstyle = [];
22814             Roo.each(styles, function(s) {
22815                 if (!s.match(/:/)) {
22816                     return;
22817                 }
22818                 var kv = s.split(":");
22819                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22820                     return;
22821                 }
22822                 // what ever is left... we allow.
22823                 nstyle.push(s);
22824             });
22825             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22826             if (!nstyle.length) {
22827                 node.removeAttribute('style');
22828             }
22829         }
22830         
22831         this.iterateChildren(node, this.cleanTableWidths);
22832         
22833         
22834     },
22835     
22836     
22837     
22838     
22839     domToHTML : function(currentElement, depth, nopadtext) {
22840         
22841         depth = depth || 0;
22842         nopadtext = nopadtext || false;
22843     
22844         if (!currentElement) {
22845             return this.domToHTML(this.doc.body);
22846         }
22847         
22848         //Roo.log(currentElement);
22849         var j;
22850         var allText = false;
22851         var nodeName = currentElement.nodeName;
22852         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22853         
22854         if  (nodeName == '#text') {
22855             
22856             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22857         }
22858         
22859         
22860         var ret = '';
22861         if (nodeName != 'BODY') {
22862              
22863             var i = 0;
22864             // Prints the node tagName, such as <A>, <IMG>, etc
22865             if (tagName) {
22866                 var attr = [];
22867                 for(i = 0; i < currentElement.attributes.length;i++) {
22868                     // quoting?
22869                     var aname = currentElement.attributes.item(i).name;
22870                     if (!currentElement.attributes.item(i).value.length) {
22871                         continue;
22872                     }
22873                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22874                 }
22875                 
22876                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22877             } 
22878             else {
22879                 
22880                 // eack
22881             }
22882         } else {
22883             tagName = false;
22884         }
22885         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22886             return ret;
22887         }
22888         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22889             nopadtext = true;
22890         }
22891         
22892         
22893         // Traverse the tree
22894         i = 0;
22895         var currentElementChild = currentElement.childNodes.item(i);
22896         var allText = true;
22897         var innerHTML  = '';
22898         lastnode = '';
22899         while (currentElementChild) {
22900             // Formatting code (indent the tree so it looks nice on the screen)
22901             var nopad = nopadtext;
22902             if (lastnode == 'SPAN') {
22903                 nopad  = true;
22904             }
22905             // text
22906             if  (currentElementChild.nodeName == '#text') {
22907                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22908                 toadd = nopadtext ? toadd : toadd.trim();
22909                 if (!nopad && toadd.length > 80) {
22910                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22911                 }
22912                 innerHTML  += toadd;
22913                 
22914                 i++;
22915                 currentElementChild = currentElement.childNodes.item(i);
22916                 lastNode = '';
22917                 continue;
22918             }
22919             allText = false;
22920             
22921             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22922                 
22923             // Recursively traverse the tree structure of the child node
22924             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22925             lastnode = currentElementChild.nodeName;
22926             i++;
22927             currentElementChild=currentElement.childNodes.item(i);
22928         }
22929         
22930         ret += innerHTML;
22931         
22932         if (!allText) {
22933                 // The remaining code is mostly for formatting the tree
22934             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22935         }
22936         
22937         
22938         if (tagName) {
22939             ret+= "</"+tagName+">";
22940         }
22941         return ret;
22942         
22943     },
22944         
22945     applyBlacklists : function()
22946     {
22947         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22948         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22949         
22950         this.white = [];
22951         this.black = [];
22952         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22953             if (b.indexOf(tag) > -1) {
22954                 return;
22955             }
22956             this.white.push(tag);
22957             
22958         }, this);
22959         
22960         Roo.each(w, function(tag) {
22961             if (b.indexOf(tag) > -1) {
22962                 return;
22963             }
22964             if (this.white.indexOf(tag) > -1) {
22965                 return;
22966             }
22967             this.white.push(tag);
22968             
22969         }, this);
22970         
22971         
22972         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22973             if (w.indexOf(tag) > -1) {
22974                 return;
22975             }
22976             this.black.push(tag);
22977             
22978         }, this);
22979         
22980         Roo.each(b, function(tag) {
22981             if (w.indexOf(tag) > -1) {
22982                 return;
22983             }
22984             if (this.black.indexOf(tag) > -1) {
22985                 return;
22986             }
22987             this.black.push(tag);
22988             
22989         }, this);
22990         
22991         
22992         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22993         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22994         
22995         this.cwhite = [];
22996         this.cblack = [];
22997         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22998             if (b.indexOf(tag) > -1) {
22999                 return;
23000             }
23001             this.cwhite.push(tag);
23002             
23003         }, this);
23004         
23005         Roo.each(w, function(tag) {
23006             if (b.indexOf(tag) > -1) {
23007                 return;
23008             }
23009             if (this.cwhite.indexOf(tag) > -1) {
23010                 return;
23011             }
23012             this.cwhite.push(tag);
23013             
23014         }, this);
23015         
23016         
23017         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23018             if (w.indexOf(tag) > -1) {
23019                 return;
23020             }
23021             this.cblack.push(tag);
23022             
23023         }, this);
23024         
23025         Roo.each(b, function(tag) {
23026             if (w.indexOf(tag) > -1) {
23027                 return;
23028             }
23029             if (this.cblack.indexOf(tag) > -1) {
23030                 return;
23031             }
23032             this.cblack.push(tag);
23033             
23034         }, this);
23035     },
23036     
23037     setStylesheets : function(stylesheets)
23038     {
23039         if(typeof(stylesheets) == 'string'){
23040             Roo.get(this.iframe.contentDocument.head).createChild({
23041                 tag : 'link',
23042                 rel : 'stylesheet',
23043                 type : 'text/css',
23044                 href : stylesheets
23045             });
23046             
23047             return;
23048         }
23049         var _this = this;
23050      
23051         Roo.each(stylesheets, function(s) {
23052             if(!s.length){
23053                 return;
23054             }
23055             
23056             Roo.get(_this.iframe.contentDocument.head).createChild({
23057                 tag : 'link',
23058                 rel : 'stylesheet',
23059                 type : 'text/css',
23060                 href : s
23061             });
23062         });
23063
23064         
23065     },
23066     
23067     removeStylesheets : function()
23068     {
23069         var _this = this;
23070         
23071         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23072             s.remove();
23073         });
23074     },
23075     
23076     setStyle : function(style)
23077     {
23078         Roo.get(this.iframe.contentDocument.head).createChild({
23079             tag : 'style',
23080             type : 'text/css',
23081             html : style
23082         });
23083
23084         return;
23085     }
23086     
23087     // hide stuff that is not compatible
23088     /**
23089      * @event blur
23090      * @hide
23091      */
23092     /**
23093      * @event change
23094      * @hide
23095      */
23096     /**
23097      * @event focus
23098      * @hide
23099      */
23100     /**
23101      * @event specialkey
23102      * @hide
23103      */
23104     /**
23105      * @cfg {String} fieldClass @hide
23106      */
23107     /**
23108      * @cfg {String} focusClass @hide
23109      */
23110     /**
23111      * @cfg {String} autoCreate @hide
23112      */
23113     /**
23114      * @cfg {String} inputType @hide
23115      */
23116     /**
23117      * @cfg {String} invalidClass @hide
23118      */
23119     /**
23120      * @cfg {String} invalidText @hide
23121      */
23122     /**
23123      * @cfg {String} msgFx @hide
23124      */
23125     /**
23126      * @cfg {String} validateOnBlur @hide
23127      */
23128 });
23129
23130 Roo.HtmlEditorCore.white = [
23131         'area', 'br', 'img', 'input', 'hr', 'wbr',
23132         
23133        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23134        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23135        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23136        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23137        'table',   'ul',         'xmp', 
23138        
23139        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23140       'thead',   'tr', 
23141      
23142       'dir', 'menu', 'ol', 'ul', 'dl',
23143        
23144       'embed',  'object'
23145 ];
23146
23147
23148 Roo.HtmlEditorCore.black = [
23149     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23150         'applet', // 
23151         'base',   'basefont', 'bgsound', 'blink',  'body', 
23152         'frame',  'frameset', 'head',    'html',   'ilayer', 
23153         'iframe', 'layer',  'link',     'meta',    'object',   
23154         'script', 'style' ,'title',  'xml' // clean later..
23155 ];
23156 Roo.HtmlEditorCore.clean = [
23157     'script', 'style', 'title', 'xml'
23158 ];
23159 Roo.HtmlEditorCore.remove = [
23160     'font'
23161 ];
23162 // attributes..
23163
23164 Roo.HtmlEditorCore.ablack = [
23165     'on'
23166 ];
23167     
23168 Roo.HtmlEditorCore.aclean = [ 
23169     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23170 ];
23171
23172 // protocols..
23173 Roo.HtmlEditorCore.pwhite= [
23174         'http',  'https',  'mailto'
23175 ];
23176
23177 // white listed style attributes.
23178 Roo.HtmlEditorCore.cwhite= [
23179       //  'text-align', /// default is to allow most things..
23180       
23181          
23182 //        'font-size'//??
23183 ];
23184
23185 // black listed style attributes.
23186 Roo.HtmlEditorCore.cblack= [
23187       //  'font-size' -- this can be set by the project 
23188 ];
23189
23190
23191 Roo.HtmlEditorCore.swapCodes   =[ 
23192     [    8211, "--" ], 
23193     [    8212, "--" ], 
23194     [    8216,  "'" ],  
23195     [    8217, "'" ],  
23196     [    8220, '"' ],  
23197     [    8221, '"' ],  
23198     [    8226, "*" ],  
23199     [    8230, "..." ]
23200 ]; 
23201
23202     /*
23203  * - LGPL
23204  *
23205  * HtmlEditor
23206  * 
23207  */
23208
23209 /**
23210  * @class Roo.bootstrap.HtmlEditor
23211  * @extends Roo.bootstrap.TextArea
23212  * Bootstrap HtmlEditor class
23213
23214  * @constructor
23215  * Create a new HtmlEditor
23216  * @param {Object} config The config object
23217  */
23218
23219 Roo.bootstrap.HtmlEditor = function(config){
23220     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23221     if (!this.toolbars) {
23222         this.toolbars = [];
23223     }
23224     
23225     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23226     this.addEvents({
23227             /**
23228              * @event initialize
23229              * Fires when the editor is fully initialized (including the iframe)
23230              * @param {HtmlEditor} this
23231              */
23232             initialize: true,
23233             /**
23234              * @event activate
23235              * Fires when the editor is first receives the focus. Any insertion must wait
23236              * until after this event.
23237              * @param {HtmlEditor} this
23238              */
23239             activate: true,
23240              /**
23241              * @event beforesync
23242              * Fires before the textarea is updated with content from the editor iframe. Return false
23243              * to cancel the sync.
23244              * @param {HtmlEditor} this
23245              * @param {String} html
23246              */
23247             beforesync: true,
23248              /**
23249              * @event beforepush
23250              * Fires before the iframe editor is updated with content from the textarea. Return false
23251              * to cancel the push.
23252              * @param {HtmlEditor} this
23253              * @param {String} html
23254              */
23255             beforepush: true,
23256              /**
23257              * @event sync
23258              * Fires when the textarea is updated with content from the editor iframe.
23259              * @param {HtmlEditor} this
23260              * @param {String} html
23261              */
23262             sync: true,
23263              /**
23264              * @event push
23265              * Fires when the iframe editor is updated with content from the textarea.
23266              * @param {HtmlEditor} this
23267              * @param {String} html
23268              */
23269             push: true,
23270              /**
23271              * @event editmodechange
23272              * Fires when the editor switches edit modes
23273              * @param {HtmlEditor} this
23274              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23275              */
23276             editmodechange: true,
23277             /**
23278              * @event editorevent
23279              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23280              * @param {HtmlEditor} this
23281              */
23282             editorevent: true,
23283             /**
23284              * @event firstfocus
23285              * Fires when on first focus - needed by toolbars..
23286              * @param {HtmlEditor} this
23287              */
23288             firstfocus: true,
23289             /**
23290              * @event autosave
23291              * Auto save the htmlEditor value as a file into Events
23292              * @param {HtmlEditor} this
23293              */
23294             autosave: true,
23295             /**
23296              * @event savedpreview
23297              * preview the saved version of htmlEditor
23298              * @param {HtmlEditor} this
23299              */
23300             savedpreview: true
23301         });
23302 };
23303
23304
23305 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23306     
23307     
23308       /**
23309      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23310      */
23311     toolbars : false,
23312     
23313      /**
23314     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23315     */
23316     btns : [],
23317    
23318      /**
23319      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23320      *                        Roo.resizable.
23321      */
23322     resizable : false,
23323      /**
23324      * @cfg {Number} height (in pixels)
23325      */   
23326     height: 300,
23327    /**
23328      * @cfg {Number} width (in pixels)
23329      */   
23330     width: false,
23331     
23332     /**
23333      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23334      * 
23335      */
23336     stylesheets: false,
23337     
23338     // id of frame..
23339     frameId: false,
23340     
23341     // private properties
23342     validationEvent : false,
23343     deferHeight: true,
23344     initialized : false,
23345     activated : false,
23346     
23347     onFocus : Roo.emptyFn,
23348     iframePad:3,
23349     hideMode:'offsets',
23350     
23351     tbContainer : false,
23352     
23353     bodyCls : '',
23354     
23355     toolbarContainer :function() {
23356         return this.wrap.select('.x-html-editor-tb',true).first();
23357     },
23358
23359     /**
23360      * Protected method that will not generally be called directly. It
23361      * is called when the editor creates its toolbar. Override this method if you need to
23362      * add custom toolbar buttons.
23363      * @param {HtmlEditor} editor
23364      */
23365     createToolbar : function(){
23366         Roo.log('renewing');
23367         Roo.log("create toolbars");
23368         
23369         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23370         this.toolbars[0].render(this.toolbarContainer());
23371         
23372         return;
23373         
23374 //        if (!editor.toolbars || !editor.toolbars.length) {
23375 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23376 //        }
23377 //        
23378 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23379 //            editor.toolbars[i] = Roo.factory(
23380 //                    typeof(editor.toolbars[i]) == 'string' ?
23381 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23382 //                Roo.bootstrap.HtmlEditor);
23383 //            editor.toolbars[i].init(editor);
23384 //        }
23385     },
23386
23387      
23388     // private
23389     onRender : function(ct, position)
23390     {
23391        // Roo.log("Call onRender: " + this.xtype);
23392         var _t = this;
23393         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23394       
23395         this.wrap = this.inputEl().wrap({
23396             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23397         });
23398         
23399         this.editorcore.onRender(ct, position);
23400          
23401         if (this.resizable) {
23402             this.resizeEl = new Roo.Resizable(this.wrap, {
23403                 pinned : true,
23404                 wrap: true,
23405                 dynamic : true,
23406                 minHeight : this.height,
23407                 height: this.height,
23408                 handles : this.resizable,
23409                 width: this.width,
23410                 listeners : {
23411                     resize : function(r, w, h) {
23412                         _t.onResize(w,h); // -something
23413                     }
23414                 }
23415             });
23416             
23417         }
23418         this.createToolbar(this);
23419        
23420         
23421         if(!this.width && this.resizable){
23422             this.setSize(this.wrap.getSize());
23423         }
23424         if (this.resizeEl) {
23425             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23426             // should trigger onReize..
23427         }
23428         
23429     },
23430
23431     // private
23432     onResize : function(w, h)
23433     {
23434         Roo.log('resize: ' +w + ',' + h );
23435         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23436         var ew = false;
23437         var eh = false;
23438         
23439         if(this.inputEl() ){
23440             if(typeof w == 'number'){
23441                 var aw = w - this.wrap.getFrameWidth('lr');
23442                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23443                 ew = aw;
23444             }
23445             if(typeof h == 'number'){
23446                  var tbh = -11;  // fixme it needs to tool bar size!
23447                 for (var i =0; i < this.toolbars.length;i++) {
23448                     // fixme - ask toolbars for heights?
23449                     tbh += this.toolbars[i].el.getHeight();
23450                     //if (this.toolbars[i].footer) {
23451                     //    tbh += this.toolbars[i].footer.el.getHeight();
23452                     //}
23453                 }
23454               
23455                 
23456                 
23457                 
23458                 
23459                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23460                 ah -= 5; // knock a few pixes off for look..
23461                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23462                 var eh = ah;
23463             }
23464         }
23465         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23466         this.editorcore.onResize(ew,eh);
23467         
23468     },
23469
23470     /**
23471      * Toggles the editor between standard and source edit mode.
23472      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23473      */
23474     toggleSourceEdit : function(sourceEditMode)
23475     {
23476         this.editorcore.toggleSourceEdit(sourceEditMode);
23477         
23478         if(this.editorcore.sourceEditMode){
23479             Roo.log('editor - showing textarea');
23480             
23481 //            Roo.log('in');
23482 //            Roo.log(this.syncValue());
23483             this.syncValue();
23484             this.inputEl().removeClass(['hide', 'x-hidden']);
23485             this.inputEl().dom.removeAttribute('tabIndex');
23486             this.inputEl().focus();
23487         }else{
23488             Roo.log('editor - hiding textarea');
23489 //            Roo.log('out')
23490 //            Roo.log(this.pushValue()); 
23491             this.pushValue();
23492             
23493             this.inputEl().addClass(['hide', 'x-hidden']);
23494             this.inputEl().dom.setAttribute('tabIndex', -1);
23495             //this.deferFocus();
23496         }
23497          
23498         if(this.resizable){
23499             this.setSize(this.wrap.getSize());
23500         }
23501         
23502         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23503     },
23504  
23505     // private (for BoxComponent)
23506     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23507
23508     // private (for BoxComponent)
23509     getResizeEl : function(){
23510         return this.wrap;
23511     },
23512
23513     // private (for BoxComponent)
23514     getPositionEl : function(){
23515         return this.wrap;
23516     },
23517
23518     // private
23519     initEvents : function(){
23520         this.originalValue = this.getValue();
23521     },
23522
23523 //    /**
23524 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23525 //     * @method
23526 //     */
23527 //    markInvalid : Roo.emptyFn,
23528 //    /**
23529 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23530 //     * @method
23531 //     */
23532 //    clearInvalid : Roo.emptyFn,
23533
23534     setValue : function(v){
23535         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23536         this.editorcore.pushValue();
23537     },
23538
23539      
23540     // private
23541     deferFocus : function(){
23542         this.focus.defer(10, this);
23543     },
23544
23545     // doc'ed in Field
23546     focus : function(){
23547         this.editorcore.focus();
23548         
23549     },
23550       
23551
23552     // private
23553     onDestroy : function(){
23554         
23555         
23556         
23557         if(this.rendered){
23558             
23559             for (var i =0; i < this.toolbars.length;i++) {
23560                 // fixme - ask toolbars for heights?
23561                 this.toolbars[i].onDestroy();
23562             }
23563             
23564             this.wrap.dom.innerHTML = '';
23565             this.wrap.remove();
23566         }
23567     },
23568
23569     // private
23570     onFirstFocus : function(){
23571         //Roo.log("onFirstFocus");
23572         this.editorcore.onFirstFocus();
23573          for (var i =0; i < this.toolbars.length;i++) {
23574             this.toolbars[i].onFirstFocus();
23575         }
23576         
23577     },
23578     
23579     // private
23580     syncValue : function()
23581     {   
23582         this.editorcore.syncValue();
23583     },
23584     
23585     pushValue : function()
23586     {   
23587         this.editorcore.pushValue();
23588     }
23589      
23590     
23591     // hide stuff that is not compatible
23592     /**
23593      * @event blur
23594      * @hide
23595      */
23596     /**
23597      * @event change
23598      * @hide
23599      */
23600     /**
23601      * @event focus
23602      * @hide
23603      */
23604     /**
23605      * @event specialkey
23606      * @hide
23607      */
23608     /**
23609      * @cfg {String} fieldClass @hide
23610      */
23611     /**
23612      * @cfg {String} focusClass @hide
23613      */
23614     /**
23615      * @cfg {String} autoCreate @hide
23616      */
23617     /**
23618      * @cfg {String} inputType @hide
23619      */
23620     /**
23621      * @cfg {String} invalidClass @hide
23622      */
23623     /**
23624      * @cfg {String} invalidText @hide
23625      */
23626     /**
23627      * @cfg {String} msgFx @hide
23628      */
23629     /**
23630      * @cfg {String} validateOnBlur @hide
23631      */
23632 });
23633  
23634     
23635    
23636    
23637    
23638       
23639 Roo.namespace('Roo.bootstrap.htmleditor');
23640 /**
23641  * @class Roo.bootstrap.HtmlEditorToolbar1
23642  * Basic Toolbar
23643  * 
23644  * Usage:
23645  *
23646  new Roo.bootstrap.HtmlEditor({
23647     ....
23648     toolbars : [
23649         new Roo.bootstrap.HtmlEditorToolbar1({
23650             disable : { fonts: 1 , format: 1, ..., ... , ...],
23651             btns : [ .... ]
23652         })
23653     }
23654      
23655  * 
23656  * @cfg {Object} disable List of elements to disable..
23657  * @cfg {Array} btns List of additional buttons.
23658  * 
23659  * 
23660  * NEEDS Extra CSS? 
23661  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23662  */
23663  
23664 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23665 {
23666     
23667     Roo.apply(this, config);
23668     
23669     // default disabled, based on 'good practice'..
23670     this.disable = this.disable || {};
23671     Roo.applyIf(this.disable, {
23672         fontSize : true,
23673         colors : true,
23674         specialElements : true
23675     });
23676     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23677     
23678     this.editor = config.editor;
23679     this.editorcore = config.editor.editorcore;
23680     
23681     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23682     
23683     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23684     // dont call parent... till later.
23685 }
23686 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23687      
23688     bar : true,
23689     
23690     editor : false,
23691     editorcore : false,
23692     
23693     
23694     formats : [
23695         "p" ,  
23696         "h1","h2","h3","h4","h5","h6", 
23697         "pre", "code", 
23698         "abbr", "acronym", "address", "cite", "samp", "var",
23699         'div','span'
23700     ],
23701     
23702     onRender : function(ct, position)
23703     {
23704        // Roo.log("Call onRender: " + this.xtype);
23705         
23706        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23707        Roo.log(this.el);
23708        this.el.dom.style.marginBottom = '0';
23709        var _this = this;
23710        var editorcore = this.editorcore;
23711        var editor= this.editor;
23712        
23713        var children = [];
23714        var btn = function(id,cmd , toggle, handler, html){
23715        
23716             var  event = toggle ? 'toggle' : 'click';
23717        
23718             var a = {
23719                 size : 'sm',
23720                 xtype: 'Button',
23721                 xns: Roo.bootstrap,
23722                 glyphicon : id,
23723                 cmd : id || cmd,
23724                 enableToggle:toggle !== false,
23725                 html : html || '',
23726                 pressed : toggle ? false : null,
23727                 listeners : {}
23728             };
23729             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23730                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23731             };
23732             children.push(a);
23733             return a;
23734        }
23735        
23736     //    var cb_box = function...
23737         
23738         var style = {
23739                 xtype: 'Button',
23740                 size : 'sm',
23741                 xns: Roo.bootstrap,
23742                 glyphicon : 'font',
23743                 //html : 'submit'
23744                 menu : {
23745                     xtype: 'Menu',
23746                     xns: Roo.bootstrap,
23747                     items:  []
23748                 }
23749         };
23750         Roo.each(this.formats, function(f) {
23751             style.menu.items.push({
23752                 xtype :'MenuItem',
23753                 xns: Roo.bootstrap,
23754                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23755                 tagname : f,
23756                 listeners : {
23757                     click : function()
23758                     {
23759                         editorcore.insertTag(this.tagname);
23760                         editor.focus();
23761                     }
23762                 }
23763                 
23764             });
23765         });
23766         children.push(style);   
23767         
23768         btn('bold',false,true);
23769         btn('italic',false,true);
23770         btn('align-left', 'justifyleft',true);
23771         btn('align-center', 'justifycenter',true);
23772         btn('align-right' , 'justifyright',true);
23773         btn('link', false, false, function(btn) {
23774             //Roo.log("create link?");
23775             var url = prompt(this.createLinkText, this.defaultLinkValue);
23776             if(url && url != 'http:/'+'/'){
23777                 this.editorcore.relayCmd('createlink', url);
23778             }
23779         }),
23780         btn('list','insertunorderedlist',true);
23781         btn('pencil', false,true, function(btn){
23782                 Roo.log(this);
23783                 this.toggleSourceEdit(btn.pressed);
23784         });
23785         
23786         if (this.editor.btns.length > 0) {
23787             for (var i = 0; i<this.editor.btns.length; i++) {
23788                 children.push(this.editor.btns[i]);
23789             }
23790         }
23791         
23792         /*
23793         var cog = {
23794                 xtype: 'Button',
23795                 size : 'sm',
23796                 xns: Roo.bootstrap,
23797                 glyphicon : 'cog',
23798                 //html : 'submit'
23799                 menu : {
23800                     xtype: 'Menu',
23801                     xns: Roo.bootstrap,
23802                     items:  []
23803                 }
23804         };
23805         
23806         cog.menu.items.push({
23807             xtype :'MenuItem',
23808             xns: Roo.bootstrap,
23809             html : Clean styles,
23810             tagname : f,
23811             listeners : {
23812                 click : function()
23813                 {
23814                     editorcore.insertTag(this.tagname);
23815                     editor.focus();
23816                 }
23817             }
23818             
23819         });
23820        */
23821         
23822          
23823        this.xtype = 'NavSimplebar';
23824         
23825         for(var i=0;i< children.length;i++) {
23826             
23827             this.buttons.add(this.addxtypeChild(children[i]));
23828             
23829         }
23830         
23831         editor.on('editorevent', this.updateToolbar, this);
23832     },
23833     onBtnClick : function(id)
23834     {
23835        this.editorcore.relayCmd(id);
23836        this.editorcore.focus();
23837     },
23838     
23839     /**
23840      * Protected method that will not generally be called directly. It triggers
23841      * a toolbar update by reading the markup state of the current selection in the editor.
23842      */
23843     updateToolbar: function(){
23844
23845         if(!this.editorcore.activated){
23846             this.editor.onFirstFocus(); // is this neeed?
23847             return;
23848         }
23849
23850         var btns = this.buttons; 
23851         var doc = this.editorcore.doc;
23852         btns.get('bold').setActive(doc.queryCommandState('bold'));
23853         btns.get('italic').setActive(doc.queryCommandState('italic'));
23854         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23855         
23856         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23857         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23858         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23859         
23860         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23861         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23862          /*
23863         
23864         var ans = this.editorcore.getAllAncestors();
23865         if (this.formatCombo) {
23866             
23867             
23868             var store = this.formatCombo.store;
23869             this.formatCombo.setValue("");
23870             for (var i =0; i < ans.length;i++) {
23871                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23872                     // select it..
23873                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23874                     break;
23875                 }
23876             }
23877         }
23878         
23879         
23880         
23881         // hides menus... - so this cant be on a menu...
23882         Roo.bootstrap.MenuMgr.hideAll();
23883         */
23884         Roo.bootstrap.MenuMgr.hideAll();
23885         //this.editorsyncValue();
23886     },
23887     onFirstFocus: function() {
23888         this.buttons.each(function(item){
23889            item.enable();
23890         });
23891     },
23892     toggleSourceEdit : function(sourceEditMode){
23893         
23894           
23895         if(sourceEditMode){
23896             Roo.log("disabling buttons");
23897            this.buttons.each( function(item){
23898                 if(item.cmd != 'pencil'){
23899                     item.disable();
23900                 }
23901             });
23902           
23903         }else{
23904             Roo.log("enabling buttons");
23905             if(this.editorcore.initialized){
23906                 this.buttons.each( function(item){
23907                     item.enable();
23908                 });
23909             }
23910             
23911         }
23912         Roo.log("calling toggole on editor");
23913         // tell the editor that it's been pressed..
23914         this.editor.toggleSourceEdit(sourceEditMode);
23915        
23916     }
23917 });
23918
23919
23920
23921
23922
23923 /**
23924  * @class Roo.bootstrap.Table.AbstractSelectionModel
23925  * @extends Roo.util.Observable
23926  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23927  * implemented by descendant classes.  This class should not be directly instantiated.
23928  * @constructor
23929  */
23930 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23931     this.locked = false;
23932     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23933 };
23934
23935
23936 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23937     /** @ignore Called by the grid automatically. Do not call directly. */
23938     init : function(grid){
23939         this.grid = grid;
23940         this.initEvents();
23941     },
23942
23943     /**
23944      * Locks the selections.
23945      */
23946     lock : function(){
23947         this.locked = true;
23948     },
23949
23950     /**
23951      * Unlocks the selections.
23952      */
23953     unlock : function(){
23954         this.locked = false;
23955     },
23956
23957     /**
23958      * Returns true if the selections are locked.
23959      * @return {Boolean}
23960      */
23961     isLocked : function(){
23962         return this.locked;
23963     }
23964 });
23965 /**
23966  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23967  * @class Roo.bootstrap.Table.RowSelectionModel
23968  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23969  * It supports multiple selections and keyboard selection/navigation. 
23970  * @constructor
23971  * @param {Object} config
23972  */
23973
23974 Roo.bootstrap.Table.RowSelectionModel = function(config){
23975     Roo.apply(this, config);
23976     this.selections = new Roo.util.MixedCollection(false, function(o){
23977         return o.id;
23978     });
23979
23980     this.last = false;
23981     this.lastActive = false;
23982
23983     this.addEvents({
23984         /**
23985              * @event selectionchange
23986              * Fires when the selection changes
23987              * @param {SelectionModel} this
23988              */
23989             "selectionchange" : true,
23990         /**
23991              * @event afterselectionchange
23992              * Fires after the selection changes (eg. by key press or clicking)
23993              * @param {SelectionModel} this
23994              */
23995             "afterselectionchange" : true,
23996         /**
23997              * @event beforerowselect
23998              * Fires when a row is selected being selected, return false to cancel.
23999              * @param {SelectionModel} this
24000              * @param {Number} rowIndex The selected index
24001              * @param {Boolean} keepExisting False if other selections will be cleared
24002              */
24003             "beforerowselect" : true,
24004         /**
24005              * @event rowselect
24006              * Fires when a row is selected.
24007              * @param {SelectionModel} this
24008              * @param {Number} rowIndex The selected index
24009              * @param {Roo.data.Record} r The record
24010              */
24011             "rowselect" : true,
24012         /**
24013              * @event rowdeselect
24014              * Fires when a row is deselected.
24015              * @param {SelectionModel} this
24016              * @param {Number} rowIndex The selected index
24017              */
24018         "rowdeselect" : true
24019     });
24020     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24021     this.locked = false;
24022  };
24023
24024 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24025     /**
24026      * @cfg {Boolean} singleSelect
24027      * True to allow selection of only one row at a time (defaults to false)
24028      */
24029     singleSelect : false,
24030
24031     // private
24032     initEvents : function()
24033     {
24034
24035         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24036         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24037         //}else{ // allow click to work like normal
24038          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24039         //}
24040         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24041         this.grid.on("rowclick", this.handleMouseDown, this);
24042         
24043         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24044             "up" : function(e){
24045                 if(!e.shiftKey){
24046                     this.selectPrevious(e.shiftKey);
24047                 }else if(this.last !== false && this.lastActive !== false){
24048                     var last = this.last;
24049                     this.selectRange(this.last,  this.lastActive-1);
24050                     this.grid.getView().focusRow(this.lastActive);
24051                     if(last !== false){
24052                         this.last = last;
24053                     }
24054                 }else{
24055                     this.selectFirstRow();
24056                 }
24057                 this.fireEvent("afterselectionchange", this);
24058             },
24059             "down" : function(e){
24060                 if(!e.shiftKey){
24061                     this.selectNext(e.shiftKey);
24062                 }else if(this.last !== false && this.lastActive !== false){
24063                     var last = this.last;
24064                     this.selectRange(this.last,  this.lastActive+1);
24065                     this.grid.getView().focusRow(this.lastActive);
24066                     if(last !== false){
24067                         this.last = last;
24068                     }
24069                 }else{
24070                     this.selectFirstRow();
24071                 }
24072                 this.fireEvent("afterselectionchange", this);
24073             },
24074             scope: this
24075         });
24076         this.grid.store.on('load', function(){
24077             this.selections.clear();
24078         },this);
24079         /*
24080         var view = this.grid.view;
24081         view.on("refresh", this.onRefresh, this);
24082         view.on("rowupdated", this.onRowUpdated, this);
24083         view.on("rowremoved", this.onRemove, this);
24084         */
24085     },
24086
24087     // private
24088     onRefresh : function()
24089     {
24090         var ds = this.grid.store, i, v = this.grid.view;
24091         var s = this.selections;
24092         s.each(function(r){
24093             if((i = ds.indexOfId(r.id)) != -1){
24094                 v.onRowSelect(i);
24095             }else{
24096                 s.remove(r);
24097             }
24098         });
24099     },
24100
24101     // private
24102     onRemove : function(v, index, r){
24103         this.selections.remove(r);
24104     },
24105
24106     // private
24107     onRowUpdated : function(v, index, r){
24108         if(this.isSelected(r)){
24109             v.onRowSelect(index);
24110         }
24111     },
24112
24113     /**
24114      * Select records.
24115      * @param {Array} records The records to select
24116      * @param {Boolean} keepExisting (optional) True to keep existing selections
24117      */
24118     selectRecords : function(records, keepExisting)
24119     {
24120         if(!keepExisting){
24121             this.clearSelections();
24122         }
24123             var ds = this.grid.store;
24124         for(var i = 0, len = records.length; i < len; i++){
24125             this.selectRow(ds.indexOf(records[i]), true);
24126         }
24127     },
24128
24129     /**
24130      * Gets the number of selected rows.
24131      * @return {Number}
24132      */
24133     getCount : function(){
24134         return this.selections.length;
24135     },
24136
24137     /**
24138      * Selects the first row in the grid.
24139      */
24140     selectFirstRow : function(){
24141         this.selectRow(0);
24142     },
24143
24144     /**
24145      * Select the last row.
24146      * @param {Boolean} keepExisting (optional) True to keep existing selections
24147      */
24148     selectLastRow : function(keepExisting){
24149         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24150         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24151     },
24152
24153     /**
24154      * Selects the row immediately following the last selected row.
24155      * @param {Boolean} keepExisting (optional) True to keep existing selections
24156      */
24157     selectNext : function(keepExisting)
24158     {
24159             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24160             this.selectRow(this.last+1, keepExisting);
24161             this.grid.getView().focusRow(this.last);
24162         }
24163     },
24164
24165     /**
24166      * Selects the row that precedes the last selected row.
24167      * @param {Boolean} keepExisting (optional) True to keep existing selections
24168      */
24169     selectPrevious : function(keepExisting){
24170         if(this.last){
24171             this.selectRow(this.last-1, keepExisting);
24172             this.grid.getView().focusRow(this.last);
24173         }
24174     },
24175
24176     /**
24177      * Returns the selected records
24178      * @return {Array} Array of selected records
24179      */
24180     getSelections : function(){
24181         return [].concat(this.selections.items);
24182     },
24183
24184     /**
24185      * Returns the first selected record.
24186      * @return {Record}
24187      */
24188     getSelected : function(){
24189         return this.selections.itemAt(0);
24190     },
24191
24192
24193     /**
24194      * Clears all selections.
24195      */
24196     clearSelections : function(fast)
24197     {
24198         if(this.locked) {
24199             return;
24200         }
24201         if(fast !== true){
24202                 var ds = this.grid.store;
24203             var s = this.selections;
24204             s.each(function(r){
24205                 this.deselectRow(ds.indexOfId(r.id));
24206             }, this);
24207             s.clear();
24208         }else{
24209             this.selections.clear();
24210         }
24211         this.last = false;
24212     },
24213
24214
24215     /**
24216      * Selects all rows.
24217      */
24218     selectAll : function(){
24219         if(this.locked) {
24220             return;
24221         }
24222         this.selections.clear();
24223         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24224             this.selectRow(i, true);
24225         }
24226     },
24227
24228     /**
24229      * Returns True if there is a selection.
24230      * @return {Boolean}
24231      */
24232     hasSelection : function(){
24233         return this.selections.length > 0;
24234     },
24235
24236     /**
24237      * Returns True if the specified row is selected.
24238      * @param {Number/Record} record The record or index of the record to check
24239      * @return {Boolean}
24240      */
24241     isSelected : function(index){
24242             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24243         return (r && this.selections.key(r.id) ? true : false);
24244     },
24245
24246     /**
24247      * Returns True if the specified record id is selected.
24248      * @param {String} id The id of record to check
24249      * @return {Boolean}
24250      */
24251     isIdSelected : function(id){
24252         return (this.selections.key(id) ? true : false);
24253     },
24254
24255
24256     // private
24257     handleMouseDBClick : function(e, t){
24258         
24259     },
24260     // private
24261     handleMouseDown : function(e, t)
24262     {
24263             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24264         if(this.isLocked() || rowIndex < 0 ){
24265             return;
24266         };
24267         if(e.shiftKey && this.last !== false){
24268             var last = this.last;
24269             this.selectRange(last, rowIndex, e.ctrlKey);
24270             this.last = last; // reset the last
24271             t.focus();
24272     
24273         }else{
24274             var isSelected = this.isSelected(rowIndex);
24275             //Roo.log("select row:" + rowIndex);
24276             if(isSelected){
24277                 this.deselectRow(rowIndex);
24278             } else {
24279                         this.selectRow(rowIndex, true);
24280             }
24281     
24282             /*
24283                 if(e.button !== 0 && isSelected){
24284                 alert('rowIndex 2: ' + rowIndex);
24285                     view.focusRow(rowIndex);
24286                 }else if(e.ctrlKey && isSelected){
24287                     this.deselectRow(rowIndex);
24288                 }else if(!isSelected){
24289                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24290                     view.focusRow(rowIndex);
24291                 }
24292             */
24293         }
24294         this.fireEvent("afterselectionchange", this);
24295     },
24296     // private
24297     handleDragableRowClick :  function(grid, rowIndex, e) 
24298     {
24299         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24300             this.selectRow(rowIndex, false);
24301             grid.view.focusRow(rowIndex);
24302              this.fireEvent("afterselectionchange", this);
24303         }
24304     },
24305     
24306     /**
24307      * Selects multiple rows.
24308      * @param {Array} rows Array of the indexes of the row to select
24309      * @param {Boolean} keepExisting (optional) True to keep existing selections
24310      */
24311     selectRows : function(rows, keepExisting){
24312         if(!keepExisting){
24313             this.clearSelections();
24314         }
24315         for(var i = 0, len = rows.length; i < len; i++){
24316             this.selectRow(rows[i], true);
24317         }
24318     },
24319
24320     /**
24321      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24322      * @param {Number} startRow The index of the first row in the range
24323      * @param {Number} endRow The index of the last row in the range
24324      * @param {Boolean} keepExisting (optional) True to retain existing selections
24325      */
24326     selectRange : function(startRow, endRow, keepExisting){
24327         if(this.locked) {
24328             return;
24329         }
24330         if(!keepExisting){
24331             this.clearSelections();
24332         }
24333         if(startRow <= endRow){
24334             for(var i = startRow; i <= endRow; i++){
24335                 this.selectRow(i, true);
24336             }
24337         }else{
24338             for(var i = startRow; i >= endRow; i--){
24339                 this.selectRow(i, true);
24340             }
24341         }
24342     },
24343
24344     /**
24345      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24346      * @param {Number} startRow The index of the first row in the range
24347      * @param {Number} endRow The index of the last row in the range
24348      */
24349     deselectRange : function(startRow, endRow, preventViewNotify){
24350         if(this.locked) {
24351             return;
24352         }
24353         for(var i = startRow; i <= endRow; i++){
24354             this.deselectRow(i, preventViewNotify);
24355         }
24356     },
24357
24358     /**
24359      * Selects a row.
24360      * @param {Number} row The index of the row to select
24361      * @param {Boolean} keepExisting (optional) True to keep existing selections
24362      */
24363     selectRow : function(index, keepExisting, preventViewNotify)
24364     {
24365             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24366             return;
24367         }
24368         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24369             if(!keepExisting || this.singleSelect){
24370                 this.clearSelections();
24371             }
24372             
24373             var r = this.grid.store.getAt(index);
24374             //console.log('selectRow - record id :' + r.id);
24375             
24376             this.selections.add(r);
24377             this.last = this.lastActive = index;
24378             if(!preventViewNotify){
24379                 var proxy = new Roo.Element(
24380                                 this.grid.getRowDom(index)
24381                 );
24382                 proxy.addClass('bg-info info');
24383             }
24384             this.fireEvent("rowselect", this, index, r);
24385             this.fireEvent("selectionchange", this);
24386         }
24387     },
24388
24389     /**
24390      * Deselects a row.
24391      * @param {Number} row The index of the row to deselect
24392      */
24393     deselectRow : function(index, preventViewNotify)
24394     {
24395         if(this.locked) {
24396             return;
24397         }
24398         if(this.last == index){
24399             this.last = false;
24400         }
24401         if(this.lastActive == index){
24402             this.lastActive = false;
24403         }
24404         
24405         var r = this.grid.store.getAt(index);
24406         if (!r) {
24407             return;
24408         }
24409         
24410         this.selections.remove(r);
24411         //.console.log('deselectRow - record id :' + r.id);
24412         if(!preventViewNotify){
24413         
24414             var proxy = new Roo.Element(
24415                 this.grid.getRowDom(index)
24416             );
24417             proxy.removeClass('bg-info info');
24418         }
24419         this.fireEvent("rowdeselect", this, index);
24420         this.fireEvent("selectionchange", this);
24421     },
24422
24423     // private
24424     restoreLast : function(){
24425         if(this._last){
24426             this.last = this._last;
24427         }
24428     },
24429
24430     // private
24431     acceptsNav : function(row, col, cm){
24432         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24433     },
24434
24435     // private
24436     onEditorKey : function(field, e){
24437         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24438         if(k == e.TAB){
24439             e.stopEvent();
24440             ed.completeEdit();
24441             if(e.shiftKey){
24442                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24443             }else{
24444                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24445             }
24446         }else if(k == e.ENTER && !e.ctrlKey){
24447             e.stopEvent();
24448             ed.completeEdit();
24449             if(e.shiftKey){
24450                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24451             }else{
24452                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24453             }
24454         }else if(k == e.ESC){
24455             ed.cancelEdit();
24456         }
24457         if(newCell){
24458             g.startEditing(newCell[0], newCell[1]);
24459         }
24460     }
24461 });
24462 /*
24463  * Based on:
24464  * Ext JS Library 1.1.1
24465  * Copyright(c) 2006-2007, Ext JS, LLC.
24466  *
24467  * Originally Released Under LGPL - original licence link has changed is not relivant.
24468  *
24469  * Fork - LGPL
24470  * <script type="text/javascript">
24471  */
24472  
24473 /**
24474  * @class Roo.bootstrap.PagingToolbar
24475  * @extends Roo.bootstrap.NavSimplebar
24476  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24477  * @constructor
24478  * Create a new PagingToolbar
24479  * @param {Object} config The config object
24480  * @param {Roo.data.Store} store
24481  */
24482 Roo.bootstrap.PagingToolbar = function(config)
24483 {
24484     // old args format still supported... - xtype is prefered..
24485         // created from xtype...
24486     
24487     this.ds = config.dataSource;
24488     
24489     if (config.store && !this.ds) {
24490         this.store= Roo.factory(config.store, Roo.data);
24491         this.ds = this.store;
24492         this.ds.xmodule = this.xmodule || false;
24493     }
24494     
24495     this.toolbarItems = [];
24496     if (config.items) {
24497         this.toolbarItems = config.items;
24498     }
24499     
24500     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24501     
24502     this.cursor = 0;
24503     
24504     if (this.ds) { 
24505         this.bind(this.ds);
24506     }
24507     
24508     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24509     
24510 };
24511
24512 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24513     /**
24514      * @cfg {Roo.data.Store} dataSource
24515      * The underlying data store providing the paged data
24516      */
24517     /**
24518      * @cfg {String/HTMLElement/Element} container
24519      * container The id or element that will contain the toolbar
24520      */
24521     /**
24522      * @cfg {Boolean} displayInfo
24523      * True to display the displayMsg (defaults to false)
24524      */
24525     /**
24526      * @cfg {Number} pageSize
24527      * The number of records to display per page (defaults to 20)
24528      */
24529     pageSize: 20,
24530     /**
24531      * @cfg {String} displayMsg
24532      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24533      */
24534     displayMsg : 'Displaying {0} - {1} of {2}',
24535     /**
24536      * @cfg {String} emptyMsg
24537      * The message to display when no records are found (defaults to "No data to display")
24538      */
24539     emptyMsg : 'No data to display',
24540     /**
24541      * Customizable piece of the default paging text (defaults to "Page")
24542      * @type String
24543      */
24544     beforePageText : "Page",
24545     /**
24546      * Customizable piece of the default paging text (defaults to "of %0")
24547      * @type String
24548      */
24549     afterPageText : "of {0}",
24550     /**
24551      * Customizable piece of the default paging text (defaults to "First Page")
24552      * @type String
24553      */
24554     firstText : "First Page",
24555     /**
24556      * Customizable piece of the default paging text (defaults to "Previous Page")
24557      * @type String
24558      */
24559     prevText : "Previous Page",
24560     /**
24561      * Customizable piece of the default paging text (defaults to "Next Page")
24562      * @type String
24563      */
24564     nextText : "Next Page",
24565     /**
24566      * Customizable piece of the default paging text (defaults to "Last Page")
24567      * @type String
24568      */
24569     lastText : "Last Page",
24570     /**
24571      * Customizable piece of the default paging text (defaults to "Refresh")
24572      * @type String
24573      */
24574     refreshText : "Refresh",
24575
24576     buttons : false,
24577     // private
24578     onRender : function(ct, position) 
24579     {
24580         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24581         this.navgroup.parentId = this.id;
24582         this.navgroup.onRender(this.el, null);
24583         // add the buttons to the navgroup
24584         
24585         if(this.displayInfo){
24586             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24587             this.displayEl = this.el.select('.x-paging-info', true).first();
24588 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24589 //            this.displayEl = navel.el.select('span',true).first();
24590         }
24591         
24592         var _this = this;
24593         
24594         if(this.buttons){
24595             Roo.each(_this.buttons, function(e){ // this might need to use render????
24596                Roo.factory(e).render(_this.el);
24597             });
24598         }
24599             
24600         Roo.each(_this.toolbarItems, function(e) {
24601             _this.navgroup.addItem(e);
24602         });
24603         
24604         
24605         this.first = this.navgroup.addItem({
24606             tooltip: this.firstText,
24607             cls: "prev",
24608             icon : 'fa fa-backward',
24609             disabled: true,
24610             preventDefault: true,
24611             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24612         });
24613         
24614         this.prev =  this.navgroup.addItem({
24615             tooltip: this.prevText,
24616             cls: "prev",
24617             icon : 'fa fa-step-backward',
24618             disabled: true,
24619             preventDefault: true,
24620             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24621         });
24622     //this.addSeparator();
24623         
24624         
24625         var field = this.navgroup.addItem( {
24626             tagtype : 'span',
24627             cls : 'x-paging-position',
24628             
24629             html : this.beforePageText  +
24630                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24631                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24632          } ); //?? escaped?
24633         
24634         this.field = field.el.select('input', true).first();
24635         this.field.on("keydown", this.onPagingKeydown, this);
24636         this.field.on("focus", function(){this.dom.select();});
24637     
24638     
24639         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24640         //this.field.setHeight(18);
24641         //this.addSeparator();
24642         this.next = this.navgroup.addItem({
24643             tooltip: this.nextText,
24644             cls: "next",
24645             html : ' <i class="fa fa-step-forward">',
24646             disabled: true,
24647             preventDefault: true,
24648             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24649         });
24650         this.last = this.navgroup.addItem({
24651             tooltip: this.lastText,
24652             icon : 'fa fa-forward',
24653             cls: "next",
24654             disabled: true,
24655             preventDefault: true,
24656             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24657         });
24658     //this.addSeparator();
24659         this.loading = this.navgroup.addItem({
24660             tooltip: this.refreshText,
24661             icon: 'fa fa-refresh',
24662             preventDefault: true,
24663             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24664         });
24665         
24666     },
24667
24668     // private
24669     updateInfo : function(){
24670         if(this.displayEl){
24671             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24672             var msg = count == 0 ?
24673                 this.emptyMsg :
24674                 String.format(
24675                     this.displayMsg,
24676                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24677                 );
24678             this.displayEl.update(msg);
24679         }
24680     },
24681
24682     // private
24683     onLoad : function(ds, r, o)
24684     {
24685         this.cursor = o.params.start ? o.params.start : 0;
24686         
24687         var d = this.getPageData(),
24688             ap = d.activePage,
24689             ps = d.pages;
24690         
24691         
24692         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24693         this.field.dom.value = ap;
24694         this.first.setDisabled(ap == 1);
24695         this.prev.setDisabled(ap == 1);
24696         this.next.setDisabled(ap == ps);
24697         this.last.setDisabled(ap == ps);
24698         this.loading.enable();
24699         this.updateInfo();
24700     },
24701
24702     // private
24703     getPageData : function(){
24704         var total = this.ds.getTotalCount();
24705         return {
24706             total : total,
24707             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24708             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24709         };
24710     },
24711
24712     // private
24713     onLoadError : function(){
24714         this.loading.enable();
24715     },
24716
24717     // private
24718     onPagingKeydown : function(e){
24719         var k = e.getKey();
24720         var d = this.getPageData();
24721         if(k == e.RETURN){
24722             var v = this.field.dom.value, pageNum;
24723             if(!v || isNaN(pageNum = parseInt(v, 10))){
24724                 this.field.dom.value = d.activePage;
24725                 return;
24726             }
24727             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24728             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24729             e.stopEvent();
24730         }
24731         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))
24732         {
24733           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24734           this.field.dom.value = pageNum;
24735           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24736           e.stopEvent();
24737         }
24738         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24739         {
24740           var v = this.field.dom.value, pageNum; 
24741           var increment = (e.shiftKey) ? 10 : 1;
24742           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24743                 increment *= -1;
24744           }
24745           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24746             this.field.dom.value = d.activePage;
24747             return;
24748           }
24749           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24750           {
24751             this.field.dom.value = parseInt(v, 10) + increment;
24752             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24753             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24754           }
24755           e.stopEvent();
24756         }
24757     },
24758
24759     // private
24760     beforeLoad : function(){
24761         if(this.loading){
24762             this.loading.disable();
24763         }
24764     },
24765
24766     // private
24767     onClick : function(which){
24768         
24769         var ds = this.ds;
24770         if (!ds) {
24771             return;
24772         }
24773         
24774         switch(which){
24775             case "first":
24776                 ds.load({params:{start: 0, limit: this.pageSize}});
24777             break;
24778             case "prev":
24779                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24780             break;
24781             case "next":
24782                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24783             break;
24784             case "last":
24785                 var total = ds.getTotalCount();
24786                 var extra = total % this.pageSize;
24787                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24788                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24789             break;
24790             case "refresh":
24791                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24792             break;
24793         }
24794     },
24795
24796     /**
24797      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24798      * @param {Roo.data.Store} store The data store to unbind
24799      */
24800     unbind : function(ds){
24801         ds.un("beforeload", this.beforeLoad, this);
24802         ds.un("load", this.onLoad, this);
24803         ds.un("loadexception", this.onLoadError, this);
24804         ds.un("remove", this.updateInfo, this);
24805         ds.un("add", this.updateInfo, this);
24806         this.ds = undefined;
24807     },
24808
24809     /**
24810      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24811      * @param {Roo.data.Store} store The data store to bind
24812      */
24813     bind : function(ds){
24814         ds.on("beforeload", this.beforeLoad, this);
24815         ds.on("load", this.onLoad, this);
24816         ds.on("loadexception", this.onLoadError, this);
24817         ds.on("remove", this.updateInfo, this);
24818         ds.on("add", this.updateInfo, this);
24819         this.ds = ds;
24820     }
24821 });/*
24822  * - LGPL
24823  *
24824  * element
24825  * 
24826  */
24827
24828 /**
24829  * @class Roo.bootstrap.MessageBar
24830  * @extends Roo.bootstrap.Component
24831  * Bootstrap MessageBar class
24832  * @cfg {String} html contents of the MessageBar
24833  * @cfg {String} weight (info | success | warning | danger) default info
24834  * @cfg {String} beforeClass insert the bar before the given class
24835  * @cfg {Boolean} closable (true | false) default false
24836  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24837  * 
24838  * @constructor
24839  * Create a new Element
24840  * @param {Object} config The config object
24841  */
24842
24843 Roo.bootstrap.MessageBar = function(config){
24844     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24845 };
24846
24847 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24848     
24849     html: '',
24850     weight: 'info',
24851     closable: false,
24852     fixed: false,
24853     beforeClass: 'bootstrap-sticky-wrap',
24854     
24855     getAutoCreate : function(){
24856         
24857         var cfg = {
24858             tag: 'div',
24859             cls: 'alert alert-dismissable alert-' + this.weight,
24860             cn: [
24861                 {
24862                     tag: 'span',
24863                     cls: 'message',
24864                     html: this.html || ''
24865                 }
24866             ]
24867         };
24868         
24869         if(this.fixed){
24870             cfg.cls += ' alert-messages-fixed';
24871         }
24872         
24873         if(this.closable){
24874             cfg.cn.push({
24875                 tag: 'button',
24876                 cls: 'close',
24877                 html: 'x'
24878             });
24879         }
24880         
24881         return cfg;
24882     },
24883     
24884     onRender : function(ct, position)
24885     {
24886         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24887         
24888         if(!this.el){
24889             var cfg = Roo.apply({},  this.getAutoCreate());
24890             cfg.id = Roo.id();
24891             
24892             if (this.cls) {
24893                 cfg.cls += ' ' + this.cls;
24894             }
24895             if (this.style) {
24896                 cfg.style = this.style;
24897             }
24898             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24899             
24900             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24901         }
24902         
24903         this.el.select('>button.close').on('click', this.hide, this);
24904         
24905     },
24906     
24907     show : function()
24908     {
24909         if (!this.rendered) {
24910             this.render();
24911         }
24912         
24913         this.el.show();
24914         
24915         this.fireEvent('show', this);
24916         
24917     },
24918     
24919     hide : function()
24920     {
24921         if (!this.rendered) {
24922             this.render();
24923         }
24924         
24925         this.el.hide();
24926         
24927         this.fireEvent('hide', this);
24928     },
24929     
24930     update : function()
24931     {
24932 //        var e = this.el.dom.firstChild;
24933 //        
24934 //        if(this.closable){
24935 //            e = e.nextSibling;
24936 //        }
24937 //        
24938 //        e.data = this.html || '';
24939
24940         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24941     }
24942    
24943 });
24944
24945  
24946
24947      /*
24948  * - LGPL
24949  *
24950  * Graph
24951  * 
24952  */
24953
24954
24955 /**
24956  * @class Roo.bootstrap.Graph
24957  * @extends Roo.bootstrap.Component
24958  * Bootstrap Graph class
24959 > Prameters
24960  -sm {number} sm 4
24961  -md {number} md 5
24962  @cfg {String} graphtype  bar | vbar | pie
24963  @cfg {number} g_x coodinator | centre x (pie)
24964  @cfg {number} g_y coodinator | centre y (pie)
24965  @cfg {number} g_r radius (pie)
24966  @cfg {number} g_height height of the chart (respected by all elements in the set)
24967  @cfg {number} g_width width of the chart (respected by all elements in the set)
24968  @cfg {Object} title The title of the chart
24969     
24970  -{Array}  values
24971  -opts (object) options for the chart 
24972      o {
24973      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24974      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24975      o vgutter (number)
24976      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.
24977      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24978      o to
24979      o stretch (boolean)
24980      o }
24981  -opts (object) options for the pie
24982      o{
24983      o cut
24984      o startAngle (number)
24985      o endAngle (number)
24986      } 
24987  *
24988  * @constructor
24989  * Create a new Input
24990  * @param {Object} config The config object
24991  */
24992
24993 Roo.bootstrap.Graph = function(config){
24994     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24995     
24996     this.addEvents({
24997         // img events
24998         /**
24999          * @event click
25000          * The img click event for the img.
25001          * @param {Roo.EventObject} e
25002          */
25003         "click" : true
25004     });
25005 };
25006
25007 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25008     
25009     sm: 4,
25010     md: 5,
25011     graphtype: 'bar',
25012     g_height: 250,
25013     g_width: 400,
25014     g_x: 50,
25015     g_y: 50,
25016     g_r: 30,
25017     opts:{
25018         //g_colors: this.colors,
25019         g_type: 'soft',
25020         g_gutter: '20%'
25021
25022     },
25023     title : false,
25024
25025     getAutoCreate : function(){
25026         
25027         var cfg = {
25028             tag: 'div',
25029             html : null
25030         };
25031         
25032         
25033         return  cfg;
25034     },
25035
25036     onRender : function(ct,position){
25037         
25038         
25039         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25040         
25041         if (typeof(Raphael) == 'undefined') {
25042             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25043             return;
25044         }
25045         
25046         this.raphael = Raphael(this.el.dom);
25047         
25048                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25049                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25050                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25051                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25052                 /*
25053                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25054                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25055                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25056                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25057                 
25058                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25059                 r.barchart(330, 10, 300, 220, data1);
25060                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25061                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25062                 */
25063                 
25064                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25065                 // r.barchart(30, 30, 560, 250,  xdata, {
25066                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25067                 //     axis : "0 0 1 1",
25068                 //     axisxlabels :  xdata
25069                 //     //yvalues : cols,
25070                    
25071                 // });
25072 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25073 //        
25074 //        this.load(null,xdata,{
25075 //                axis : "0 0 1 1",
25076 //                axisxlabels :  xdata
25077 //                });
25078
25079     },
25080
25081     load : function(graphtype,xdata,opts)
25082     {
25083         this.raphael.clear();
25084         if(!graphtype) {
25085             graphtype = this.graphtype;
25086         }
25087         if(!opts){
25088             opts = this.opts;
25089         }
25090         var r = this.raphael,
25091             fin = function () {
25092                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25093             },
25094             fout = function () {
25095                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25096             },
25097             pfin = function() {
25098                 this.sector.stop();
25099                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25100
25101                 if (this.label) {
25102                     this.label[0].stop();
25103                     this.label[0].attr({ r: 7.5 });
25104                     this.label[1].attr({ "font-weight": 800 });
25105                 }
25106             },
25107             pfout = function() {
25108                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25109
25110                 if (this.label) {
25111                     this.label[0].animate({ r: 5 }, 500, "bounce");
25112                     this.label[1].attr({ "font-weight": 400 });
25113                 }
25114             };
25115
25116         switch(graphtype){
25117             case 'bar':
25118                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25119                 break;
25120             case 'hbar':
25121                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25122                 break;
25123             case 'pie':
25124 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25125 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25126 //            
25127                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25128                 
25129                 break;
25130
25131         }
25132         
25133         if(this.title){
25134             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25135         }
25136         
25137     },
25138     
25139     setTitle: function(o)
25140     {
25141         this.title = o;
25142     },
25143     
25144     initEvents: function() {
25145         
25146         if(!this.href){
25147             this.el.on('click', this.onClick, this);
25148         }
25149     },
25150     
25151     onClick : function(e)
25152     {
25153         Roo.log('img onclick');
25154         this.fireEvent('click', this, e);
25155     }
25156    
25157 });
25158
25159  
25160 /*
25161  * - LGPL
25162  *
25163  * numberBox
25164  * 
25165  */
25166 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25167
25168 /**
25169  * @class Roo.bootstrap.dash.NumberBox
25170  * @extends Roo.bootstrap.Component
25171  * Bootstrap NumberBox class
25172  * @cfg {String} headline Box headline
25173  * @cfg {String} content Box content
25174  * @cfg {String} icon Box icon
25175  * @cfg {String} footer Footer text
25176  * @cfg {String} fhref Footer href
25177  * 
25178  * @constructor
25179  * Create a new NumberBox
25180  * @param {Object} config The config object
25181  */
25182
25183
25184 Roo.bootstrap.dash.NumberBox = function(config){
25185     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25186     
25187 };
25188
25189 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25190     
25191     headline : '',
25192     content : '',
25193     icon : '',
25194     footer : '',
25195     fhref : '',
25196     ficon : '',
25197     
25198     getAutoCreate : function(){
25199         
25200         var cfg = {
25201             tag : 'div',
25202             cls : 'small-box ',
25203             cn : [
25204                 {
25205                     tag : 'div',
25206                     cls : 'inner',
25207                     cn :[
25208                         {
25209                             tag : 'h3',
25210                             cls : 'roo-headline',
25211                             html : this.headline
25212                         },
25213                         {
25214                             tag : 'p',
25215                             cls : 'roo-content',
25216                             html : this.content
25217                         }
25218                     ]
25219                 }
25220             ]
25221         };
25222         
25223         if(this.icon){
25224             cfg.cn.push({
25225                 tag : 'div',
25226                 cls : 'icon',
25227                 cn :[
25228                     {
25229                         tag : 'i',
25230                         cls : 'ion ' + this.icon
25231                     }
25232                 ]
25233             });
25234         }
25235         
25236         if(this.footer){
25237             var footer = {
25238                 tag : 'a',
25239                 cls : 'small-box-footer',
25240                 href : this.fhref || '#',
25241                 html : this.footer
25242             };
25243             
25244             cfg.cn.push(footer);
25245             
25246         }
25247         
25248         return  cfg;
25249     },
25250
25251     onRender : function(ct,position){
25252         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25253
25254
25255        
25256                 
25257     },
25258
25259     setHeadline: function (value)
25260     {
25261         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25262     },
25263     
25264     setFooter: function (value, href)
25265     {
25266         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25267         
25268         if(href){
25269             this.el.select('a.small-box-footer',true).first().attr('href', href);
25270         }
25271         
25272     },
25273
25274     setContent: function (value)
25275     {
25276         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25277     },
25278
25279     initEvents: function() 
25280     {   
25281         
25282     }
25283     
25284 });
25285
25286  
25287 /*
25288  * - LGPL
25289  *
25290  * TabBox
25291  * 
25292  */
25293 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25294
25295 /**
25296  * @class Roo.bootstrap.dash.TabBox
25297  * @extends Roo.bootstrap.Component
25298  * Bootstrap TabBox class
25299  * @cfg {String} title Title of the TabBox
25300  * @cfg {String} icon Icon of the TabBox
25301  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25302  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25303  * 
25304  * @constructor
25305  * Create a new TabBox
25306  * @param {Object} config The config object
25307  */
25308
25309
25310 Roo.bootstrap.dash.TabBox = function(config){
25311     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25312     this.addEvents({
25313         // raw events
25314         /**
25315          * @event addpane
25316          * When a pane is added
25317          * @param {Roo.bootstrap.dash.TabPane} pane
25318          */
25319         "addpane" : true,
25320         /**
25321          * @event activatepane
25322          * When a pane is activated
25323          * @param {Roo.bootstrap.dash.TabPane} pane
25324          */
25325         "activatepane" : true
25326         
25327          
25328     });
25329     
25330     this.panes = [];
25331 };
25332
25333 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25334
25335     title : '',
25336     icon : false,
25337     showtabs : true,
25338     tabScrollable : false,
25339     
25340     getChildContainer : function()
25341     {
25342         return this.el.select('.tab-content', true).first();
25343     },
25344     
25345     getAutoCreate : function(){
25346         
25347         var header = {
25348             tag: 'li',
25349             cls: 'pull-left header',
25350             html: this.title,
25351             cn : []
25352         };
25353         
25354         if(this.icon){
25355             header.cn.push({
25356                 tag: 'i',
25357                 cls: 'fa ' + this.icon
25358             });
25359         }
25360         
25361         var h = {
25362             tag: 'ul',
25363             cls: 'nav nav-tabs pull-right',
25364             cn: [
25365                 header
25366             ]
25367         };
25368         
25369         if(this.tabScrollable){
25370             h = {
25371                 tag: 'div',
25372                 cls: 'tab-header',
25373                 cn: [
25374                     {
25375                         tag: 'ul',
25376                         cls: 'nav nav-tabs pull-right',
25377                         cn: [
25378                             header
25379                         ]
25380                     }
25381                 ]
25382             };
25383         }
25384         
25385         var cfg = {
25386             tag: 'div',
25387             cls: 'nav-tabs-custom',
25388             cn: [
25389                 h,
25390                 {
25391                     tag: 'div',
25392                     cls: 'tab-content no-padding',
25393                     cn: []
25394                 }
25395             ]
25396         };
25397
25398         return  cfg;
25399     },
25400     initEvents : function()
25401     {
25402         //Roo.log('add add pane handler');
25403         this.on('addpane', this.onAddPane, this);
25404     },
25405      /**
25406      * Updates the box title
25407      * @param {String} html to set the title to.
25408      */
25409     setTitle : function(value)
25410     {
25411         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25412     },
25413     onAddPane : function(pane)
25414     {
25415         this.panes.push(pane);
25416         //Roo.log('addpane');
25417         //Roo.log(pane);
25418         // tabs are rendere left to right..
25419         if(!this.showtabs){
25420             return;
25421         }
25422         
25423         var ctr = this.el.select('.nav-tabs', true).first();
25424          
25425          
25426         var existing = ctr.select('.nav-tab',true);
25427         var qty = existing.getCount();;
25428         
25429         
25430         var tab = ctr.createChild({
25431             tag : 'li',
25432             cls : 'nav-tab' + (qty ? '' : ' active'),
25433             cn : [
25434                 {
25435                     tag : 'a',
25436                     href:'#',
25437                     html : pane.title
25438                 }
25439             ]
25440         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25441         pane.tab = tab;
25442         
25443         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25444         if (!qty) {
25445             pane.el.addClass('active');
25446         }
25447         
25448                 
25449     },
25450     onTabClick : function(ev,un,ob,pane)
25451     {
25452         //Roo.log('tab - prev default');
25453         ev.preventDefault();
25454         
25455         
25456         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25457         pane.tab.addClass('active');
25458         //Roo.log(pane.title);
25459         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25460         // technically we should have a deactivate event.. but maybe add later.
25461         // and it should not de-activate the selected tab...
25462         this.fireEvent('activatepane', pane);
25463         pane.el.addClass('active');
25464         pane.fireEvent('activate');
25465         
25466         
25467     },
25468     
25469     getActivePane : function()
25470     {
25471         var r = false;
25472         Roo.each(this.panes, function(p) {
25473             if(p.el.hasClass('active')){
25474                 r = p;
25475                 return false;
25476             }
25477             
25478             return;
25479         });
25480         
25481         return r;
25482     }
25483     
25484     
25485 });
25486
25487  
25488 /*
25489  * - LGPL
25490  *
25491  * Tab pane
25492  * 
25493  */
25494 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25495 /**
25496  * @class Roo.bootstrap.TabPane
25497  * @extends Roo.bootstrap.Component
25498  * Bootstrap TabPane class
25499  * @cfg {Boolean} active (false | true) Default false
25500  * @cfg {String} title title of panel
25501
25502  * 
25503  * @constructor
25504  * Create a new TabPane
25505  * @param {Object} config The config object
25506  */
25507
25508 Roo.bootstrap.dash.TabPane = function(config){
25509     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25510     
25511     this.addEvents({
25512         // raw events
25513         /**
25514          * @event activate
25515          * When a pane is activated
25516          * @param {Roo.bootstrap.dash.TabPane} pane
25517          */
25518         "activate" : true
25519          
25520     });
25521 };
25522
25523 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25524     
25525     active : false,
25526     title : '',
25527     
25528     // the tabBox that this is attached to.
25529     tab : false,
25530      
25531     getAutoCreate : function() 
25532     {
25533         var cfg = {
25534             tag: 'div',
25535             cls: 'tab-pane'
25536         };
25537         
25538         if(this.active){
25539             cfg.cls += ' active';
25540         }
25541         
25542         return cfg;
25543     },
25544     initEvents  : function()
25545     {
25546         //Roo.log('trigger add pane handler');
25547         this.parent().fireEvent('addpane', this)
25548     },
25549     
25550      /**
25551      * Updates the tab title 
25552      * @param {String} html to set the title to.
25553      */
25554     setTitle: function(str)
25555     {
25556         if (!this.tab) {
25557             return;
25558         }
25559         this.title = str;
25560         this.tab.select('a', true).first().dom.innerHTML = str;
25561         
25562     }
25563     
25564     
25565     
25566 });
25567
25568  
25569
25570
25571  /*
25572  * - LGPL
25573  *
25574  * menu
25575  * 
25576  */
25577 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25578
25579 /**
25580  * @class Roo.bootstrap.menu.Menu
25581  * @extends Roo.bootstrap.Component
25582  * Bootstrap Menu class - container for Menu
25583  * @cfg {String} html Text of the menu
25584  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25585  * @cfg {String} icon Font awesome icon
25586  * @cfg {String} pos Menu align to (top | bottom) default bottom
25587  * 
25588  * 
25589  * @constructor
25590  * Create a new Menu
25591  * @param {Object} config The config object
25592  */
25593
25594
25595 Roo.bootstrap.menu.Menu = function(config){
25596     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25597     
25598     this.addEvents({
25599         /**
25600          * @event beforeshow
25601          * Fires before this menu is displayed
25602          * @param {Roo.bootstrap.menu.Menu} this
25603          */
25604         beforeshow : true,
25605         /**
25606          * @event beforehide
25607          * Fires before this menu is hidden
25608          * @param {Roo.bootstrap.menu.Menu} this
25609          */
25610         beforehide : true,
25611         /**
25612          * @event show
25613          * Fires after this menu is displayed
25614          * @param {Roo.bootstrap.menu.Menu} this
25615          */
25616         show : true,
25617         /**
25618          * @event hide
25619          * Fires after this menu is hidden
25620          * @param {Roo.bootstrap.menu.Menu} this
25621          */
25622         hide : true,
25623         /**
25624          * @event click
25625          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25626          * @param {Roo.bootstrap.menu.Menu} this
25627          * @param {Roo.EventObject} e
25628          */
25629         click : true
25630     });
25631     
25632 };
25633
25634 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25635     
25636     submenu : false,
25637     html : '',
25638     weight : 'default',
25639     icon : false,
25640     pos : 'bottom',
25641     
25642     
25643     getChildContainer : function() {
25644         if(this.isSubMenu){
25645             return this.el;
25646         }
25647         
25648         return this.el.select('ul.dropdown-menu', true).first();  
25649     },
25650     
25651     getAutoCreate : function()
25652     {
25653         var text = [
25654             {
25655                 tag : 'span',
25656                 cls : 'roo-menu-text',
25657                 html : this.html
25658             }
25659         ];
25660         
25661         if(this.icon){
25662             text.unshift({
25663                 tag : 'i',
25664                 cls : 'fa ' + this.icon
25665             })
25666         }
25667         
25668         
25669         var cfg = {
25670             tag : 'div',
25671             cls : 'btn-group',
25672             cn : [
25673                 {
25674                     tag : 'button',
25675                     cls : 'dropdown-button btn btn-' + this.weight,
25676                     cn : text
25677                 },
25678                 {
25679                     tag : 'button',
25680                     cls : 'dropdown-toggle btn btn-' + this.weight,
25681                     cn : [
25682                         {
25683                             tag : 'span',
25684                             cls : 'caret'
25685                         }
25686                     ]
25687                 },
25688                 {
25689                     tag : 'ul',
25690                     cls : 'dropdown-menu'
25691                 }
25692             ]
25693             
25694         };
25695         
25696         if(this.pos == 'top'){
25697             cfg.cls += ' dropup';
25698         }
25699         
25700         if(this.isSubMenu){
25701             cfg = {
25702                 tag : 'ul',
25703                 cls : 'dropdown-menu'
25704             }
25705         }
25706         
25707         return cfg;
25708     },
25709     
25710     onRender : function(ct, position)
25711     {
25712         this.isSubMenu = ct.hasClass('dropdown-submenu');
25713         
25714         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25715     },
25716     
25717     initEvents : function() 
25718     {
25719         if(this.isSubMenu){
25720             return;
25721         }
25722         
25723         this.hidden = true;
25724         
25725         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25726         this.triggerEl.on('click', this.onTriggerPress, this);
25727         
25728         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25729         this.buttonEl.on('click', this.onClick, this);
25730         
25731     },
25732     
25733     list : function()
25734     {
25735         if(this.isSubMenu){
25736             return this.el;
25737         }
25738         
25739         return this.el.select('ul.dropdown-menu', true).first();
25740     },
25741     
25742     onClick : function(e)
25743     {
25744         this.fireEvent("click", this, e);
25745     },
25746     
25747     onTriggerPress  : function(e)
25748     {   
25749         if (this.isVisible()) {
25750             this.hide();
25751         } else {
25752             this.show();
25753         }
25754     },
25755     
25756     isVisible : function(){
25757         return !this.hidden;
25758     },
25759     
25760     show : function()
25761     {
25762         this.fireEvent("beforeshow", this);
25763         
25764         this.hidden = false;
25765         this.el.addClass('open');
25766         
25767         Roo.get(document).on("mouseup", this.onMouseUp, this);
25768         
25769         this.fireEvent("show", this);
25770         
25771         
25772     },
25773     
25774     hide : function()
25775     {
25776         this.fireEvent("beforehide", this);
25777         
25778         this.hidden = true;
25779         this.el.removeClass('open');
25780         
25781         Roo.get(document).un("mouseup", this.onMouseUp);
25782         
25783         this.fireEvent("hide", this);
25784     },
25785     
25786     onMouseUp : function()
25787     {
25788         this.hide();
25789     }
25790     
25791 });
25792
25793  
25794  /*
25795  * - LGPL
25796  *
25797  * menu item
25798  * 
25799  */
25800 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25801
25802 /**
25803  * @class Roo.bootstrap.menu.Item
25804  * @extends Roo.bootstrap.Component
25805  * Bootstrap MenuItem class
25806  * @cfg {Boolean} submenu (true | false) default false
25807  * @cfg {String} html text of the item
25808  * @cfg {String} href the link
25809  * @cfg {Boolean} disable (true | false) default false
25810  * @cfg {Boolean} preventDefault (true | false) default true
25811  * @cfg {String} icon Font awesome icon
25812  * @cfg {String} pos Submenu align to (left | right) default right 
25813  * 
25814  * 
25815  * @constructor
25816  * Create a new Item
25817  * @param {Object} config The config object
25818  */
25819
25820
25821 Roo.bootstrap.menu.Item = function(config){
25822     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25823     this.addEvents({
25824         /**
25825          * @event mouseover
25826          * Fires when the mouse is hovering over this menu
25827          * @param {Roo.bootstrap.menu.Item} this
25828          * @param {Roo.EventObject} e
25829          */
25830         mouseover : true,
25831         /**
25832          * @event mouseout
25833          * Fires when the mouse exits this menu
25834          * @param {Roo.bootstrap.menu.Item} this
25835          * @param {Roo.EventObject} e
25836          */
25837         mouseout : true,
25838         // raw events
25839         /**
25840          * @event click
25841          * The raw click event for the entire grid.
25842          * @param {Roo.EventObject} e
25843          */
25844         click : true
25845     });
25846 };
25847
25848 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25849     
25850     submenu : false,
25851     href : '',
25852     html : '',
25853     preventDefault: true,
25854     disable : false,
25855     icon : false,
25856     pos : 'right',
25857     
25858     getAutoCreate : function()
25859     {
25860         var text = [
25861             {
25862                 tag : 'span',
25863                 cls : 'roo-menu-item-text',
25864                 html : this.html
25865             }
25866         ];
25867         
25868         if(this.icon){
25869             text.unshift({
25870                 tag : 'i',
25871                 cls : 'fa ' + this.icon
25872             })
25873         }
25874         
25875         var cfg = {
25876             tag : 'li',
25877             cn : [
25878                 {
25879                     tag : 'a',
25880                     href : this.href || '#',
25881                     cn : text
25882                 }
25883             ]
25884         };
25885         
25886         if(this.disable){
25887             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25888         }
25889         
25890         if(this.submenu){
25891             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25892             
25893             if(this.pos == 'left'){
25894                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25895             }
25896         }
25897         
25898         return cfg;
25899     },
25900     
25901     initEvents : function() 
25902     {
25903         this.el.on('mouseover', this.onMouseOver, this);
25904         this.el.on('mouseout', this.onMouseOut, this);
25905         
25906         this.el.select('a', true).first().on('click', this.onClick, this);
25907         
25908     },
25909     
25910     onClick : function(e)
25911     {
25912         if(this.preventDefault){
25913             e.preventDefault();
25914         }
25915         
25916         this.fireEvent("click", this, e);
25917     },
25918     
25919     onMouseOver : function(e)
25920     {
25921         if(this.submenu && this.pos == 'left'){
25922             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25923         }
25924         
25925         this.fireEvent("mouseover", this, e);
25926     },
25927     
25928     onMouseOut : function(e)
25929     {
25930         this.fireEvent("mouseout", this, e);
25931     }
25932 });
25933
25934  
25935
25936  /*
25937  * - LGPL
25938  *
25939  * menu separator
25940  * 
25941  */
25942 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25943
25944 /**
25945  * @class Roo.bootstrap.menu.Separator
25946  * @extends Roo.bootstrap.Component
25947  * Bootstrap Separator class
25948  * 
25949  * @constructor
25950  * Create a new Separator
25951  * @param {Object} config The config object
25952  */
25953
25954
25955 Roo.bootstrap.menu.Separator = function(config){
25956     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25957 };
25958
25959 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25960     
25961     getAutoCreate : function(){
25962         var cfg = {
25963             tag : 'li',
25964             cls: 'divider'
25965         };
25966         
25967         return cfg;
25968     }
25969    
25970 });
25971
25972  
25973
25974  /*
25975  * - LGPL
25976  *
25977  * Tooltip
25978  * 
25979  */
25980
25981 /**
25982  * @class Roo.bootstrap.Tooltip
25983  * Bootstrap Tooltip class
25984  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25985  * to determine which dom element triggers the tooltip.
25986  * 
25987  * It needs to add support for additional attributes like tooltip-position
25988  * 
25989  * @constructor
25990  * Create a new Toolti
25991  * @param {Object} config The config object
25992  */
25993
25994 Roo.bootstrap.Tooltip = function(config){
25995     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25996     
25997     this.alignment = Roo.bootstrap.Tooltip.alignment;
25998     
25999     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26000         this.alignment = config.alignment;
26001     }
26002     
26003 };
26004
26005 Roo.apply(Roo.bootstrap.Tooltip, {
26006     /**
26007      * @function init initialize tooltip monitoring.
26008      * @static
26009      */
26010     currentEl : false,
26011     currentTip : false,
26012     currentRegion : false,
26013     
26014     //  init : delay?
26015     
26016     init : function()
26017     {
26018         Roo.get(document).on('mouseover', this.enter ,this);
26019         Roo.get(document).on('mouseout', this.leave, this);
26020          
26021         
26022         this.currentTip = new Roo.bootstrap.Tooltip();
26023     },
26024     
26025     enter : function(ev)
26026     {
26027         var dom = ev.getTarget();
26028         
26029         //Roo.log(['enter',dom]);
26030         var el = Roo.fly(dom);
26031         if (this.currentEl) {
26032             //Roo.log(dom);
26033             //Roo.log(this.currentEl);
26034             //Roo.log(this.currentEl.contains(dom));
26035             if (this.currentEl == el) {
26036                 return;
26037             }
26038             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26039                 return;
26040             }
26041
26042         }
26043         
26044         if (this.currentTip.el) {
26045             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26046         }    
26047         //Roo.log(ev);
26048         
26049         if(!el || el.dom == document){
26050             return;
26051         }
26052         
26053         var bindEl = el;
26054         
26055         // you can not look for children, as if el is the body.. then everythign is the child..
26056         if (!el.attr('tooltip')) { //
26057             if (!el.select("[tooltip]").elements.length) {
26058                 return;
26059             }
26060             // is the mouse over this child...?
26061             bindEl = el.select("[tooltip]").first();
26062             var xy = ev.getXY();
26063             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26064                 //Roo.log("not in region.");
26065                 return;
26066             }
26067             //Roo.log("child element over..");
26068             
26069         }
26070         this.currentEl = bindEl;
26071         this.currentTip.bind(bindEl);
26072         this.currentRegion = Roo.lib.Region.getRegion(dom);
26073         this.currentTip.enter();
26074         
26075     },
26076     leave : function(ev)
26077     {
26078         var dom = ev.getTarget();
26079         //Roo.log(['leave',dom]);
26080         if (!this.currentEl) {
26081             return;
26082         }
26083         
26084         
26085         if (dom != this.currentEl.dom) {
26086             return;
26087         }
26088         var xy = ev.getXY();
26089         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26090             return;
26091         }
26092         // only activate leave if mouse cursor is outside... bounding box..
26093         
26094         
26095         
26096         
26097         if (this.currentTip) {
26098             this.currentTip.leave();
26099         }
26100         //Roo.log('clear currentEl');
26101         this.currentEl = false;
26102         
26103         
26104     },
26105     alignment : {
26106         'left' : ['r-l', [-2,0], 'right'],
26107         'right' : ['l-r', [2,0], 'left'],
26108         'bottom' : ['t-b', [0,2], 'top'],
26109         'top' : [ 'b-t', [0,-2], 'bottom']
26110     }
26111     
26112 });
26113
26114
26115 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26116     
26117     
26118     bindEl : false,
26119     
26120     delay : null, // can be { show : 300 , hide: 500}
26121     
26122     timeout : null,
26123     
26124     hoverState : null, //???
26125     
26126     placement : 'bottom', 
26127     
26128     alignment : false,
26129     
26130     getAutoCreate : function(){
26131     
26132         var cfg = {
26133            cls : 'tooltip',
26134            role : 'tooltip',
26135            cn : [
26136                 {
26137                     cls : 'tooltip-arrow'
26138                 },
26139                 {
26140                     cls : 'tooltip-inner'
26141                 }
26142            ]
26143         };
26144         
26145         return cfg;
26146     },
26147     bind : function(el)
26148     {
26149         this.bindEl = el;
26150     },
26151       
26152     
26153     enter : function () {
26154        
26155         if (this.timeout != null) {
26156             clearTimeout(this.timeout);
26157         }
26158         
26159         this.hoverState = 'in';
26160          //Roo.log("enter - show");
26161         if (!this.delay || !this.delay.show) {
26162             this.show();
26163             return;
26164         }
26165         var _t = this;
26166         this.timeout = setTimeout(function () {
26167             if (_t.hoverState == 'in') {
26168                 _t.show();
26169             }
26170         }, this.delay.show);
26171     },
26172     leave : function()
26173     {
26174         clearTimeout(this.timeout);
26175     
26176         this.hoverState = 'out';
26177          if (!this.delay || !this.delay.hide) {
26178             this.hide();
26179             return;
26180         }
26181        
26182         var _t = this;
26183         this.timeout = setTimeout(function () {
26184             //Roo.log("leave - timeout");
26185             
26186             if (_t.hoverState == 'out') {
26187                 _t.hide();
26188                 Roo.bootstrap.Tooltip.currentEl = false;
26189             }
26190         }, delay);
26191     },
26192     
26193     show : function (msg)
26194     {
26195         if (!this.el) {
26196             this.render(document.body);
26197         }
26198         // set content.
26199         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26200         
26201         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26202         
26203         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26204         
26205         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26206         
26207         var placement = typeof this.placement == 'function' ?
26208             this.placement.call(this, this.el, on_el) :
26209             this.placement;
26210             
26211         var autoToken = /\s?auto?\s?/i;
26212         var autoPlace = autoToken.test(placement);
26213         if (autoPlace) {
26214             placement = placement.replace(autoToken, '') || 'top';
26215         }
26216         
26217         //this.el.detach()
26218         //this.el.setXY([0,0]);
26219         this.el.show();
26220         //this.el.dom.style.display='block';
26221         
26222         //this.el.appendTo(on_el);
26223         
26224         var p = this.getPosition();
26225         var box = this.el.getBox();
26226         
26227         if (autoPlace) {
26228             // fixme..
26229         }
26230         
26231         var align = this.alignment[placement];
26232         
26233         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26234         
26235         if(placement == 'top' || placement == 'bottom'){
26236             if(xy[0] < 0){
26237                 placement = 'right';
26238             }
26239             
26240             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26241                 placement = 'left';
26242             }
26243             
26244             var scroll = Roo.select('body', true).first().getScroll();
26245             
26246             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26247                 placement = 'top';
26248             }
26249             
26250             align = this.alignment[placement];
26251         }
26252         
26253         this.el.alignTo(this.bindEl, align[0],align[1]);
26254         //var arrow = this.el.select('.arrow',true).first();
26255         //arrow.set(align[2], 
26256         
26257         this.el.addClass(placement);
26258         
26259         this.el.addClass('in fade');
26260         
26261         this.hoverState = null;
26262         
26263         if (this.el.hasClass('fade')) {
26264             // fade it?
26265         }
26266         
26267     },
26268     hide : function()
26269     {
26270          
26271         if (!this.el) {
26272             return;
26273         }
26274         //this.el.setXY([0,0]);
26275         this.el.removeClass('in');
26276         //this.el.hide();
26277         
26278     }
26279     
26280 });
26281  
26282
26283  /*
26284  * - LGPL
26285  *
26286  * Location Picker
26287  * 
26288  */
26289
26290 /**
26291  * @class Roo.bootstrap.LocationPicker
26292  * @extends Roo.bootstrap.Component
26293  * Bootstrap LocationPicker class
26294  * @cfg {Number} latitude Position when init default 0
26295  * @cfg {Number} longitude Position when init default 0
26296  * @cfg {Number} zoom default 15
26297  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26298  * @cfg {Boolean} mapTypeControl default false
26299  * @cfg {Boolean} disableDoubleClickZoom default false
26300  * @cfg {Boolean} scrollwheel default true
26301  * @cfg {Boolean} streetViewControl default false
26302  * @cfg {Number} radius default 0
26303  * @cfg {String} locationName
26304  * @cfg {Boolean} draggable default true
26305  * @cfg {Boolean} enableAutocomplete default false
26306  * @cfg {Boolean} enableReverseGeocode default true
26307  * @cfg {String} markerTitle
26308  * 
26309  * @constructor
26310  * Create a new LocationPicker
26311  * @param {Object} config The config object
26312  */
26313
26314
26315 Roo.bootstrap.LocationPicker = function(config){
26316     
26317     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26318     
26319     this.addEvents({
26320         /**
26321          * @event initial
26322          * Fires when the picker initialized.
26323          * @param {Roo.bootstrap.LocationPicker} this
26324          * @param {Google Location} location
26325          */
26326         initial : true,
26327         /**
26328          * @event positionchanged
26329          * Fires when the picker position changed.
26330          * @param {Roo.bootstrap.LocationPicker} this
26331          * @param {Google Location} location
26332          */
26333         positionchanged : true,
26334         /**
26335          * @event resize
26336          * Fires when the map resize.
26337          * @param {Roo.bootstrap.LocationPicker} this
26338          */
26339         resize : true,
26340         /**
26341          * @event show
26342          * Fires when the map show.
26343          * @param {Roo.bootstrap.LocationPicker} this
26344          */
26345         show : true,
26346         /**
26347          * @event hide
26348          * Fires when the map hide.
26349          * @param {Roo.bootstrap.LocationPicker} this
26350          */
26351         hide : true,
26352         /**
26353          * @event mapClick
26354          * Fires when click the map.
26355          * @param {Roo.bootstrap.LocationPicker} this
26356          * @param {Map event} e
26357          */
26358         mapClick : true,
26359         /**
26360          * @event mapRightClick
26361          * Fires when right click the map.
26362          * @param {Roo.bootstrap.LocationPicker} this
26363          * @param {Map event} e
26364          */
26365         mapRightClick : true,
26366         /**
26367          * @event markerClick
26368          * Fires when click the marker.
26369          * @param {Roo.bootstrap.LocationPicker} this
26370          * @param {Map event} e
26371          */
26372         markerClick : true,
26373         /**
26374          * @event markerRightClick
26375          * Fires when right click the marker.
26376          * @param {Roo.bootstrap.LocationPicker} this
26377          * @param {Map event} e
26378          */
26379         markerRightClick : true,
26380         /**
26381          * @event OverlayViewDraw
26382          * Fires when OverlayView Draw
26383          * @param {Roo.bootstrap.LocationPicker} this
26384          */
26385         OverlayViewDraw : true,
26386         /**
26387          * @event OverlayViewOnAdd
26388          * Fires when OverlayView Draw
26389          * @param {Roo.bootstrap.LocationPicker} this
26390          */
26391         OverlayViewOnAdd : true,
26392         /**
26393          * @event OverlayViewOnRemove
26394          * Fires when OverlayView Draw
26395          * @param {Roo.bootstrap.LocationPicker} this
26396          */
26397         OverlayViewOnRemove : true,
26398         /**
26399          * @event OverlayViewShow
26400          * Fires when OverlayView Draw
26401          * @param {Roo.bootstrap.LocationPicker} this
26402          * @param {Pixel} cpx
26403          */
26404         OverlayViewShow : true,
26405         /**
26406          * @event OverlayViewHide
26407          * Fires when OverlayView Draw
26408          * @param {Roo.bootstrap.LocationPicker} this
26409          */
26410         OverlayViewHide : true,
26411         /**
26412          * @event loadexception
26413          * Fires when load google lib failed.
26414          * @param {Roo.bootstrap.LocationPicker} this
26415          */
26416         loadexception : true
26417     });
26418         
26419 };
26420
26421 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26422     
26423     gMapContext: false,
26424     
26425     latitude: 0,
26426     longitude: 0,
26427     zoom: 15,
26428     mapTypeId: false,
26429     mapTypeControl: false,
26430     disableDoubleClickZoom: false,
26431     scrollwheel: true,
26432     streetViewControl: false,
26433     radius: 0,
26434     locationName: '',
26435     draggable: true,
26436     enableAutocomplete: false,
26437     enableReverseGeocode: true,
26438     markerTitle: '',
26439     
26440     getAutoCreate: function()
26441     {
26442
26443         var cfg = {
26444             tag: 'div',
26445             cls: 'roo-location-picker'
26446         };
26447         
26448         return cfg
26449     },
26450     
26451     initEvents: function(ct, position)
26452     {       
26453         if(!this.el.getWidth() || this.isApplied()){
26454             return;
26455         }
26456         
26457         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26458         
26459         this.initial();
26460     },
26461     
26462     initial: function()
26463     {
26464         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26465             this.fireEvent('loadexception', this);
26466             return;
26467         }
26468         
26469         if(!this.mapTypeId){
26470             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26471         }
26472         
26473         this.gMapContext = this.GMapContext();
26474         
26475         this.initOverlayView();
26476         
26477         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26478         
26479         var _this = this;
26480                 
26481         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26482             _this.setPosition(_this.gMapContext.marker.position);
26483         });
26484         
26485         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26486             _this.fireEvent('mapClick', this, event);
26487             
26488         });
26489
26490         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26491             _this.fireEvent('mapRightClick', this, event);
26492             
26493         });
26494         
26495         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26496             _this.fireEvent('markerClick', this, event);
26497             
26498         });
26499
26500         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26501             _this.fireEvent('markerRightClick', this, event);
26502             
26503         });
26504         
26505         this.setPosition(this.gMapContext.location);
26506         
26507         this.fireEvent('initial', this, this.gMapContext.location);
26508     },
26509     
26510     initOverlayView: function()
26511     {
26512         var _this = this;
26513         
26514         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26515             
26516             draw: function()
26517             {
26518                 _this.fireEvent('OverlayViewDraw', _this);
26519             },
26520             
26521             onAdd: function()
26522             {
26523                 _this.fireEvent('OverlayViewOnAdd', _this);
26524             },
26525             
26526             onRemove: function()
26527             {
26528                 _this.fireEvent('OverlayViewOnRemove', _this);
26529             },
26530             
26531             show: function(cpx)
26532             {
26533                 _this.fireEvent('OverlayViewShow', _this, cpx);
26534             },
26535             
26536             hide: function()
26537             {
26538                 _this.fireEvent('OverlayViewHide', _this);
26539             }
26540             
26541         });
26542     },
26543     
26544     fromLatLngToContainerPixel: function(event)
26545     {
26546         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26547     },
26548     
26549     isApplied: function() 
26550     {
26551         return this.getGmapContext() == false ? false : true;
26552     },
26553     
26554     getGmapContext: function() 
26555     {
26556         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26557     },
26558     
26559     GMapContext: function() 
26560     {
26561         var position = new google.maps.LatLng(this.latitude, this.longitude);
26562         
26563         var _map = new google.maps.Map(this.el.dom, {
26564             center: position,
26565             zoom: this.zoom,
26566             mapTypeId: this.mapTypeId,
26567             mapTypeControl: this.mapTypeControl,
26568             disableDoubleClickZoom: this.disableDoubleClickZoom,
26569             scrollwheel: this.scrollwheel,
26570             streetViewControl: this.streetViewControl,
26571             locationName: this.locationName,
26572             draggable: this.draggable,
26573             enableAutocomplete: this.enableAutocomplete,
26574             enableReverseGeocode: this.enableReverseGeocode
26575         });
26576         
26577         var _marker = new google.maps.Marker({
26578             position: position,
26579             map: _map,
26580             title: this.markerTitle,
26581             draggable: this.draggable
26582         });
26583         
26584         return {
26585             map: _map,
26586             marker: _marker,
26587             circle: null,
26588             location: position,
26589             radius: this.radius,
26590             locationName: this.locationName,
26591             addressComponents: {
26592                 formatted_address: null,
26593                 addressLine1: null,
26594                 addressLine2: null,
26595                 streetName: null,
26596                 streetNumber: null,
26597                 city: null,
26598                 district: null,
26599                 state: null,
26600                 stateOrProvince: null
26601             },
26602             settings: this,
26603             domContainer: this.el.dom,
26604             geodecoder: new google.maps.Geocoder()
26605         };
26606     },
26607     
26608     drawCircle: function(center, radius, options) 
26609     {
26610         if (this.gMapContext.circle != null) {
26611             this.gMapContext.circle.setMap(null);
26612         }
26613         if (radius > 0) {
26614             radius *= 1;
26615             options = Roo.apply({}, options, {
26616                 strokeColor: "#0000FF",
26617                 strokeOpacity: .35,
26618                 strokeWeight: 2,
26619                 fillColor: "#0000FF",
26620                 fillOpacity: .2
26621             });
26622             
26623             options.map = this.gMapContext.map;
26624             options.radius = radius;
26625             options.center = center;
26626             this.gMapContext.circle = new google.maps.Circle(options);
26627             return this.gMapContext.circle;
26628         }
26629         
26630         return null;
26631     },
26632     
26633     setPosition: function(location) 
26634     {
26635         this.gMapContext.location = location;
26636         this.gMapContext.marker.setPosition(location);
26637         this.gMapContext.map.panTo(location);
26638         this.drawCircle(location, this.gMapContext.radius, {});
26639         
26640         var _this = this;
26641         
26642         if (this.gMapContext.settings.enableReverseGeocode) {
26643             this.gMapContext.geodecoder.geocode({
26644                 latLng: this.gMapContext.location
26645             }, function(results, status) {
26646                 
26647                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26648                     _this.gMapContext.locationName = results[0].formatted_address;
26649                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26650                     
26651                     _this.fireEvent('positionchanged', this, location);
26652                 }
26653             });
26654             
26655             return;
26656         }
26657         
26658         this.fireEvent('positionchanged', this, location);
26659     },
26660     
26661     resize: function()
26662     {
26663         google.maps.event.trigger(this.gMapContext.map, "resize");
26664         
26665         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26666         
26667         this.fireEvent('resize', this);
26668     },
26669     
26670     setPositionByLatLng: function(latitude, longitude)
26671     {
26672         this.setPosition(new google.maps.LatLng(latitude, longitude));
26673     },
26674     
26675     getCurrentPosition: function() 
26676     {
26677         return {
26678             latitude: this.gMapContext.location.lat(),
26679             longitude: this.gMapContext.location.lng()
26680         };
26681     },
26682     
26683     getAddressName: function() 
26684     {
26685         return this.gMapContext.locationName;
26686     },
26687     
26688     getAddressComponents: function() 
26689     {
26690         return this.gMapContext.addressComponents;
26691     },
26692     
26693     address_component_from_google_geocode: function(address_components) 
26694     {
26695         var result = {};
26696         
26697         for (var i = 0; i < address_components.length; i++) {
26698             var component = address_components[i];
26699             if (component.types.indexOf("postal_code") >= 0) {
26700                 result.postalCode = component.short_name;
26701             } else if (component.types.indexOf("street_number") >= 0) {
26702                 result.streetNumber = component.short_name;
26703             } else if (component.types.indexOf("route") >= 0) {
26704                 result.streetName = component.short_name;
26705             } else if (component.types.indexOf("neighborhood") >= 0) {
26706                 result.city = component.short_name;
26707             } else if (component.types.indexOf("locality") >= 0) {
26708                 result.city = component.short_name;
26709             } else if (component.types.indexOf("sublocality") >= 0) {
26710                 result.district = component.short_name;
26711             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26712                 result.stateOrProvince = component.short_name;
26713             } else if (component.types.indexOf("country") >= 0) {
26714                 result.country = component.short_name;
26715             }
26716         }
26717         
26718         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26719         result.addressLine2 = "";
26720         return result;
26721     },
26722     
26723     setZoomLevel: function(zoom)
26724     {
26725         this.gMapContext.map.setZoom(zoom);
26726     },
26727     
26728     show: function()
26729     {
26730         if(!this.el){
26731             return;
26732         }
26733         
26734         this.el.show();
26735         
26736         this.resize();
26737         
26738         this.fireEvent('show', this);
26739     },
26740     
26741     hide: function()
26742     {
26743         if(!this.el){
26744             return;
26745         }
26746         
26747         this.el.hide();
26748         
26749         this.fireEvent('hide', this);
26750     }
26751     
26752 });
26753
26754 Roo.apply(Roo.bootstrap.LocationPicker, {
26755     
26756     OverlayView : function(map, options)
26757     {
26758         options = options || {};
26759         
26760         this.setMap(map);
26761     }
26762     
26763     
26764 });/*
26765  * - LGPL
26766  *
26767  * Alert
26768  * 
26769  */
26770
26771 /**
26772  * @class Roo.bootstrap.Alert
26773  * @extends Roo.bootstrap.Component
26774  * Bootstrap Alert class
26775  * @cfg {String} title The title of alert
26776  * @cfg {String} html The content of alert
26777  * @cfg {String} weight (  success | info | warning | danger )
26778  * @cfg {String} faicon font-awesomeicon
26779  * 
26780  * @constructor
26781  * Create a new alert
26782  * @param {Object} config The config object
26783  */
26784
26785
26786 Roo.bootstrap.Alert = function(config){
26787     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26788     
26789 };
26790
26791 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26792     
26793     title: '',
26794     html: '',
26795     weight: false,
26796     faicon: false,
26797     
26798     getAutoCreate : function()
26799     {
26800         
26801         var cfg = {
26802             tag : 'div',
26803             cls : 'alert',
26804             cn : [
26805                 {
26806                     tag : 'i',
26807                     cls : 'roo-alert-icon'
26808                     
26809                 },
26810                 {
26811                     tag : 'b',
26812                     cls : 'roo-alert-title',
26813                     html : this.title
26814                 },
26815                 {
26816                     tag : 'span',
26817                     cls : 'roo-alert-text',
26818                     html : this.html
26819                 }
26820             ]
26821         };
26822         
26823         if(this.faicon){
26824             cfg.cn[0].cls += ' fa ' + this.faicon;
26825         }
26826         
26827         if(this.weight){
26828             cfg.cls += ' alert-' + this.weight;
26829         }
26830         
26831         return cfg;
26832     },
26833     
26834     initEvents: function() 
26835     {
26836         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26837     },
26838     
26839     setTitle : function(str)
26840     {
26841         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26842     },
26843     
26844     setText : function(str)
26845     {
26846         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26847     },
26848     
26849     setWeight : function(weight)
26850     {
26851         if(this.weight){
26852             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26853         }
26854         
26855         this.weight = weight;
26856         
26857         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26858     },
26859     
26860     setIcon : function(icon)
26861     {
26862         if(this.faicon){
26863             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26864         }
26865         
26866         this.faicon = icon;
26867         
26868         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26869     },
26870     
26871     hide: function() 
26872     {
26873         this.el.hide();   
26874     },
26875     
26876     show: function() 
26877     {  
26878         this.el.show();   
26879     }
26880     
26881 });
26882
26883  
26884 /*
26885 * Licence: LGPL
26886 */
26887
26888 /**
26889  * @class Roo.bootstrap.UploadCropbox
26890  * @extends Roo.bootstrap.Component
26891  * Bootstrap UploadCropbox class
26892  * @cfg {String} emptyText show when image has been loaded
26893  * @cfg {String} rotateNotify show when image too small to rotate
26894  * @cfg {Number} errorTimeout default 3000
26895  * @cfg {Number} minWidth default 300
26896  * @cfg {Number} minHeight default 300
26897  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26898  * @cfg {Boolean} isDocument (true|false) default false
26899  * @cfg {String} url action url
26900  * @cfg {String} paramName default 'imageUpload'
26901  * @cfg {String} method default POST
26902  * @cfg {Boolean} loadMask (true|false) default true
26903  * @cfg {Boolean} loadingText default 'Loading...'
26904  * 
26905  * @constructor
26906  * Create a new UploadCropbox
26907  * @param {Object} config The config object
26908  */
26909
26910 Roo.bootstrap.UploadCropbox = function(config){
26911     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26912     
26913     this.addEvents({
26914         /**
26915          * @event beforeselectfile
26916          * Fire before select file
26917          * @param {Roo.bootstrap.UploadCropbox} this
26918          */
26919         "beforeselectfile" : true,
26920         /**
26921          * @event initial
26922          * Fire after initEvent
26923          * @param {Roo.bootstrap.UploadCropbox} this
26924          */
26925         "initial" : true,
26926         /**
26927          * @event crop
26928          * Fire after initEvent
26929          * @param {Roo.bootstrap.UploadCropbox} this
26930          * @param {String} data
26931          */
26932         "crop" : true,
26933         /**
26934          * @event prepare
26935          * Fire when preparing the file data
26936          * @param {Roo.bootstrap.UploadCropbox} this
26937          * @param {Object} file
26938          */
26939         "prepare" : true,
26940         /**
26941          * @event exception
26942          * Fire when get exception
26943          * @param {Roo.bootstrap.UploadCropbox} this
26944          * @param {XMLHttpRequest} xhr
26945          */
26946         "exception" : true,
26947         /**
26948          * @event beforeloadcanvas
26949          * Fire before load the canvas
26950          * @param {Roo.bootstrap.UploadCropbox} this
26951          * @param {String} src
26952          */
26953         "beforeloadcanvas" : true,
26954         /**
26955          * @event trash
26956          * Fire when trash image
26957          * @param {Roo.bootstrap.UploadCropbox} this
26958          */
26959         "trash" : true,
26960         /**
26961          * @event download
26962          * Fire when download the image
26963          * @param {Roo.bootstrap.UploadCropbox} this
26964          */
26965         "download" : true,
26966         /**
26967          * @event footerbuttonclick
26968          * Fire when footerbuttonclick
26969          * @param {Roo.bootstrap.UploadCropbox} this
26970          * @param {String} type
26971          */
26972         "footerbuttonclick" : true,
26973         /**
26974          * @event resize
26975          * Fire when resize
26976          * @param {Roo.bootstrap.UploadCropbox} this
26977          */
26978         "resize" : true,
26979         /**
26980          * @event rotate
26981          * Fire when rotate the image
26982          * @param {Roo.bootstrap.UploadCropbox} this
26983          * @param {String} pos
26984          */
26985         "rotate" : true,
26986         /**
26987          * @event inspect
26988          * Fire when inspect the file
26989          * @param {Roo.bootstrap.UploadCropbox} this
26990          * @param {Object} file
26991          */
26992         "inspect" : true,
26993         /**
26994          * @event upload
26995          * Fire when xhr upload the file
26996          * @param {Roo.bootstrap.UploadCropbox} this
26997          * @param {Object} data
26998          */
26999         "upload" : true,
27000         /**
27001          * @event arrange
27002          * Fire when arrange the file data
27003          * @param {Roo.bootstrap.UploadCropbox} this
27004          * @param {Object} formData
27005          */
27006         "arrange" : true
27007     });
27008     
27009     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27010 };
27011
27012 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27013     
27014     emptyText : 'Click to upload image',
27015     rotateNotify : 'Image is too small to rotate',
27016     errorTimeout : 3000,
27017     scale : 0,
27018     baseScale : 1,
27019     rotate : 0,
27020     dragable : false,
27021     pinching : false,
27022     mouseX : 0,
27023     mouseY : 0,
27024     cropData : false,
27025     minWidth : 300,
27026     minHeight : 300,
27027     file : false,
27028     exif : {},
27029     baseRotate : 1,
27030     cropType : 'image/jpeg',
27031     buttons : false,
27032     canvasLoaded : false,
27033     isDocument : false,
27034     method : 'POST',
27035     paramName : 'imageUpload',
27036     loadMask : true,
27037     loadingText : 'Loading...',
27038     maskEl : false,
27039     
27040     getAutoCreate : function()
27041     {
27042         var cfg = {
27043             tag : 'div',
27044             cls : 'roo-upload-cropbox',
27045             cn : [
27046                 {
27047                     tag : 'input',
27048                     cls : 'roo-upload-cropbox-selector',
27049                     type : 'file'
27050                 },
27051                 {
27052                     tag : 'div',
27053                     cls : 'roo-upload-cropbox-body',
27054                     style : 'cursor:pointer',
27055                     cn : [
27056                         {
27057                             tag : 'div',
27058                             cls : 'roo-upload-cropbox-preview'
27059                         },
27060                         {
27061                             tag : 'div',
27062                             cls : 'roo-upload-cropbox-thumb'
27063                         },
27064                         {
27065                             tag : 'div',
27066                             cls : 'roo-upload-cropbox-empty-notify',
27067                             html : this.emptyText
27068                         },
27069                         {
27070                             tag : 'div',
27071                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27072                             html : this.rotateNotify
27073                         }
27074                     ]
27075                 },
27076                 {
27077                     tag : 'div',
27078                     cls : 'roo-upload-cropbox-footer',
27079                     cn : {
27080                         tag : 'div',
27081                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27082                         cn : []
27083                     }
27084                 }
27085             ]
27086         };
27087         
27088         return cfg;
27089     },
27090     
27091     onRender : function(ct, position)
27092     {
27093         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27094         
27095         if (this.buttons.length) {
27096             
27097             Roo.each(this.buttons, function(bb) {
27098                 
27099                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27100                 
27101                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27102                 
27103             }, this);
27104         }
27105         
27106         if(this.loadMask){
27107             this.maskEl = this.el;
27108         }
27109     },
27110     
27111     initEvents : function()
27112     {
27113         this.urlAPI = (window.createObjectURL && window) || 
27114                                 (window.URL && URL.revokeObjectURL && URL) || 
27115                                 (window.webkitURL && webkitURL);
27116                         
27117         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27118         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27119         
27120         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27121         this.selectorEl.hide();
27122         
27123         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27124         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27125         
27126         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27127         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27128         this.thumbEl.hide();
27129         
27130         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27131         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27132         
27133         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27134         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27135         this.errorEl.hide();
27136         
27137         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27138         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27139         this.footerEl.hide();
27140         
27141         this.setThumbBoxSize();
27142         
27143         this.bind();
27144         
27145         this.resize();
27146         
27147         this.fireEvent('initial', this);
27148     },
27149
27150     bind : function()
27151     {
27152         var _this = this;
27153         
27154         window.addEventListener("resize", function() { _this.resize(); } );
27155         
27156         this.bodyEl.on('click', this.beforeSelectFile, this);
27157         
27158         if(Roo.isTouch){
27159             this.bodyEl.on('touchstart', this.onTouchStart, this);
27160             this.bodyEl.on('touchmove', this.onTouchMove, this);
27161             this.bodyEl.on('touchend', this.onTouchEnd, this);
27162         }
27163         
27164         if(!Roo.isTouch){
27165             this.bodyEl.on('mousedown', this.onMouseDown, this);
27166             this.bodyEl.on('mousemove', this.onMouseMove, this);
27167             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27168             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27169             Roo.get(document).on('mouseup', this.onMouseUp, this);
27170         }
27171         
27172         this.selectorEl.on('change', this.onFileSelected, this);
27173     },
27174     
27175     reset : function()
27176     {    
27177         this.scale = 0;
27178         this.baseScale = 1;
27179         this.rotate = 0;
27180         this.baseRotate = 1;
27181         this.dragable = false;
27182         this.pinching = false;
27183         this.mouseX = 0;
27184         this.mouseY = 0;
27185         this.cropData = false;
27186         this.notifyEl.dom.innerHTML = this.emptyText;
27187         
27188         this.selectorEl.dom.value = '';
27189         
27190     },
27191     
27192     resize : function()
27193     {
27194         if(this.fireEvent('resize', this) != false){
27195             this.setThumbBoxPosition();
27196             this.setCanvasPosition();
27197         }
27198     },
27199     
27200     onFooterButtonClick : function(e, el, o, type)
27201     {
27202         switch (type) {
27203             case 'rotate-left' :
27204                 this.onRotateLeft(e);
27205                 break;
27206             case 'rotate-right' :
27207                 this.onRotateRight(e);
27208                 break;
27209             case 'picture' :
27210                 this.beforeSelectFile(e);
27211                 break;
27212             case 'trash' :
27213                 this.trash(e);
27214                 break;
27215             case 'crop' :
27216                 this.crop(e);
27217                 break;
27218             case 'download' :
27219                 this.download(e);
27220                 break;
27221             default :
27222                 break;
27223         }
27224         
27225         this.fireEvent('footerbuttonclick', this, type);
27226     },
27227     
27228     beforeSelectFile : function(e)
27229     {
27230         e.preventDefault();
27231         
27232         if(this.fireEvent('beforeselectfile', this) != false){
27233             this.selectorEl.dom.click();
27234         }
27235     },
27236     
27237     onFileSelected : function(e)
27238     {
27239         e.preventDefault();
27240         
27241         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27242             return;
27243         }
27244         
27245         var file = this.selectorEl.dom.files[0];
27246         
27247         if(this.fireEvent('inspect', this, file) != false){
27248             this.prepare(file);
27249         }
27250         
27251     },
27252     
27253     trash : function(e)
27254     {
27255         this.fireEvent('trash', this);
27256     },
27257     
27258     download : function(e)
27259     {
27260         this.fireEvent('download', this);
27261     },
27262     
27263     loadCanvas : function(src)
27264     {   
27265         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27266             
27267             this.reset();
27268             
27269             this.imageEl = document.createElement('img');
27270             
27271             var _this = this;
27272             
27273             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27274             
27275             this.imageEl.src = src;
27276         }
27277     },
27278     
27279     onLoadCanvas : function()
27280     {   
27281         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27282         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27283         
27284         this.bodyEl.un('click', this.beforeSelectFile, this);
27285         
27286         this.notifyEl.hide();
27287         this.thumbEl.show();
27288         this.footerEl.show();
27289         
27290         this.baseRotateLevel();
27291         
27292         if(this.isDocument){
27293             this.setThumbBoxSize();
27294         }
27295         
27296         this.setThumbBoxPosition();
27297         
27298         this.baseScaleLevel();
27299         
27300         this.draw();
27301         
27302         this.resize();
27303         
27304         this.canvasLoaded = true;
27305         
27306         if(this.loadMask){
27307             this.maskEl.unmask();
27308         }
27309         
27310     },
27311     
27312     setCanvasPosition : function()
27313     {   
27314         if(!this.canvasEl){
27315             return;
27316         }
27317         
27318         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27319         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27320         
27321         this.previewEl.setLeft(pw);
27322         this.previewEl.setTop(ph);
27323         
27324     },
27325     
27326     onMouseDown : function(e)
27327     {   
27328         e.stopEvent();
27329         
27330         this.dragable = true;
27331         this.pinching = false;
27332         
27333         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27334             this.dragable = false;
27335             return;
27336         }
27337         
27338         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27339         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27340         
27341     },
27342     
27343     onMouseMove : function(e)
27344     {   
27345         e.stopEvent();
27346         
27347         if(!this.canvasLoaded){
27348             return;
27349         }
27350         
27351         if (!this.dragable){
27352             return;
27353         }
27354         
27355         var minX = Math.ceil(this.thumbEl.getLeft(true));
27356         var minY = Math.ceil(this.thumbEl.getTop(true));
27357         
27358         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27359         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27360         
27361         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27362         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27363         
27364         x = x - this.mouseX;
27365         y = y - this.mouseY;
27366         
27367         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27368         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27369         
27370         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27371         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27372         
27373         this.previewEl.setLeft(bgX);
27374         this.previewEl.setTop(bgY);
27375         
27376         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27377         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27378     },
27379     
27380     onMouseUp : function(e)
27381     {   
27382         e.stopEvent();
27383         
27384         this.dragable = false;
27385     },
27386     
27387     onMouseWheel : function(e)
27388     {   
27389         e.stopEvent();
27390         
27391         this.startScale = this.scale;
27392         
27393         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27394         
27395         if(!this.zoomable()){
27396             this.scale = this.startScale;
27397             return;
27398         }
27399         
27400         this.draw();
27401         
27402         return;
27403     },
27404     
27405     zoomable : function()
27406     {
27407         var minScale = this.thumbEl.getWidth() / this.minWidth;
27408         
27409         if(this.minWidth < this.minHeight){
27410             minScale = this.thumbEl.getHeight() / this.minHeight;
27411         }
27412         
27413         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27414         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27415         
27416         if(
27417                 this.isDocument &&
27418                 (this.rotate == 0 || this.rotate == 180) && 
27419                 (
27420                     width > this.imageEl.OriginWidth || 
27421                     height > this.imageEl.OriginHeight ||
27422                     (width < this.minWidth && height < this.minHeight)
27423                 )
27424         ){
27425             return false;
27426         }
27427         
27428         if(
27429                 this.isDocument &&
27430                 (this.rotate == 90 || this.rotate == 270) && 
27431                 (
27432                     width > this.imageEl.OriginWidth || 
27433                     height > this.imageEl.OriginHeight ||
27434                     (width < this.minHeight && height < this.minWidth)
27435                 )
27436         ){
27437             return false;
27438         }
27439         
27440         if(
27441                 !this.isDocument &&
27442                 (this.rotate == 0 || this.rotate == 180) && 
27443                 (
27444                     width < this.minWidth || 
27445                     width > this.imageEl.OriginWidth || 
27446                     height < this.minHeight || 
27447                     height > this.imageEl.OriginHeight
27448                 )
27449         ){
27450             return false;
27451         }
27452         
27453         if(
27454                 !this.isDocument &&
27455                 (this.rotate == 90 || this.rotate == 270) && 
27456                 (
27457                     width < this.minHeight || 
27458                     width > this.imageEl.OriginWidth || 
27459                     height < this.minWidth || 
27460                     height > this.imageEl.OriginHeight
27461                 )
27462         ){
27463             return false;
27464         }
27465         
27466         return true;
27467         
27468     },
27469     
27470     onRotateLeft : function(e)
27471     {   
27472         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27473             
27474             var minScale = this.thumbEl.getWidth() / this.minWidth;
27475             
27476             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27477             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27478             
27479             this.startScale = this.scale;
27480             
27481             while (this.getScaleLevel() < minScale){
27482             
27483                 this.scale = this.scale + 1;
27484                 
27485                 if(!this.zoomable()){
27486                     break;
27487                 }
27488                 
27489                 if(
27490                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27491                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27492                 ){
27493                     continue;
27494                 }
27495                 
27496                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27497
27498                 this.draw();
27499                 
27500                 return;
27501             }
27502             
27503             this.scale = this.startScale;
27504             
27505             this.onRotateFail();
27506             
27507             return false;
27508         }
27509         
27510         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27511
27512         if(this.isDocument){
27513             this.setThumbBoxSize();
27514             this.setThumbBoxPosition();
27515             this.setCanvasPosition();
27516         }
27517         
27518         this.draw();
27519         
27520         this.fireEvent('rotate', this, 'left');
27521         
27522     },
27523     
27524     onRotateRight : function(e)
27525     {
27526         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27527             
27528             var minScale = this.thumbEl.getWidth() / this.minWidth;
27529         
27530             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27531             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27532             
27533             this.startScale = this.scale;
27534             
27535             while (this.getScaleLevel() < minScale){
27536             
27537                 this.scale = this.scale + 1;
27538                 
27539                 if(!this.zoomable()){
27540                     break;
27541                 }
27542                 
27543                 if(
27544                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27545                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27546                 ){
27547                     continue;
27548                 }
27549                 
27550                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27551
27552                 this.draw();
27553                 
27554                 return;
27555             }
27556             
27557             this.scale = this.startScale;
27558             
27559             this.onRotateFail();
27560             
27561             return false;
27562         }
27563         
27564         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27565
27566         if(this.isDocument){
27567             this.setThumbBoxSize();
27568             this.setThumbBoxPosition();
27569             this.setCanvasPosition();
27570         }
27571         
27572         this.draw();
27573         
27574         this.fireEvent('rotate', this, 'right');
27575     },
27576     
27577     onRotateFail : function()
27578     {
27579         this.errorEl.show(true);
27580         
27581         var _this = this;
27582         
27583         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27584     },
27585     
27586     draw : function()
27587     {
27588         this.previewEl.dom.innerHTML = '';
27589         
27590         var canvasEl = document.createElement("canvas");
27591         
27592         var contextEl = canvasEl.getContext("2d");
27593         
27594         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27595         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27596         var center = this.imageEl.OriginWidth / 2;
27597         
27598         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27599             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27600             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27601             center = this.imageEl.OriginHeight / 2;
27602         }
27603         
27604         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27605         
27606         contextEl.translate(center, center);
27607         contextEl.rotate(this.rotate * Math.PI / 180);
27608
27609         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27610         
27611         this.canvasEl = document.createElement("canvas");
27612         
27613         this.contextEl = this.canvasEl.getContext("2d");
27614         
27615         switch (this.rotate) {
27616             case 0 :
27617                 
27618                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27619                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27620                 
27621                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27622                 
27623                 break;
27624             case 90 : 
27625                 
27626                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27627                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27628                 
27629                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27630                     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);
27631                     break;
27632                 }
27633                 
27634                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27635                 
27636                 break;
27637             case 180 :
27638                 
27639                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27640                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27641                 
27642                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27643                     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);
27644                     break;
27645                 }
27646                 
27647                 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);
27648                 
27649                 break;
27650             case 270 :
27651                 
27652                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27653                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27654         
27655                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27656                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27657                     break;
27658                 }
27659                 
27660                 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);
27661                 
27662                 break;
27663             default : 
27664                 break;
27665         }
27666         
27667         this.previewEl.appendChild(this.canvasEl);
27668         
27669         this.setCanvasPosition();
27670     },
27671     
27672     crop : function()
27673     {
27674         if(!this.canvasLoaded){
27675             return;
27676         }
27677         
27678         var imageCanvas = document.createElement("canvas");
27679         
27680         var imageContext = imageCanvas.getContext("2d");
27681         
27682         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27683         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27684         
27685         var center = imageCanvas.width / 2;
27686         
27687         imageContext.translate(center, center);
27688         
27689         imageContext.rotate(this.rotate * Math.PI / 180);
27690         
27691         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27692         
27693         var canvas = document.createElement("canvas");
27694         
27695         var context = canvas.getContext("2d");
27696                 
27697         canvas.width = this.minWidth;
27698         canvas.height = this.minHeight;
27699
27700         switch (this.rotate) {
27701             case 0 :
27702                 
27703                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27704                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27705                 
27706                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27707                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27708                 
27709                 var targetWidth = this.minWidth - 2 * x;
27710                 var targetHeight = this.minHeight - 2 * y;
27711                 
27712                 var scale = 1;
27713                 
27714                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27715                     scale = targetWidth / width;
27716                 }
27717                 
27718                 if(x > 0 && y == 0){
27719                     scale = targetHeight / height;
27720                 }
27721                 
27722                 if(x > 0 && y > 0){
27723                     scale = targetWidth / width;
27724                     
27725                     if(width < height){
27726                         scale = targetHeight / height;
27727                     }
27728                 }
27729                 
27730                 context.scale(scale, scale);
27731                 
27732                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27733                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27734
27735                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27736                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27737
27738                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27739                 
27740                 break;
27741             case 90 : 
27742                 
27743                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27744                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27745                 
27746                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27747                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27748                 
27749                 var targetWidth = this.minWidth - 2 * x;
27750                 var targetHeight = this.minHeight - 2 * y;
27751                 
27752                 var scale = 1;
27753                 
27754                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27755                     scale = targetWidth / width;
27756                 }
27757                 
27758                 if(x > 0 && y == 0){
27759                     scale = targetHeight / height;
27760                 }
27761                 
27762                 if(x > 0 && y > 0){
27763                     scale = targetWidth / width;
27764                     
27765                     if(width < height){
27766                         scale = targetHeight / height;
27767                     }
27768                 }
27769                 
27770                 context.scale(scale, scale);
27771                 
27772                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27773                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27774
27775                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27776                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27777                 
27778                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27779                 
27780                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27781                 
27782                 break;
27783             case 180 :
27784                 
27785                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27786                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27787                 
27788                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27789                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27790                 
27791                 var targetWidth = this.minWidth - 2 * x;
27792                 var targetHeight = this.minHeight - 2 * y;
27793                 
27794                 var scale = 1;
27795                 
27796                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27797                     scale = targetWidth / width;
27798                 }
27799                 
27800                 if(x > 0 && y == 0){
27801                     scale = targetHeight / height;
27802                 }
27803                 
27804                 if(x > 0 && y > 0){
27805                     scale = targetWidth / width;
27806                     
27807                     if(width < height){
27808                         scale = targetHeight / height;
27809                     }
27810                 }
27811                 
27812                 context.scale(scale, scale);
27813                 
27814                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27815                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27816
27817                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27818                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27819
27820                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27821                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27822                 
27823                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27824                 
27825                 break;
27826             case 270 :
27827                 
27828                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27829                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27830                 
27831                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27832                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27833                 
27834                 var targetWidth = this.minWidth - 2 * x;
27835                 var targetHeight = this.minHeight - 2 * y;
27836                 
27837                 var scale = 1;
27838                 
27839                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27840                     scale = targetWidth / width;
27841                 }
27842                 
27843                 if(x > 0 && y == 0){
27844                     scale = targetHeight / height;
27845                 }
27846                 
27847                 if(x > 0 && y > 0){
27848                     scale = targetWidth / width;
27849                     
27850                     if(width < height){
27851                         scale = targetHeight / height;
27852                     }
27853                 }
27854                 
27855                 context.scale(scale, scale);
27856                 
27857                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27858                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27859
27860                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27861                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27862                 
27863                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27864                 
27865                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27866                 
27867                 break;
27868             default : 
27869                 break;
27870         }
27871         
27872         this.cropData = canvas.toDataURL(this.cropType);
27873         
27874         if(this.fireEvent('crop', this, this.cropData) !== false){
27875             this.process(this.file, this.cropData);
27876         }
27877         
27878         return;
27879         
27880     },
27881     
27882     setThumbBoxSize : function()
27883     {
27884         var width, height;
27885         
27886         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27887             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27888             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27889             
27890             this.minWidth = width;
27891             this.minHeight = height;
27892             
27893             if(this.rotate == 90 || this.rotate == 270){
27894                 this.minWidth = height;
27895                 this.minHeight = width;
27896             }
27897         }
27898         
27899         height = 300;
27900         width = Math.ceil(this.minWidth * height / this.minHeight);
27901         
27902         if(this.minWidth > this.minHeight){
27903             width = 300;
27904             height = Math.ceil(this.minHeight * width / this.minWidth);
27905         }
27906         
27907         this.thumbEl.setStyle({
27908             width : width + 'px',
27909             height : height + 'px'
27910         });
27911
27912         return;
27913             
27914     },
27915     
27916     setThumbBoxPosition : function()
27917     {
27918         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27919         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27920         
27921         this.thumbEl.setLeft(x);
27922         this.thumbEl.setTop(y);
27923         
27924     },
27925     
27926     baseRotateLevel : function()
27927     {
27928         this.baseRotate = 1;
27929         
27930         if(
27931                 typeof(this.exif) != 'undefined' &&
27932                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27933                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27934         ){
27935             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27936         }
27937         
27938         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27939         
27940     },
27941     
27942     baseScaleLevel : function()
27943     {
27944         var width, height;
27945         
27946         if(this.isDocument){
27947             
27948             if(this.baseRotate == 6 || this.baseRotate == 8){
27949             
27950                 height = this.thumbEl.getHeight();
27951                 this.baseScale = height / this.imageEl.OriginWidth;
27952
27953                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27954                     width = this.thumbEl.getWidth();
27955                     this.baseScale = width / this.imageEl.OriginHeight;
27956                 }
27957
27958                 return;
27959             }
27960
27961             height = this.thumbEl.getHeight();
27962             this.baseScale = height / this.imageEl.OriginHeight;
27963
27964             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27965                 width = this.thumbEl.getWidth();
27966                 this.baseScale = width / this.imageEl.OriginWidth;
27967             }
27968
27969             return;
27970         }
27971         
27972         if(this.baseRotate == 6 || this.baseRotate == 8){
27973             
27974             width = this.thumbEl.getHeight();
27975             this.baseScale = width / this.imageEl.OriginHeight;
27976             
27977             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27978                 height = this.thumbEl.getWidth();
27979                 this.baseScale = height / this.imageEl.OriginHeight;
27980             }
27981             
27982             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27983                 height = this.thumbEl.getWidth();
27984                 this.baseScale = height / this.imageEl.OriginHeight;
27985                 
27986                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27987                     width = this.thumbEl.getHeight();
27988                     this.baseScale = width / this.imageEl.OriginWidth;
27989                 }
27990             }
27991             
27992             return;
27993         }
27994         
27995         width = this.thumbEl.getWidth();
27996         this.baseScale = width / this.imageEl.OriginWidth;
27997         
27998         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27999             height = this.thumbEl.getHeight();
28000             this.baseScale = height / this.imageEl.OriginHeight;
28001         }
28002         
28003         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28004             
28005             height = this.thumbEl.getHeight();
28006             this.baseScale = height / this.imageEl.OriginHeight;
28007             
28008             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28009                 width = this.thumbEl.getWidth();
28010                 this.baseScale = width / this.imageEl.OriginWidth;
28011             }
28012             
28013         }
28014         
28015         return;
28016     },
28017     
28018     getScaleLevel : function()
28019     {
28020         return this.baseScale * Math.pow(1.1, this.scale);
28021     },
28022     
28023     onTouchStart : function(e)
28024     {
28025         if(!this.canvasLoaded){
28026             this.beforeSelectFile(e);
28027             return;
28028         }
28029         
28030         var touches = e.browserEvent.touches;
28031         
28032         if(!touches){
28033             return;
28034         }
28035         
28036         if(touches.length == 1){
28037             this.onMouseDown(e);
28038             return;
28039         }
28040         
28041         if(touches.length != 2){
28042             return;
28043         }
28044         
28045         var coords = [];
28046         
28047         for(var i = 0, finger; finger = touches[i]; i++){
28048             coords.push(finger.pageX, finger.pageY);
28049         }
28050         
28051         var x = Math.pow(coords[0] - coords[2], 2);
28052         var y = Math.pow(coords[1] - coords[3], 2);
28053         
28054         this.startDistance = Math.sqrt(x + y);
28055         
28056         this.startScale = this.scale;
28057         
28058         this.pinching = true;
28059         this.dragable = false;
28060         
28061     },
28062     
28063     onTouchMove : function(e)
28064     {
28065         if(!this.pinching && !this.dragable){
28066             return;
28067         }
28068         
28069         var touches = e.browserEvent.touches;
28070         
28071         if(!touches){
28072             return;
28073         }
28074         
28075         if(this.dragable){
28076             this.onMouseMove(e);
28077             return;
28078         }
28079         
28080         var coords = [];
28081         
28082         for(var i = 0, finger; finger = touches[i]; i++){
28083             coords.push(finger.pageX, finger.pageY);
28084         }
28085         
28086         var x = Math.pow(coords[0] - coords[2], 2);
28087         var y = Math.pow(coords[1] - coords[3], 2);
28088         
28089         this.endDistance = Math.sqrt(x + y);
28090         
28091         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28092         
28093         if(!this.zoomable()){
28094             this.scale = this.startScale;
28095             return;
28096         }
28097         
28098         this.draw();
28099         
28100     },
28101     
28102     onTouchEnd : function(e)
28103     {
28104         this.pinching = false;
28105         this.dragable = false;
28106         
28107     },
28108     
28109     process : function(file, crop)
28110     {
28111         if(this.loadMask){
28112             this.maskEl.mask(this.loadingText);
28113         }
28114         
28115         this.xhr = new XMLHttpRequest();
28116         
28117         file.xhr = this.xhr;
28118
28119         this.xhr.open(this.method, this.url, true);
28120         
28121         var headers = {
28122             "Accept": "application/json",
28123             "Cache-Control": "no-cache",
28124             "X-Requested-With": "XMLHttpRequest"
28125         };
28126         
28127         for (var headerName in headers) {
28128             var headerValue = headers[headerName];
28129             if (headerValue) {
28130                 this.xhr.setRequestHeader(headerName, headerValue);
28131             }
28132         }
28133         
28134         var _this = this;
28135         
28136         this.xhr.onload = function()
28137         {
28138             _this.xhrOnLoad(_this.xhr);
28139         }
28140         
28141         this.xhr.onerror = function()
28142         {
28143             _this.xhrOnError(_this.xhr);
28144         }
28145         
28146         var formData = new FormData();
28147
28148         formData.append('returnHTML', 'NO');
28149         
28150         if(crop){
28151             formData.append('crop', crop);
28152         }
28153         
28154         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28155             formData.append(this.paramName, file, file.name);
28156         }
28157         
28158         if(typeof(file.filename) != 'undefined'){
28159             formData.append('filename', file.filename);
28160         }
28161         
28162         if(typeof(file.mimetype) != 'undefined'){
28163             formData.append('mimetype', file.mimetype);
28164         }
28165         
28166         if(this.fireEvent('arrange', this, formData) != false){
28167             this.xhr.send(formData);
28168         };
28169     },
28170     
28171     xhrOnLoad : function(xhr)
28172     {
28173         if(this.loadMask){
28174             this.maskEl.unmask();
28175         }
28176         
28177         if (xhr.readyState !== 4) {
28178             this.fireEvent('exception', this, xhr);
28179             return;
28180         }
28181
28182         var response = Roo.decode(xhr.responseText);
28183         
28184         if(!response.success){
28185             this.fireEvent('exception', this, xhr);
28186             return;
28187         }
28188         
28189         var response = Roo.decode(xhr.responseText);
28190         
28191         this.fireEvent('upload', this, response);
28192         
28193     },
28194     
28195     xhrOnError : function()
28196     {
28197         if(this.loadMask){
28198             this.maskEl.unmask();
28199         }
28200         
28201         Roo.log('xhr on error');
28202         
28203         var response = Roo.decode(xhr.responseText);
28204           
28205         Roo.log(response);
28206         
28207     },
28208     
28209     prepare : function(file)
28210     {   
28211         if(this.loadMask){
28212             this.maskEl.mask(this.loadingText);
28213         }
28214         
28215         this.file = false;
28216         this.exif = {};
28217         
28218         if(typeof(file) === 'string'){
28219             this.loadCanvas(file);
28220             return;
28221         }
28222         
28223         if(!file || !this.urlAPI){
28224             return;
28225         }
28226         
28227         this.file = file;
28228         this.cropType = file.type;
28229         
28230         var _this = this;
28231         
28232         if(this.fireEvent('prepare', this, this.file) != false){
28233             
28234             var reader = new FileReader();
28235             
28236             reader.onload = function (e) {
28237                 if (e.target.error) {
28238                     Roo.log(e.target.error);
28239                     return;
28240                 }
28241                 
28242                 var buffer = e.target.result,
28243                     dataView = new DataView(buffer),
28244                     offset = 2,
28245                     maxOffset = dataView.byteLength - 4,
28246                     markerBytes,
28247                     markerLength;
28248                 
28249                 if (dataView.getUint16(0) === 0xffd8) {
28250                     while (offset < maxOffset) {
28251                         markerBytes = dataView.getUint16(offset);
28252                         
28253                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28254                             markerLength = dataView.getUint16(offset + 2) + 2;
28255                             if (offset + markerLength > dataView.byteLength) {
28256                                 Roo.log('Invalid meta data: Invalid segment size.');
28257                                 break;
28258                             }
28259                             
28260                             if(markerBytes == 0xffe1){
28261                                 _this.parseExifData(
28262                                     dataView,
28263                                     offset,
28264                                     markerLength
28265                                 );
28266                             }
28267                             
28268                             offset += markerLength;
28269                             
28270                             continue;
28271                         }
28272                         
28273                         break;
28274                     }
28275                     
28276                 }
28277                 
28278                 var url = _this.urlAPI.createObjectURL(_this.file);
28279                 
28280                 _this.loadCanvas(url);
28281                 
28282                 return;
28283             }
28284             
28285             reader.readAsArrayBuffer(this.file);
28286             
28287         }
28288         
28289     },
28290     
28291     parseExifData : function(dataView, offset, length)
28292     {
28293         var tiffOffset = offset + 10,
28294             littleEndian,
28295             dirOffset;
28296     
28297         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28298             // No Exif data, might be XMP data instead
28299             return;
28300         }
28301         
28302         // Check for the ASCII code for "Exif" (0x45786966):
28303         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28304             // No Exif data, might be XMP data instead
28305             return;
28306         }
28307         if (tiffOffset + 8 > dataView.byteLength) {
28308             Roo.log('Invalid Exif data: Invalid segment size.');
28309             return;
28310         }
28311         // Check for the two null bytes:
28312         if (dataView.getUint16(offset + 8) !== 0x0000) {
28313             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28314             return;
28315         }
28316         // Check the byte alignment:
28317         switch (dataView.getUint16(tiffOffset)) {
28318         case 0x4949:
28319             littleEndian = true;
28320             break;
28321         case 0x4D4D:
28322             littleEndian = false;
28323             break;
28324         default:
28325             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28326             return;
28327         }
28328         // Check for the TIFF tag marker (0x002A):
28329         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28330             Roo.log('Invalid Exif data: Missing TIFF marker.');
28331             return;
28332         }
28333         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28334         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28335         
28336         this.parseExifTags(
28337             dataView,
28338             tiffOffset,
28339             tiffOffset + dirOffset,
28340             littleEndian
28341         );
28342     },
28343     
28344     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28345     {
28346         var tagsNumber,
28347             dirEndOffset,
28348             i;
28349         if (dirOffset + 6 > dataView.byteLength) {
28350             Roo.log('Invalid Exif data: Invalid directory offset.');
28351             return;
28352         }
28353         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28354         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28355         if (dirEndOffset + 4 > dataView.byteLength) {
28356             Roo.log('Invalid Exif data: Invalid directory size.');
28357             return;
28358         }
28359         for (i = 0; i < tagsNumber; i += 1) {
28360             this.parseExifTag(
28361                 dataView,
28362                 tiffOffset,
28363                 dirOffset + 2 + 12 * i, // tag offset
28364                 littleEndian
28365             );
28366         }
28367         // Return the offset to the next directory:
28368         return dataView.getUint32(dirEndOffset, littleEndian);
28369     },
28370     
28371     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28372     {
28373         var tag = dataView.getUint16(offset, littleEndian);
28374         
28375         this.exif[tag] = this.getExifValue(
28376             dataView,
28377             tiffOffset,
28378             offset,
28379             dataView.getUint16(offset + 2, littleEndian), // tag type
28380             dataView.getUint32(offset + 4, littleEndian), // tag length
28381             littleEndian
28382         );
28383     },
28384     
28385     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28386     {
28387         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28388             tagSize,
28389             dataOffset,
28390             values,
28391             i,
28392             str,
28393             c;
28394     
28395         if (!tagType) {
28396             Roo.log('Invalid Exif data: Invalid tag type.');
28397             return;
28398         }
28399         
28400         tagSize = tagType.size * length;
28401         // Determine if the value is contained in the dataOffset bytes,
28402         // or if the value at the dataOffset is a pointer to the actual data:
28403         dataOffset = tagSize > 4 ?
28404                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28405         if (dataOffset + tagSize > dataView.byteLength) {
28406             Roo.log('Invalid Exif data: Invalid data offset.');
28407             return;
28408         }
28409         if (length === 1) {
28410             return tagType.getValue(dataView, dataOffset, littleEndian);
28411         }
28412         values = [];
28413         for (i = 0; i < length; i += 1) {
28414             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28415         }
28416         
28417         if (tagType.ascii) {
28418             str = '';
28419             // Concatenate the chars:
28420             for (i = 0; i < values.length; i += 1) {
28421                 c = values[i];
28422                 // Ignore the terminating NULL byte(s):
28423                 if (c === '\u0000') {
28424                     break;
28425                 }
28426                 str += c;
28427             }
28428             return str;
28429         }
28430         return values;
28431     }
28432     
28433 });
28434
28435 Roo.apply(Roo.bootstrap.UploadCropbox, {
28436     tags : {
28437         'Orientation': 0x0112
28438     },
28439     
28440     Orientation: {
28441             1: 0, //'top-left',
28442 //            2: 'top-right',
28443             3: 180, //'bottom-right',
28444 //            4: 'bottom-left',
28445 //            5: 'left-top',
28446             6: 90, //'right-top',
28447 //            7: 'right-bottom',
28448             8: 270 //'left-bottom'
28449     },
28450     
28451     exifTagTypes : {
28452         // byte, 8-bit unsigned int:
28453         1: {
28454             getValue: function (dataView, dataOffset) {
28455                 return dataView.getUint8(dataOffset);
28456             },
28457             size: 1
28458         },
28459         // ascii, 8-bit byte:
28460         2: {
28461             getValue: function (dataView, dataOffset) {
28462                 return String.fromCharCode(dataView.getUint8(dataOffset));
28463             },
28464             size: 1,
28465             ascii: true
28466         },
28467         // short, 16 bit int:
28468         3: {
28469             getValue: function (dataView, dataOffset, littleEndian) {
28470                 return dataView.getUint16(dataOffset, littleEndian);
28471             },
28472             size: 2
28473         },
28474         // long, 32 bit int:
28475         4: {
28476             getValue: function (dataView, dataOffset, littleEndian) {
28477                 return dataView.getUint32(dataOffset, littleEndian);
28478             },
28479             size: 4
28480         },
28481         // rational = two long values, first is numerator, second is denominator:
28482         5: {
28483             getValue: function (dataView, dataOffset, littleEndian) {
28484                 return dataView.getUint32(dataOffset, littleEndian) /
28485                     dataView.getUint32(dataOffset + 4, littleEndian);
28486             },
28487             size: 8
28488         },
28489         // slong, 32 bit signed int:
28490         9: {
28491             getValue: function (dataView, dataOffset, littleEndian) {
28492                 return dataView.getInt32(dataOffset, littleEndian);
28493             },
28494             size: 4
28495         },
28496         // srational, two slongs, first is numerator, second is denominator:
28497         10: {
28498             getValue: function (dataView, dataOffset, littleEndian) {
28499                 return dataView.getInt32(dataOffset, littleEndian) /
28500                     dataView.getInt32(dataOffset + 4, littleEndian);
28501             },
28502             size: 8
28503         }
28504     },
28505     
28506     footer : {
28507         STANDARD : [
28508             {
28509                 tag : 'div',
28510                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28511                 action : 'rotate-left',
28512                 cn : [
28513                     {
28514                         tag : 'button',
28515                         cls : 'btn btn-default',
28516                         html : '<i class="fa fa-undo"></i>'
28517                     }
28518                 ]
28519             },
28520             {
28521                 tag : 'div',
28522                 cls : 'btn-group roo-upload-cropbox-picture',
28523                 action : 'picture',
28524                 cn : [
28525                     {
28526                         tag : 'button',
28527                         cls : 'btn btn-default',
28528                         html : '<i class="fa fa-picture-o"></i>'
28529                     }
28530                 ]
28531             },
28532             {
28533                 tag : 'div',
28534                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28535                 action : 'rotate-right',
28536                 cn : [
28537                     {
28538                         tag : 'button',
28539                         cls : 'btn btn-default',
28540                         html : '<i class="fa fa-repeat"></i>'
28541                     }
28542                 ]
28543             }
28544         ],
28545         DOCUMENT : [
28546             {
28547                 tag : 'div',
28548                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28549                 action : 'rotate-left',
28550                 cn : [
28551                     {
28552                         tag : 'button',
28553                         cls : 'btn btn-default',
28554                         html : '<i class="fa fa-undo"></i>'
28555                     }
28556                 ]
28557             },
28558             {
28559                 tag : 'div',
28560                 cls : 'btn-group roo-upload-cropbox-download',
28561                 action : 'download',
28562                 cn : [
28563                     {
28564                         tag : 'button',
28565                         cls : 'btn btn-default',
28566                         html : '<i class="fa fa-download"></i>'
28567                     }
28568                 ]
28569             },
28570             {
28571                 tag : 'div',
28572                 cls : 'btn-group roo-upload-cropbox-crop',
28573                 action : 'crop',
28574                 cn : [
28575                     {
28576                         tag : 'button',
28577                         cls : 'btn btn-default',
28578                         html : '<i class="fa fa-crop"></i>'
28579                     }
28580                 ]
28581             },
28582             {
28583                 tag : 'div',
28584                 cls : 'btn-group roo-upload-cropbox-trash',
28585                 action : 'trash',
28586                 cn : [
28587                     {
28588                         tag : 'button',
28589                         cls : 'btn btn-default',
28590                         html : '<i class="fa fa-trash"></i>'
28591                     }
28592                 ]
28593             },
28594             {
28595                 tag : 'div',
28596                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28597                 action : 'rotate-right',
28598                 cn : [
28599                     {
28600                         tag : 'button',
28601                         cls : 'btn btn-default',
28602                         html : '<i class="fa fa-repeat"></i>'
28603                     }
28604                 ]
28605             }
28606         ],
28607         ROTATOR : [
28608             {
28609                 tag : 'div',
28610                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28611                 action : 'rotate-left',
28612                 cn : [
28613                     {
28614                         tag : 'button',
28615                         cls : 'btn btn-default',
28616                         html : '<i class="fa fa-undo"></i>'
28617                     }
28618                 ]
28619             },
28620             {
28621                 tag : 'div',
28622                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28623                 action : 'rotate-right',
28624                 cn : [
28625                     {
28626                         tag : 'button',
28627                         cls : 'btn btn-default',
28628                         html : '<i class="fa fa-repeat"></i>'
28629                     }
28630                 ]
28631             }
28632         ]
28633     }
28634 });
28635
28636 /*
28637 * Licence: LGPL
28638 */
28639
28640 /**
28641  * @class Roo.bootstrap.DocumentManager
28642  * @extends Roo.bootstrap.Component
28643  * Bootstrap DocumentManager class
28644  * @cfg {String} paramName default 'imageUpload'
28645  * @cfg {String} toolTipName default 'filename'
28646  * @cfg {String} method default POST
28647  * @cfg {String} url action url
28648  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28649  * @cfg {Boolean} multiple multiple upload default true
28650  * @cfg {Number} thumbSize default 300
28651  * @cfg {String} fieldLabel
28652  * @cfg {Number} labelWidth default 4
28653  * @cfg {String} labelAlign (left|top) default left
28654  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28655 * @cfg {Number} labellg set the width of label (1-12)
28656  * @cfg {Number} labelmd set the width of label (1-12)
28657  * @cfg {Number} labelsm set the width of label (1-12)
28658  * @cfg {Number} labelxs set the width of label (1-12)
28659  * 
28660  * @constructor
28661  * Create a new DocumentManager
28662  * @param {Object} config The config object
28663  */
28664
28665 Roo.bootstrap.DocumentManager = function(config){
28666     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28667     
28668     this.files = [];
28669     this.delegates = [];
28670     
28671     this.addEvents({
28672         /**
28673          * @event initial
28674          * Fire when initial the DocumentManager
28675          * @param {Roo.bootstrap.DocumentManager} this
28676          */
28677         "initial" : true,
28678         /**
28679          * @event inspect
28680          * inspect selected file
28681          * @param {Roo.bootstrap.DocumentManager} this
28682          * @param {File} file
28683          */
28684         "inspect" : true,
28685         /**
28686          * @event exception
28687          * Fire when xhr load exception
28688          * @param {Roo.bootstrap.DocumentManager} this
28689          * @param {XMLHttpRequest} xhr
28690          */
28691         "exception" : true,
28692         /**
28693          * @event afterupload
28694          * Fire when xhr load exception
28695          * @param {Roo.bootstrap.DocumentManager} this
28696          * @param {XMLHttpRequest} xhr
28697          */
28698         "afterupload" : true,
28699         /**
28700          * @event prepare
28701          * prepare the form data
28702          * @param {Roo.bootstrap.DocumentManager} this
28703          * @param {Object} formData
28704          */
28705         "prepare" : true,
28706         /**
28707          * @event remove
28708          * Fire when remove the file
28709          * @param {Roo.bootstrap.DocumentManager} this
28710          * @param {Object} file
28711          */
28712         "remove" : true,
28713         /**
28714          * @event refresh
28715          * Fire after refresh the file
28716          * @param {Roo.bootstrap.DocumentManager} this
28717          */
28718         "refresh" : true,
28719         /**
28720          * @event click
28721          * Fire after click the image
28722          * @param {Roo.bootstrap.DocumentManager} this
28723          * @param {Object} file
28724          */
28725         "click" : true,
28726         /**
28727          * @event edit
28728          * Fire when upload a image and editable set to true
28729          * @param {Roo.bootstrap.DocumentManager} this
28730          * @param {Object} file
28731          */
28732         "edit" : true,
28733         /**
28734          * @event beforeselectfile
28735          * Fire before select file
28736          * @param {Roo.bootstrap.DocumentManager} this
28737          */
28738         "beforeselectfile" : true,
28739         /**
28740          * @event process
28741          * Fire before process file
28742          * @param {Roo.bootstrap.DocumentManager} this
28743          * @param {Object} file
28744          */
28745         "process" : true,
28746         /**
28747          * @event previewrendered
28748          * Fire when preview rendered
28749          * @param {Roo.bootstrap.DocumentManager} this
28750          * @param {Object} file
28751          */
28752         "previewrendered" : true,
28753         /**
28754          */
28755         "previewResize" : true
28756         
28757     });
28758 };
28759
28760 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28761     
28762     boxes : 0,
28763     inputName : '',
28764     thumbSize : 300,
28765     multiple : true,
28766     files : false,
28767     method : 'POST',
28768     url : '',
28769     paramName : 'imageUpload',
28770     toolTipName : 'filename',
28771     fieldLabel : '',
28772     labelWidth : 4,
28773     labelAlign : 'left',
28774     editable : true,
28775     delegates : false,
28776     xhr : false, 
28777     
28778     labellg : 0,
28779     labelmd : 0,
28780     labelsm : 0,
28781     labelxs : 0,
28782     
28783     getAutoCreate : function()
28784     {   
28785         var managerWidget = {
28786             tag : 'div',
28787             cls : 'roo-document-manager',
28788             cn : [
28789                 {
28790                     tag : 'input',
28791                     cls : 'roo-document-manager-selector',
28792                     type : 'file'
28793                 },
28794                 {
28795                     tag : 'div',
28796                     cls : 'roo-document-manager-uploader',
28797                     cn : [
28798                         {
28799                             tag : 'div',
28800                             cls : 'roo-document-manager-upload-btn',
28801                             html : '<i class="fa fa-plus"></i>'
28802                         }
28803                     ]
28804                     
28805                 }
28806             ]
28807         };
28808         
28809         var content = [
28810             {
28811                 tag : 'div',
28812                 cls : 'column col-md-12',
28813                 cn : managerWidget
28814             }
28815         ];
28816         
28817         if(this.fieldLabel.length){
28818             
28819             content = [
28820                 {
28821                     tag : 'div',
28822                     cls : 'column col-md-12',
28823                     html : this.fieldLabel
28824                 },
28825                 {
28826                     tag : 'div',
28827                     cls : 'column col-md-12',
28828                     cn : managerWidget
28829                 }
28830             ];
28831
28832             if(this.labelAlign == 'left'){
28833                 content = [
28834                     {
28835                         tag : 'div',
28836                         cls : 'column',
28837                         html : this.fieldLabel
28838                     },
28839                     {
28840                         tag : 'div',
28841                         cls : 'column',
28842                         cn : managerWidget
28843                     }
28844                 ];
28845                 
28846                 if(this.labelWidth > 12){
28847                     content[0].style = "width: " + this.labelWidth + 'px';
28848                 }
28849
28850                 if(this.labelWidth < 13 && this.labelmd == 0){
28851                     this.labelmd = this.labelWidth;
28852                 }
28853
28854                 if(this.labellg > 0){
28855                     content[0].cls += ' col-lg-' + this.labellg;
28856                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28857                 }
28858
28859                 if(this.labelmd > 0){
28860                     content[0].cls += ' col-md-' + this.labelmd;
28861                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28862                 }
28863
28864                 if(this.labelsm > 0){
28865                     content[0].cls += ' col-sm-' + this.labelsm;
28866                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28867                 }
28868
28869                 if(this.labelxs > 0){
28870                     content[0].cls += ' col-xs-' + this.labelxs;
28871                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28872                 }
28873                 
28874             }
28875         }
28876         
28877         var cfg = {
28878             tag : 'div',
28879             cls : 'row clearfix',
28880             cn : content
28881         };
28882         
28883         return cfg;
28884         
28885     },
28886     
28887     initEvents : function()
28888     {
28889         this.managerEl = this.el.select('.roo-document-manager', true).first();
28890         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28891         
28892         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28893         this.selectorEl.hide();
28894         
28895         if(this.multiple){
28896             this.selectorEl.attr('multiple', 'multiple');
28897         }
28898         
28899         this.selectorEl.on('change', this.onFileSelected, this);
28900         
28901         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28902         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28903         
28904         this.uploader.on('click', this.onUploaderClick, this);
28905         
28906         this.renderProgressDialog();
28907         
28908         var _this = this;
28909         
28910         window.addEventListener("resize", function() { _this.refresh(); } );
28911         
28912         this.fireEvent('initial', this);
28913     },
28914     
28915     renderProgressDialog : function()
28916     {
28917         var _this = this;
28918         
28919         this.progressDialog = new Roo.bootstrap.Modal({
28920             cls : 'roo-document-manager-progress-dialog',
28921             allow_close : false,
28922             title : '',
28923             buttons : [
28924                 {
28925                     name  :'cancel',
28926                     weight : 'danger',
28927                     html : 'Cancel'
28928                 }
28929             ], 
28930             listeners : { 
28931                 btnclick : function() {
28932                     _this.uploadCancel();
28933                     this.hide();
28934                 }
28935             }
28936         });
28937          
28938         this.progressDialog.render(Roo.get(document.body));
28939          
28940         this.progress = new Roo.bootstrap.Progress({
28941             cls : 'roo-document-manager-progress',
28942             active : true,
28943             striped : true
28944         });
28945         
28946         this.progress.render(this.progressDialog.getChildContainer());
28947         
28948         this.progressBar = new Roo.bootstrap.ProgressBar({
28949             cls : 'roo-document-manager-progress-bar',
28950             aria_valuenow : 0,
28951             aria_valuemin : 0,
28952             aria_valuemax : 12,
28953             panel : 'success'
28954         });
28955         
28956         this.progressBar.render(this.progress.getChildContainer());
28957     },
28958     
28959     onUploaderClick : function(e)
28960     {
28961         e.preventDefault();
28962      
28963         if(this.fireEvent('beforeselectfile', this) != false){
28964             this.selectorEl.dom.click();
28965         }
28966         
28967     },
28968     
28969     onFileSelected : function(e)
28970     {
28971         e.preventDefault();
28972         
28973         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28974             return;
28975         }
28976         
28977         Roo.each(this.selectorEl.dom.files, function(file){
28978             if(this.fireEvent('inspect', this, file) != false){
28979                 this.files.push(file);
28980             }
28981         }, this);
28982         
28983         this.queue();
28984         
28985     },
28986     
28987     queue : function()
28988     {
28989         this.selectorEl.dom.value = '';
28990         
28991         if(!this.files || !this.files.length){
28992             return;
28993         }
28994         
28995         if(this.boxes > 0 && this.files.length > this.boxes){
28996             this.files = this.files.slice(0, this.boxes);
28997         }
28998         
28999         this.uploader.show();
29000         
29001         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29002             this.uploader.hide();
29003         }
29004         
29005         var _this = this;
29006         
29007         var files = [];
29008         
29009         var docs = [];
29010         
29011         Roo.each(this.files, function(file){
29012             
29013             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29014                 var f = this.renderPreview(file);
29015                 files.push(f);
29016                 return;
29017             }
29018             
29019             if(file.type.indexOf('image') != -1){
29020                 this.delegates.push(
29021                     (function(){
29022                         _this.process(file);
29023                     }).createDelegate(this)
29024                 );
29025         
29026                 return;
29027             }
29028             
29029             docs.push(
29030                 (function(){
29031                     _this.process(file);
29032                 }).createDelegate(this)
29033             );
29034             
29035         }, this);
29036         
29037         this.files = files;
29038         
29039         this.delegates = this.delegates.concat(docs);
29040         
29041         if(!this.delegates.length){
29042             this.refresh();
29043             return;
29044         }
29045         
29046         this.progressBar.aria_valuemax = this.delegates.length;
29047         
29048         this.arrange();
29049         
29050         return;
29051     },
29052     
29053     arrange : function()
29054     {
29055         if(!this.delegates.length){
29056             this.progressDialog.hide();
29057             this.refresh();
29058             return;
29059         }
29060         
29061         var delegate = this.delegates.shift();
29062         
29063         this.progressDialog.show();
29064         
29065         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29066         
29067         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29068         
29069         delegate();
29070     },
29071     
29072     refresh : function()
29073     {
29074         this.uploader.show();
29075         
29076         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29077             this.uploader.hide();
29078         }
29079         
29080         Roo.isTouch ? this.closable(false) : this.closable(true);
29081         
29082         this.fireEvent('refresh', this);
29083     },
29084     
29085     onRemove : function(e, el, o)
29086     {
29087         e.preventDefault();
29088         
29089         this.fireEvent('remove', this, o);
29090         
29091     },
29092     
29093     remove : function(o)
29094     {
29095         var files = [];
29096         
29097         Roo.each(this.files, function(file){
29098             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29099                 files.push(file);
29100                 return;
29101             }
29102
29103             o.target.remove();
29104
29105         }, this);
29106         
29107         this.files = files;
29108         
29109         this.refresh();
29110     },
29111     
29112     clear : function()
29113     {
29114         Roo.each(this.files, function(file){
29115             if(!file.target){
29116                 return;
29117             }
29118             
29119             file.target.remove();
29120
29121         }, this);
29122         
29123         this.files = [];
29124         
29125         this.refresh();
29126     },
29127     
29128     onClick : function(e, el, o)
29129     {
29130         e.preventDefault();
29131         
29132         this.fireEvent('click', this, o);
29133         
29134     },
29135     
29136     closable : function(closable)
29137     {
29138         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29139             
29140             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29141             
29142             if(closable){
29143                 el.show();
29144                 return;
29145             }
29146             
29147             el.hide();
29148             
29149         }, this);
29150     },
29151     
29152     xhrOnLoad : function(xhr)
29153     {
29154         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29155             el.remove();
29156         }, this);
29157         
29158         if (xhr.readyState !== 4) {
29159             this.arrange();
29160             this.fireEvent('exception', this, xhr);
29161             return;
29162         }
29163
29164         var response = Roo.decode(xhr.responseText);
29165         
29166         if(!response.success){
29167             this.arrange();
29168             this.fireEvent('exception', this, xhr);
29169             return;
29170         }
29171         
29172         var file = this.renderPreview(response.data);
29173         
29174         this.files.push(file);
29175         
29176         this.arrange();
29177         
29178         this.fireEvent('afterupload', this, xhr);
29179         
29180     },
29181     
29182     xhrOnError : function(xhr)
29183     {
29184         Roo.log('xhr on error');
29185         
29186         var response = Roo.decode(xhr.responseText);
29187           
29188         Roo.log(response);
29189         
29190         this.arrange();
29191     },
29192     
29193     process : function(file)
29194     {
29195         if(this.fireEvent('process', this, file) !== false){
29196             if(this.editable && file.type.indexOf('image') != -1){
29197                 this.fireEvent('edit', this, file);
29198                 return;
29199             }
29200
29201             this.uploadStart(file, false);
29202
29203             return;
29204         }
29205         
29206     },
29207     
29208     uploadStart : function(file, crop)
29209     {
29210         this.xhr = new XMLHttpRequest();
29211         
29212         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29213             this.arrange();
29214             return;
29215         }
29216         
29217         file.xhr = this.xhr;
29218             
29219         this.managerEl.createChild({
29220             tag : 'div',
29221             cls : 'roo-document-manager-loading',
29222             cn : [
29223                 {
29224                     tag : 'div',
29225                     tooltip : file.name,
29226                     cls : 'roo-document-manager-thumb',
29227                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29228                 }
29229             ]
29230
29231         });
29232
29233         this.xhr.open(this.method, this.url, true);
29234         
29235         var headers = {
29236             "Accept": "application/json",
29237             "Cache-Control": "no-cache",
29238             "X-Requested-With": "XMLHttpRequest"
29239         };
29240         
29241         for (var headerName in headers) {
29242             var headerValue = headers[headerName];
29243             if (headerValue) {
29244                 this.xhr.setRequestHeader(headerName, headerValue);
29245             }
29246         }
29247         
29248         var _this = this;
29249         
29250         this.xhr.onload = function()
29251         {
29252             _this.xhrOnLoad(_this.xhr);
29253         }
29254         
29255         this.xhr.onerror = function()
29256         {
29257             _this.xhrOnError(_this.xhr);
29258         }
29259         
29260         var formData = new FormData();
29261
29262         formData.append('returnHTML', 'NO');
29263         
29264         if(crop){
29265             formData.append('crop', crop);
29266         }
29267         
29268         formData.append(this.paramName, file, file.name);
29269         
29270         var options = {
29271             file : file, 
29272             manually : false
29273         };
29274         
29275         if(this.fireEvent('prepare', this, formData, options) != false){
29276             
29277             if(options.manually){
29278                 return;
29279             }
29280             
29281             this.xhr.send(formData);
29282             return;
29283         };
29284         
29285         this.uploadCancel();
29286     },
29287     
29288     uploadCancel : function()
29289     {
29290         if (this.xhr) {
29291             this.xhr.abort();
29292         }
29293         
29294         this.delegates = [];
29295         
29296         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29297             el.remove();
29298         }, this);
29299         
29300         this.arrange();
29301     },
29302     
29303     renderPreview : function(file)
29304     {
29305         if(typeof(file.target) != 'undefined' && file.target){
29306             return file;
29307         }
29308         
29309         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29310         
29311         var previewEl = this.managerEl.createChild({
29312             tag : 'div',
29313             cls : 'roo-document-manager-preview',
29314             cn : [
29315                 {
29316                     tag : 'div',
29317                     tooltip : file[this.toolTipName],
29318                     cls : 'roo-document-manager-thumb',
29319                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29320                 },
29321                 {
29322                     tag : 'button',
29323                     cls : 'close',
29324                     html : '<i class="fa fa-times-circle"></i>'
29325                 }
29326             ]
29327         });
29328
29329         var close = previewEl.select('button.close', true).first();
29330
29331         close.on('click', this.onRemove, this, file);
29332
29333         file.target = previewEl;
29334
29335         var image = previewEl.select('img', true).first();
29336         
29337         var _this = this;
29338         
29339         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29340         
29341         image.on('click', this.onClick, this, file);
29342         
29343         this.fireEvent('previewrendered', this, file);
29344         
29345         return file;
29346         
29347     },
29348     
29349     onPreviewLoad : function(file, image)
29350     {
29351         if(typeof(file.target) == 'undefined' || !file.target){
29352             return;
29353         }
29354         
29355         var width = image.dom.naturalWidth || image.dom.width;
29356         var height = image.dom.naturalHeight || image.dom.height;
29357         
29358         if(!this.previewResize) {
29359             return;
29360         }
29361         
29362         if(width > height){
29363             file.target.addClass('wide');
29364             return;
29365         }
29366         
29367         file.target.addClass('tall');
29368         return;
29369         
29370     },
29371     
29372     uploadFromSource : function(file, crop)
29373     {
29374         this.xhr = new XMLHttpRequest();
29375         
29376         this.managerEl.createChild({
29377             tag : 'div',
29378             cls : 'roo-document-manager-loading',
29379             cn : [
29380                 {
29381                     tag : 'div',
29382                     tooltip : file.name,
29383                     cls : 'roo-document-manager-thumb',
29384                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29385                 }
29386             ]
29387
29388         });
29389
29390         this.xhr.open(this.method, this.url, true);
29391         
29392         var headers = {
29393             "Accept": "application/json",
29394             "Cache-Control": "no-cache",
29395             "X-Requested-With": "XMLHttpRequest"
29396         };
29397         
29398         for (var headerName in headers) {
29399             var headerValue = headers[headerName];
29400             if (headerValue) {
29401                 this.xhr.setRequestHeader(headerName, headerValue);
29402             }
29403         }
29404         
29405         var _this = this;
29406         
29407         this.xhr.onload = function()
29408         {
29409             _this.xhrOnLoad(_this.xhr);
29410         }
29411         
29412         this.xhr.onerror = function()
29413         {
29414             _this.xhrOnError(_this.xhr);
29415         }
29416         
29417         var formData = new FormData();
29418
29419         formData.append('returnHTML', 'NO');
29420         
29421         formData.append('crop', crop);
29422         
29423         if(typeof(file.filename) != 'undefined'){
29424             formData.append('filename', file.filename);
29425         }
29426         
29427         if(typeof(file.mimetype) != 'undefined'){
29428             formData.append('mimetype', file.mimetype);
29429         }
29430         
29431         Roo.log(formData);
29432         
29433         if(this.fireEvent('prepare', this, formData) != false){
29434             this.xhr.send(formData);
29435         };
29436     }
29437 });
29438
29439 /*
29440 * Licence: LGPL
29441 */
29442
29443 /**
29444  * @class Roo.bootstrap.DocumentViewer
29445  * @extends Roo.bootstrap.Component
29446  * Bootstrap DocumentViewer class
29447  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29448  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29449  * 
29450  * @constructor
29451  * Create a new DocumentViewer
29452  * @param {Object} config The config object
29453  */
29454
29455 Roo.bootstrap.DocumentViewer = function(config){
29456     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29457     
29458     this.addEvents({
29459         /**
29460          * @event initial
29461          * Fire after initEvent
29462          * @param {Roo.bootstrap.DocumentViewer} this
29463          */
29464         "initial" : true,
29465         /**
29466          * @event click
29467          * Fire after click
29468          * @param {Roo.bootstrap.DocumentViewer} this
29469          */
29470         "click" : true,
29471         /**
29472          * @event download
29473          * Fire after download button
29474          * @param {Roo.bootstrap.DocumentViewer} this
29475          */
29476         "download" : true,
29477         /**
29478          * @event trash
29479          * Fire after trash button
29480          * @param {Roo.bootstrap.DocumentViewer} this
29481          */
29482         "trash" : true
29483         
29484     });
29485 };
29486
29487 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29488     
29489     showDownload : true,
29490     
29491     showTrash : true,
29492     
29493     getAutoCreate : function()
29494     {
29495         var cfg = {
29496             tag : 'div',
29497             cls : 'roo-document-viewer',
29498             cn : [
29499                 {
29500                     tag : 'div',
29501                     cls : 'roo-document-viewer-body',
29502                     cn : [
29503                         {
29504                             tag : 'div',
29505                             cls : 'roo-document-viewer-thumb',
29506                             cn : [
29507                                 {
29508                                     tag : 'img',
29509                                     cls : 'roo-document-viewer-image'
29510                                 }
29511                             ]
29512                         }
29513                     ]
29514                 },
29515                 {
29516                     tag : 'div',
29517                     cls : 'roo-document-viewer-footer',
29518                     cn : {
29519                         tag : 'div',
29520                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29521                         cn : [
29522                             {
29523                                 tag : 'div',
29524                                 cls : 'btn-group roo-document-viewer-download',
29525                                 cn : [
29526                                     {
29527                                         tag : 'button',
29528                                         cls : 'btn btn-default',
29529                                         html : '<i class="fa fa-download"></i>'
29530                                     }
29531                                 ]
29532                             },
29533                             {
29534                                 tag : 'div',
29535                                 cls : 'btn-group roo-document-viewer-trash',
29536                                 cn : [
29537                                     {
29538                                         tag : 'button',
29539                                         cls : 'btn btn-default',
29540                                         html : '<i class="fa fa-trash"></i>'
29541                                     }
29542                                 ]
29543                             }
29544                         ]
29545                     }
29546                 }
29547             ]
29548         };
29549         
29550         return cfg;
29551     },
29552     
29553     initEvents : function()
29554     {
29555         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29556         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29557         
29558         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29559         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29560         
29561         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29562         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29563         
29564         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29565         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29566         
29567         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29568         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29569         
29570         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29571         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29572         
29573         this.bodyEl.on('click', this.onClick, this);
29574         this.downloadBtn.on('click', this.onDownload, this);
29575         this.trashBtn.on('click', this.onTrash, this);
29576         
29577         this.downloadBtn.hide();
29578         this.trashBtn.hide();
29579         
29580         if(this.showDownload){
29581             this.downloadBtn.show();
29582         }
29583         
29584         if(this.showTrash){
29585             this.trashBtn.show();
29586         }
29587         
29588         if(!this.showDownload && !this.showTrash) {
29589             this.footerEl.hide();
29590         }
29591         
29592     },
29593     
29594     initial : function()
29595     {
29596         this.fireEvent('initial', this);
29597         
29598     },
29599     
29600     onClick : function(e)
29601     {
29602         e.preventDefault();
29603         
29604         this.fireEvent('click', this);
29605     },
29606     
29607     onDownload : function(e)
29608     {
29609         e.preventDefault();
29610         
29611         this.fireEvent('download', this);
29612     },
29613     
29614     onTrash : function(e)
29615     {
29616         e.preventDefault();
29617         
29618         this.fireEvent('trash', this);
29619     }
29620     
29621 });
29622 /*
29623  * - LGPL
29624  *
29625  * nav progress bar
29626  * 
29627  */
29628
29629 /**
29630  * @class Roo.bootstrap.NavProgressBar
29631  * @extends Roo.bootstrap.Component
29632  * Bootstrap NavProgressBar class
29633  * 
29634  * @constructor
29635  * Create a new nav progress bar
29636  * @param {Object} config The config object
29637  */
29638
29639 Roo.bootstrap.NavProgressBar = function(config){
29640     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29641
29642     this.bullets = this.bullets || [];
29643    
29644 //    Roo.bootstrap.NavProgressBar.register(this);
29645      this.addEvents({
29646         /**
29647              * @event changed
29648              * Fires when the active item changes
29649              * @param {Roo.bootstrap.NavProgressBar} this
29650              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29651              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29652          */
29653         'changed': true
29654      });
29655     
29656 };
29657
29658 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29659     
29660     bullets : [],
29661     barItems : [],
29662     
29663     getAutoCreate : function()
29664     {
29665         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29666         
29667         cfg = {
29668             tag : 'div',
29669             cls : 'roo-navigation-bar-group',
29670             cn : [
29671                 {
29672                     tag : 'div',
29673                     cls : 'roo-navigation-top-bar'
29674                 },
29675                 {
29676                     tag : 'div',
29677                     cls : 'roo-navigation-bullets-bar',
29678                     cn : [
29679                         {
29680                             tag : 'ul',
29681                             cls : 'roo-navigation-bar'
29682                         }
29683                     ]
29684                 },
29685                 
29686                 {
29687                     tag : 'div',
29688                     cls : 'roo-navigation-bottom-bar'
29689                 }
29690             ]
29691             
29692         };
29693         
29694         return cfg;
29695         
29696     },
29697     
29698     initEvents: function() 
29699     {
29700         
29701     },
29702     
29703     onRender : function(ct, position) 
29704     {
29705         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29706         
29707         if(this.bullets.length){
29708             Roo.each(this.bullets, function(b){
29709                this.addItem(b);
29710             }, this);
29711         }
29712         
29713         this.format();
29714         
29715     },
29716     
29717     addItem : function(cfg)
29718     {
29719         var item = new Roo.bootstrap.NavProgressItem(cfg);
29720         
29721         item.parentId = this.id;
29722         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29723         
29724         if(cfg.html){
29725             var top = new Roo.bootstrap.Element({
29726                 tag : 'div',
29727                 cls : 'roo-navigation-bar-text'
29728             });
29729             
29730             var bottom = new Roo.bootstrap.Element({
29731                 tag : 'div',
29732                 cls : 'roo-navigation-bar-text'
29733             });
29734             
29735             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29736             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29737             
29738             var topText = new Roo.bootstrap.Element({
29739                 tag : 'span',
29740                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29741             });
29742             
29743             var bottomText = new Roo.bootstrap.Element({
29744                 tag : 'span',
29745                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29746             });
29747             
29748             topText.onRender(top.el, null);
29749             bottomText.onRender(bottom.el, null);
29750             
29751             item.topEl = top;
29752             item.bottomEl = bottom;
29753         }
29754         
29755         this.barItems.push(item);
29756         
29757         return item;
29758     },
29759     
29760     getActive : function()
29761     {
29762         var active = false;
29763         
29764         Roo.each(this.barItems, function(v){
29765             
29766             if (!v.isActive()) {
29767                 return;
29768             }
29769             
29770             active = v;
29771             return false;
29772             
29773         });
29774         
29775         return active;
29776     },
29777     
29778     setActiveItem : function(item)
29779     {
29780         var prev = false;
29781         
29782         Roo.each(this.barItems, function(v){
29783             if (v.rid == item.rid) {
29784                 return ;
29785             }
29786             
29787             if (v.isActive()) {
29788                 v.setActive(false);
29789                 prev = v;
29790             }
29791         });
29792
29793         item.setActive(true);
29794         
29795         this.fireEvent('changed', this, item, prev);
29796     },
29797     
29798     getBarItem: function(rid)
29799     {
29800         var ret = false;
29801         
29802         Roo.each(this.barItems, function(e) {
29803             if (e.rid != rid) {
29804                 return;
29805             }
29806             
29807             ret =  e;
29808             return false;
29809         });
29810         
29811         return ret;
29812     },
29813     
29814     indexOfItem : function(item)
29815     {
29816         var index = false;
29817         
29818         Roo.each(this.barItems, function(v, i){
29819             
29820             if (v.rid != item.rid) {
29821                 return;
29822             }
29823             
29824             index = i;
29825             return false
29826         });
29827         
29828         return index;
29829     },
29830     
29831     setActiveNext : function()
29832     {
29833         var i = this.indexOfItem(this.getActive());
29834         
29835         if (i > this.barItems.length) {
29836             return;
29837         }
29838         
29839         this.setActiveItem(this.barItems[i+1]);
29840     },
29841     
29842     setActivePrev : function()
29843     {
29844         var i = this.indexOfItem(this.getActive());
29845         
29846         if (i  < 1) {
29847             return;
29848         }
29849         
29850         this.setActiveItem(this.barItems[i-1]);
29851     },
29852     
29853     format : function()
29854     {
29855         if(!this.barItems.length){
29856             return;
29857         }
29858      
29859         var width = 100 / this.barItems.length;
29860         
29861         Roo.each(this.barItems, function(i){
29862             i.el.setStyle('width', width + '%');
29863             i.topEl.el.setStyle('width', width + '%');
29864             i.bottomEl.el.setStyle('width', width + '%');
29865         }, this);
29866         
29867     }
29868     
29869 });
29870 /*
29871  * - LGPL
29872  *
29873  * Nav Progress Item
29874  * 
29875  */
29876
29877 /**
29878  * @class Roo.bootstrap.NavProgressItem
29879  * @extends Roo.bootstrap.Component
29880  * Bootstrap NavProgressItem class
29881  * @cfg {String} rid the reference id
29882  * @cfg {Boolean} active (true|false) Is item active default false
29883  * @cfg {Boolean} disabled (true|false) Is item active default false
29884  * @cfg {String} html
29885  * @cfg {String} position (top|bottom) text position default bottom
29886  * @cfg {String} icon show icon instead of number
29887  * 
29888  * @constructor
29889  * Create a new NavProgressItem
29890  * @param {Object} config The config object
29891  */
29892 Roo.bootstrap.NavProgressItem = function(config){
29893     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29894     this.addEvents({
29895         // raw events
29896         /**
29897          * @event click
29898          * The raw click event for the entire grid.
29899          * @param {Roo.bootstrap.NavProgressItem} this
29900          * @param {Roo.EventObject} e
29901          */
29902         "click" : true
29903     });
29904    
29905 };
29906
29907 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29908     
29909     rid : '',
29910     active : false,
29911     disabled : false,
29912     html : '',
29913     position : 'bottom',
29914     icon : false,
29915     
29916     getAutoCreate : function()
29917     {
29918         var iconCls = 'roo-navigation-bar-item-icon';
29919         
29920         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29921         
29922         var cfg = {
29923             tag: 'li',
29924             cls: 'roo-navigation-bar-item',
29925             cn : [
29926                 {
29927                     tag : 'i',
29928                     cls : iconCls
29929                 }
29930             ]
29931         };
29932         
29933         if(this.active){
29934             cfg.cls += ' active';
29935         }
29936         if(this.disabled){
29937             cfg.cls += ' disabled';
29938         }
29939         
29940         return cfg;
29941     },
29942     
29943     disable : function()
29944     {
29945         this.setDisabled(true);
29946     },
29947     
29948     enable : function()
29949     {
29950         this.setDisabled(false);
29951     },
29952     
29953     initEvents: function() 
29954     {
29955         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29956         
29957         this.iconEl.on('click', this.onClick, this);
29958     },
29959     
29960     onClick : function(e)
29961     {
29962         e.preventDefault();
29963         
29964         if(this.disabled){
29965             return;
29966         }
29967         
29968         if(this.fireEvent('click', this, e) === false){
29969             return;
29970         };
29971         
29972         this.parent().setActiveItem(this);
29973     },
29974     
29975     isActive: function () 
29976     {
29977         return this.active;
29978     },
29979     
29980     setActive : function(state)
29981     {
29982         if(this.active == state){
29983             return;
29984         }
29985         
29986         this.active = state;
29987         
29988         if (state) {
29989             this.el.addClass('active');
29990             return;
29991         }
29992         
29993         this.el.removeClass('active');
29994         
29995         return;
29996     },
29997     
29998     setDisabled : function(state)
29999     {
30000         if(this.disabled == state){
30001             return;
30002         }
30003         
30004         this.disabled = state;
30005         
30006         if (state) {
30007             this.el.addClass('disabled');
30008             return;
30009         }
30010         
30011         this.el.removeClass('disabled');
30012     },
30013     
30014     tooltipEl : function()
30015     {
30016         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30017     }
30018 });
30019  
30020
30021  /*
30022  * - LGPL
30023  *
30024  * FieldLabel
30025  * 
30026  */
30027
30028 /**
30029  * @class Roo.bootstrap.FieldLabel
30030  * @extends Roo.bootstrap.Component
30031  * Bootstrap FieldLabel class
30032  * @cfg {String} html contents of the element
30033  * @cfg {String} tag tag of the element default label
30034  * @cfg {String} cls class of the element
30035  * @cfg {String} target label target 
30036  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30037  * @cfg {String} invalidClass default "text-warning"
30038  * @cfg {String} validClass default "text-success"
30039  * @cfg {String} iconTooltip default "This field is required"
30040  * @cfg {String} indicatorpos (left|right) default left
30041  * 
30042  * @constructor
30043  * Create a new FieldLabel
30044  * @param {Object} config The config object
30045  */
30046
30047 Roo.bootstrap.FieldLabel = function(config){
30048     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30049     
30050     this.addEvents({
30051             /**
30052              * @event invalid
30053              * Fires after the field has been marked as invalid.
30054              * @param {Roo.form.FieldLabel} this
30055              * @param {String} msg The validation message
30056              */
30057             invalid : true,
30058             /**
30059              * @event valid
30060              * Fires after the field has been validated with no errors.
30061              * @param {Roo.form.FieldLabel} this
30062              */
30063             valid : true
30064         });
30065 };
30066
30067 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30068     
30069     tag: 'label',
30070     cls: '',
30071     html: '',
30072     target: '',
30073     allowBlank : true,
30074     invalidClass : 'has-warning',
30075     validClass : 'has-success',
30076     iconTooltip : 'This field is required',
30077     indicatorpos : 'left',
30078     
30079     getAutoCreate : function(){
30080         
30081         var cls = "";
30082         if (!this.allowBlank) {
30083             cls  = "visible";
30084         }
30085         
30086         var cfg = {
30087             tag : this.tag,
30088             cls : 'roo-bootstrap-field-label ' + this.cls,
30089             for : this.target,
30090             cn : [
30091                 {
30092                     tag : 'i',
30093                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30094                     tooltip : this.iconTooltip
30095                 },
30096                 {
30097                     tag : 'span',
30098                     html : this.html
30099                 }
30100             ] 
30101         };
30102         
30103         if(this.indicatorpos == 'right'){
30104             var cfg = {
30105                 tag : this.tag,
30106                 cls : 'roo-bootstrap-field-label ' + this.cls,
30107                 for : this.target,
30108                 cn : [
30109                     {
30110                         tag : 'span',
30111                         html : this.html
30112                     },
30113                     {
30114                         tag : 'i',
30115                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30116                         tooltip : this.iconTooltip
30117                     }
30118                 ] 
30119             };
30120         }
30121         
30122         return cfg;
30123     },
30124     
30125     initEvents: function() 
30126     {
30127         Roo.bootstrap.Element.superclass.initEvents.call(this);
30128         
30129         this.indicator = this.indicatorEl();
30130         
30131         if(this.indicator){
30132             this.indicator.removeClass('visible');
30133             this.indicator.addClass('invisible');
30134         }
30135         
30136         Roo.bootstrap.FieldLabel.register(this);
30137     },
30138     
30139     indicatorEl : function()
30140     {
30141         var indicator = this.el.select('i.roo-required-indicator',true).first();
30142         
30143         if(!indicator){
30144             return false;
30145         }
30146         
30147         return indicator;
30148         
30149     },
30150     
30151     /**
30152      * Mark this field as valid
30153      */
30154     markValid : function()
30155     {
30156         if(this.indicator){
30157             this.indicator.removeClass('visible');
30158             this.indicator.addClass('invisible');
30159         }
30160         
30161         this.el.removeClass(this.invalidClass);
30162         
30163         this.el.addClass(this.validClass);
30164         
30165         this.fireEvent('valid', this);
30166     },
30167     
30168     /**
30169      * Mark this field as invalid
30170      * @param {String} msg The validation message
30171      */
30172     markInvalid : function(msg)
30173     {
30174         if(this.indicator){
30175             this.indicator.removeClass('invisible');
30176             this.indicator.addClass('visible');
30177         }
30178         
30179         this.el.removeClass(this.validClass);
30180         
30181         this.el.addClass(this.invalidClass);
30182         
30183         this.fireEvent('invalid', this, msg);
30184     }
30185     
30186    
30187 });
30188
30189 Roo.apply(Roo.bootstrap.FieldLabel, {
30190     
30191     groups: {},
30192     
30193      /**
30194     * register a FieldLabel Group
30195     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30196     */
30197     register : function(label)
30198     {
30199         if(this.groups.hasOwnProperty(label.target)){
30200             return;
30201         }
30202      
30203         this.groups[label.target] = label;
30204         
30205     },
30206     /**
30207     * fetch a FieldLabel Group based on the target
30208     * @param {string} target
30209     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30210     */
30211     get: function(target) {
30212         if (typeof(this.groups[target]) == 'undefined') {
30213             return false;
30214         }
30215         
30216         return this.groups[target] ;
30217     }
30218 });
30219
30220  
30221
30222  /*
30223  * - LGPL
30224  *
30225  * page DateSplitField.
30226  * 
30227  */
30228
30229
30230 /**
30231  * @class Roo.bootstrap.DateSplitField
30232  * @extends Roo.bootstrap.Component
30233  * Bootstrap DateSplitField class
30234  * @cfg {string} fieldLabel - the label associated
30235  * @cfg {Number} labelWidth set the width of label (0-12)
30236  * @cfg {String} labelAlign (top|left)
30237  * @cfg {Boolean} dayAllowBlank (true|false) default false
30238  * @cfg {Boolean} monthAllowBlank (true|false) default false
30239  * @cfg {Boolean} yearAllowBlank (true|false) default false
30240  * @cfg {string} dayPlaceholder 
30241  * @cfg {string} monthPlaceholder
30242  * @cfg {string} yearPlaceholder
30243  * @cfg {string} dayFormat default 'd'
30244  * @cfg {string} monthFormat default 'm'
30245  * @cfg {string} yearFormat default 'Y'
30246  * @cfg {Number} labellg set the width of label (1-12)
30247  * @cfg {Number} labelmd set the width of label (1-12)
30248  * @cfg {Number} labelsm set the width of label (1-12)
30249  * @cfg {Number} labelxs set the width of label (1-12)
30250
30251  *     
30252  * @constructor
30253  * Create a new DateSplitField
30254  * @param {Object} config The config object
30255  */
30256
30257 Roo.bootstrap.DateSplitField = function(config){
30258     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30259     
30260     this.addEvents({
30261         // raw events
30262          /**
30263          * @event years
30264          * getting the data of years
30265          * @param {Roo.bootstrap.DateSplitField} this
30266          * @param {Object} years
30267          */
30268         "years" : true,
30269         /**
30270          * @event days
30271          * getting the data of days
30272          * @param {Roo.bootstrap.DateSplitField} this
30273          * @param {Object} days
30274          */
30275         "days" : true,
30276         /**
30277          * @event invalid
30278          * Fires after the field has been marked as invalid.
30279          * @param {Roo.form.Field} this
30280          * @param {String} msg The validation message
30281          */
30282         invalid : true,
30283        /**
30284          * @event valid
30285          * Fires after the field has been validated with no errors.
30286          * @param {Roo.form.Field} this
30287          */
30288         valid : true
30289     });
30290 };
30291
30292 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30293     
30294     fieldLabel : '',
30295     labelAlign : 'top',
30296     labelWidth : 3,
30297     dayAllowBlank : false,
30298     monthAllowBlank : false,
30299     yearAllowBlank : false,
30300     dayPlaceholder : '',
30301     monthPlaceholder : '',
30302     yearPlaceholder : '',
30303     dayFormat : 'd',
30304     monthFormat : 'm',
30305     yearFormat : 'Y',
30306     isFormField : true,
30307     labellg : 0,
30308     labelmd : 0,
30309     labelsm : 0,
30310     labelxs : 0,
30311     
30312     getAutoCreate : function()
30313     {
30314         var cfg = {
30315             tag : 'div',
30316             cls : 'row roo-date-split-field-group',
30317             cn : [
30318                 {
30319                     tag : 'input',
30320                     type : 'hidden',
30321                     cls : 'form-hidden-field roo-date-split-field-group-value',
30322                     name : this.name
30323                 }
30324             ]
30325         };
30326         
30327         var labelCls = 'col-md-12';
30328         var contentCls = 'col-md-4';
30329         
30330         if(this.fieldLabel){
30331             
30332             var label = {
30333                 tag : 'div',
30334                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30335                 cn : [
30336                     {
30337                         tag : 'label',
30338                         html : this.fieldLabel
30339                     }
30340                 ]
30341             };
30342             
30343             if(this.labelAlign == 'left'){
30344             
30345                 if(this.labelWidth > 12){
30346                     label.style = "width: " + this.labelWidth + 'px';
30347                 }
30348
30349                 if(this.labelWidth < 13 && this.labelmd == 0){
30350                     this.labelmd = this.labelWidth;
30351                 }
30352
30353                 if(this.labellg > 0){
30354                     labelCls = ' col-lg-' + this.labellg;
30355                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30356                 }
30357
30358                 if(this.labelmd > 0){
30359                     labelCls = ' col-md-' + this.labelmd;
30360                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30361                 }
30362
30363                 if(this.labelsm > 0){
30364                     labelCls = ' col-sm-' + this.labelsm;
30365                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30366                 }
30367
30368                 if(this.labelxs > 0){
30369                     labelCls = ' col-xs-' + this.labelxs;
30370                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30371                 }
30372             }
30373             
30374             label.cls += ' ' + labelCls;
30375             
30376             cfg.cn.push(label);
30377         }
30378         
30379         Roo.each(['day', 'month', 'year'], function(t){
30380             cfg.cn.push({
30381                 tag : 'div',
30382                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30383             });
30384         }, this);
30385         
30386         return cfg;
30387     },
30388     
30389     inputEl: function ()
30390     {
30391         return this.el.select('.roo-date-split-field-group-value', true).first();
30392     },
30393     
30394     onRender : function(ct, position) 
30395     {
30396         var _this = this;
30397         
30398         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30399         
30400         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30401         
30402         this.dayField = new Roo.bootstrap.ComboBox({
30403             allowBlank : this.dayAllowBlank,
30404             alwaysQuery : true,
30405             displayField : 'value',
30406             editable : false,
30407             fieldLabel : '',
30408             forceSelection : true,
30409             mode : 'local',
30410             placeholder : this.dayPlaceholder,
30411             selectOnFocus : true,
30412             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30413             triggerAction : 'all',
30414             typeAhead : true,
30415             valueField : 'value',
30416             store : new Roo.data.SimpleStore({
30417                 data : (function() {    
30418                     var days = [];
30419                     _this.fireEvent('days', _this, days);
30420                     return days;
30421                 })(),
30422                 fields : [ 'value' ]
30423             }),
30424             listeners : {
30425                 select : function (_self, record, index)
30426                 {
30427                     _this.setValue(_this.getValue());
30428                 }
30429             }
30430         });
30431
30432         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30433         
30434         this.monthField = new Roo.bootstrap.MonthField({
30435             after : '<i class=\"fa fa-calendar\"></i>',
30436             allowBlank : this.monthAllowBlank,
30437             placeholder : this.monthPlaceholder,
30438             readOnly : true,
30439             listeners : {
30440                 render : function (_self)
30441                 {
30442                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30443                         e.preventDefault();
30444                         _self.focus();
30445                     });
30446                 },
30447                 select : function (_self, oldvalue, newvalue)
30448                 {
30449                     _this.setValue(_this.getValue());
30450                 }
30451             }
30452         });
30453         
30454         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30455         
30456         this.yearField = new Roo.bootstrap.ComboBox({
30457             allowBlank : this.yearAllowBlank,
30458             alwaysQuery : true,
30459             displayField : 'value',
30460             editable : false,
30461             fieldLabel : '',
30462             forceSelection : true,
30463             mode : 'local',
30464             placeholder : this.yearPlaceholder,
30465             selectOnFocus : true,
30466             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30467             triggerAction : 'all',
30468             typeAhead : true,
30469             valueField : 'value',
30470             store : new Roo.data.SimpleStore({
30471                 data : (function() {
30472                     var years = [];
30473                     _this.fireEvent('years', _this, years);
30474                     return years;
30475                 })(),
30476                 fields : [ 'value' ]
30477             }),
30478             listeners : {
30479                 select : function (_self, record, index)
30480                 {
30481                     _this.setValue(_this.getValue());
30482                 }
30483             }
30484         });
30485
30486         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30487     },
30488     
30489     setValue : function(v, format)
30490     {
30491         this.inputEl.dom.value = v;
30492         
30493         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30494         
30495         var d = Date.parseDate(v, f);
30496         
30497         if(!d){
30498             this.validate();
30499             return;
30500         }
30501         
30502         this.setDay(d.format(this.dayFormat));
30503         this.setMonth(d.format(this.monthFormat));
30504         this.setYear(d.format(this.yearFormat));
30505         
30506         this.validate();
30507         
30508         return;
30509     },
30510     
30511     setDay : function(v)
30512     {
30513         this.dayField.setValue(v);
30514         this.inputEl.dom.value = this.getValue();
30515         this.validate();
30516         return;
30517     },
30518     
30519     setMonth : function(v)
30520     {
30521         this.monthField.setValue(v, true);
30522         this.inputEl.dom.value = this.getValue();
30523         this.validate();
30524         return;
30525     },
30526     
30527     setYear : function(v)
30528     {
30529         this.yearField.setValue(v);
30530         this.inputEl.dom.value = this.getValue();
30531         this.validate();
30532         return;
30533     },
30534     
30535     getDay : function()
30536     {
30537         return this.dayField.getValue();
30538     },
30539     
30540     getMonth : function()
30541     {
30542         return this.monthField.getValue();
30543     },
30544     
30545     getYear : function()
30546     {
30547         return this.yearField.getValue();
30548     },
30549     
30550     getValue : function()
30551     {
30552         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30553         
30554         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30555         
30556         return date;
30557     },
30558     
30559     reset : function()
30560     {
30561         this.setDay('');
30562         this.setMonth('');
30563         this.setYear('');
30564         this.inputEl.dom.value = '';
30565         this.validate();
30566         return;
30567     },
30568     
30569     validate : function()
30570     {
30571         var d = this.dayField.validate();
30572         var m = this.monthField.validate();
30573         var y = this.yearField.validate();
30574         
30575         var valid = true;
30576         
30577         if(
30578                 (!this.dayAllowBlank && !d) ||
30579                 (!this.monthAllowBlank && !m) ||
30580                 (!this.yearAllowBlank && !y)
30581         ){
30582             valid = false;
30583         }
30584         
30585         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30586             return valid;
30587         }
30588         
30589         if(valid){
30590             this.markValid();
30591             return valid;
30592         }
30593         
30594         this.markInvalid();
30595         
30596         return valid;
30597     },
30598     
30599     markValid : function()
30600     {
30601         
30602         var label = this.el.select('label', true).first();
30603         var icon = this.el.select('i.fa-star', true).first();
30604
30605         if(label && icon){
30606             icon.remove();
30607         }
30608         
30609         this.fireEvent('valid', this);
30610     },
30611     
30612      /**
30613      * Mark this field as invalid
30614      * @param {String} msg The validation message
30615      */
30616     markInvalid : function(msg)
30617     {
30618         
30619         var label = this.el.select('label', true).first();
30620         var icon = this.el.select('i.fa-star', true).first();
30621
30622         if(label && !icon){
30623             this.el.select('.roo-date-split-field-label', true).createChild({
30624                 tag : 'i',
30625                 cls : 'text-danger fa fa-lg fa-star',
30626                 tooltip : 'This field is required',
30627                 style : 'margin-right:5px;'
30628             }, label, true);
30629         }
30630         
30631         this.fireEvent('invalid', this, msg);
30632     },
30633     
30634     clearInvalid : function()
30635     {
30636         var label = this.el.select('label', true).first();
30637         var icon = this.el.select('i.fa-star', true).first();
30638
30639         if(label && icon){
30640             icon.remove();
30641         }
30642         
30643         this.fireEvent('valid', this);
30644     },
30645     
30646     getName: function()
30647     {
30648         return this.name;
30649     }
30650     
30651 });
30652
30653  /**
30654  *
30655  * This is based on 
30656  * http://masonry.desandro.com
30657  *
30658  * The idea is to render all the bricks based on vertical width...
30659  *
30660  * The original code extends 'outlayer' - we might need to use that....
30661  * 
30662  */
30663
30664
30665 /**
30666  * @class Roo.bootstrap.LayoutMasonry
30667  * @extends Roo.bootstrap.Component
30668  * Bootstrap Layout Masonry class
30669  * 
30670  * @constructor
30671  * Create a new Element
30672  * @param {Object} config The config object
30673  */
30674
30675 Roo.bootstrap.LayoutMasonry = function(config){
30676     
30677     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30678     
30679     this.bricks = [];
30680     
30681     Roo.bootstrap.LayoutMasonry.register(this);
30682     
30683     this.addEvents({
30684         // raw events
30685         /**
30686          * @event layout
30687          * Fire after layout the items
30688          * @param {Roo.bootstrap.LayoutMasonry} this
30689          * @param {Roo.EventObject} e
30690          */
30691         "layout" : true
30692     });
30693     
30694 };
30695
30696 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30697     
30698     /**
30699      * @cfg {Boolean} isLayoutInstant = no animation?
30700      */   
30701     isLayoutInstant : false, // needed?
30702    
30703     /**
30704      * @cfg {Number} boxWidth  width of the columns
30705      */   
30706     boxWidth : 450,
30707     
30708       /**
30709      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30710      */   
30711     boxHeight : 0,
30712     
30713     /**
30714      * @cfg {Number} padWidth padding below box..
30715      */   
30716     padWidth : 10, 
30717     
30718     /**
30719      * @cfg {Number} gutter gutter width..
30720      */   
30721     gutter : 10,
30722     
30723      /**
30724      * @cfg {Number} maxCols maximum number of columns
30725      */   
30726     
30727     maxCols: 0,
30728     
30729     /**
30730      * @cfg {Boolean} isAutoInitial defalut true
30731      */   
30732     isAutoInitial : true, 
30733     
30734     containerWidth: 0,
30735     
30736     /**
30737      * @cfg {Boolean} isHorizontal defalut false
30738      */   
30739     isHorizontal : false, 
30740
30741     currentSize : null,
30742     
30743     tag: 'div',
30744     
30745     cls: '',
30746     
30747     bricks: null, //CompositeElement
30748     
30749     cols : 1,
30750     
30751     _isLayoutInited : false,
30752     
30753 //    isAlternative : false, // only use for vertical layout...
30754     
30755     /**
30756      * @cfg {Number} alternativePadWidth padding below box..
30757      */   
30758     alternativePadWidth : 50,
30759     
30760     selectedBrick : [],
30761     
30762     getAutoCreate : function(){
30763         
30764         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30765         
30766         var cfg = {
30767             tag: this.tag,
30768             cls: 'blog-masonary-wrapper ' + this.cls,
30769             cn : {
30770                 cls : 'mas-boxes masonary'
30771             }
30772         };
30773         
30774         return cfg;
30775     },
30776     
30777     getChildContainer: function( )
30778     {
30779         if (this.boxesEl) {
30780             return this.boxesEl;
30781         }
30782         
30783         this.boxesEl = this.el.select('.mas-boxes').first();
30784         
30785         return this.boxesEl;
30786     },
30787     
30788     
30789     initEvents : function()
30790     {
30791         var _this = this;
30792         
30793         if(this.isAutoInitial){
30794             Roo.log('hook children rendered');
30795             this.on('childrenrendered', function() {
30796                 Roo.log('children rendered');
30797                 _this.initial();
30798             } ,this);
30799         }
30800     },
30801     
30802     initial : function()
30803     {
30804         this.selectedBrick = [];
30805         
30806         this.currentSize = this.el.getBox(true);
30807         
30808         Roo.EventManager.onWindowResize(this.resize, this); 
30809
30810         if(!this.isAutoInitial){
30811             this.layout();
30812             return;
30813         }
30814         
30815         this.layout();
30816         
30817         return;
30818         //this.layout.defer(500,this);
30819         
30820     },
30821     
30822     resize : function()
30823     {
30824         var cs = this.el.getBox(true);
30825         
30826         if (
30827                 this.currentSize.width == cs.width && 
30828                 this.currentSize.x == cs.x && 
30829                 this.currentSize.height == cs.height && 
30830                 this.currentSize.y == cs.y 
30831         ) {
30832             Roo.log("no change in with or X or Y");
30833             return;
30834         }
30835         
30836         this.currentSize = cs;
30837         
30838         this.layout();
30839         
30840     },
30841     
30842     layout : function()
30843     {   
30844         this._resetLayout();
30845         
30846         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30847         
30848         this.layoutItems( isInstant );
30849       
30850         this._isLayoutInited = true;
30851         
30852         this.fireEvent('layout', this);
30853         
30854     },
30855     
30856     _resetLayout : function()
30857     {
30858         if(this.isHorizontal){
30859             this.horizontalMeasureColumns();
30860             return;
30861         }
30862         
30863         this.verticalMeasureColumns();
30864         
30865     },
30866     
30867     verticalMeasureColumns : function()
30868     {
30869         this.getContainerWidth();
30870         
30871 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30872 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30873 //            return;
30874 //        }
30875         
30876         var boxWidth = this.boxWidth + this.padWidth;
30877         
30878         if(this.containerWidth < this.boxWidth){
30879             boxWidth = this.containerWidth
30880         }
30881         
30882         var containerWidth = this.containerWidth;
30883         
30884         var cols = Math.floor(containerWidth / boxWidth);
30885         
30886         this.cols = Math.max( cols, 1 );
30887         
30888         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30889         
30890         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30891         
30892         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30893         
30894         this.colWidth = boxWidth + avail - this.padWidth;
30895         
30896         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30897         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30898     },
30899     
30900     horizontalMeasureColumns : function()
30901     {
30902         this.getContainerWidth();
30903         
30904         var boxWidth = this.boxWidth;
30905         
30906         if(this.containerWidth < boxWidth){
30907             boxWidth = this.containerWidth;
30908         }
30909         
30910         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30911         
30912         this.el.setHeight(boxWidth);
30913         
30914     },
30915     
30916     getContainerWidth : function()
30917     {
30918         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30919     },
30920     
30921     layoutItems : function( isInstant )
30922     {
30923         Roo.log(this.bricks);
30924         
30925         var items = Roo.apply([], this.bricks);
30926         
30927         if(this.isHorizontal){
30928             this._horizontalLayoutItems( items , isInstant );
30929             return;
30930         }
30931         
30932 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30933 //            this._verticalAlternativeLayoutItems( items , isInstant );
30934 //            return;
30935 //        }
30936         
30937         this._verticalLayoutItems( items , isInstant );
30938         
30939     },
30940     
30941     _verticalLayoutItems : function ( items , isInstant)
30942     {
30943         if ( !items || !items.length ) {
30944             return;
30945         }
30946         
30947         var standard = [
30948             ['xs', 'xs', 'xs', 'tall'],
30949             ['xs', 'xs', 'tall'],
30950             ['xs', 'xs', 'sm'],
30951             ['xs', 'xs', 'xs'],
30952             ['xs', 'tall'],
30953             ['xs', 'sm'],
30954             ['xs', 'xs'],
30955             ['xs'],
30956             
30957             ['sm', 'xs', 'xs'],
30958             ['sm', 'xs'],
30959             ['sm'],
30960             
30961             ['tall', 'xs', 'xs', 'xs'],
30962             ['tall', 'xs', 'xs'],
30963             ['tall', 'xs'],
30964             ['tall']
30965             
30966         ];
30967         
30968         var queue = [];
30969         
30970         var boxes = [];
30971         
30972         var box = [];
30973         
30974         Roo.each(items, function(item, k){
30975             
30976             switch (item.size) {
30977                 // these layouts take up a full box,
30978                 case 'md' :
30979                 case 'md-left' :
30980                 case 'md-right' :
30981                 case 'wide' :
30982                     
30983                     if(box.length){
30984                         boxes.push(box);
30985                         box = [];
30986                     }
30987                     
30988                     boxes.push([item]);
30989                     
30990                     break;
30991                     
30992                 case 'xs' :
30993                 case 'sm' :
30994                 case 'tall' :
30995                     
30996                     box.push(item);
30997                     
30998                     break;
30999                 default :
31000                     break;
31001                     
31002             }
31003             
31004         }, this);
31005         
31006         if(box.length){
31007             boxes.push(box);
31008             box = [];
31009         }
31010         
31011         var filterPattern = function(box, length)
31012         {
31013             if(!box.length){
31014                 return;
31015             }
31016             
31017             var match = false;
31018             
31019             var pattern = box.slice(0, length);
31020             
31021             var format = [];
31022             
31023             Roo.each(pattern, function(i){
31024                 format.push(i.size);
31025             }, this);
31026             
31027             Roo.each(standard, function(s){
31028                 
31029                 if(String(s) != String(format)){
31030                     return;
31031                 }
31032                 
31033                 match = true;
31034                 return false;
31035                 
31036             }, this);
31037             
31038             if(!match && length == 1){
31039                 return;
31040             }
31041             
31042             if(!match){
31043                 filterPattern(box, length - 1);
31044                 return;
31045             }
31046                 
31047             queue.push(pattern);
31048
31049             box = box.slice(length, box.length);
31050
31051             filterPattern(box, 4);
31052
31053             return;
31054             
31055         }
31056         
31057         Roo.each(boxes, function(box, k){
31058             
31059             if(!box.length){
31060                 return;
31061             }
31062             
31063             if(box.length == 1){
31064                 queue.push(box);
31065                 return;
31066             }
31067             
31068             filterPattern(box, 4);
31069             
31070         }, this);
31071         
31072         this._processVerticalLayoutQueue( queue, isInstant );
31073         
31074     },
31075     
31076 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31077 //    {
31078 //        if ( !items || !items.length ) {
31079 //            return;
31080 //        }
31081 //
31082 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31083 //        
31084 //    },
31085     
31086     _horizontalLayoutItems : function ( items , isInstant)
31087     {
31088         if ( !items || !items.length || items.length < 3) {
31089             return;
31090         }
31091         
31092         items.reverse();
31093         
31094         var eItems = items.slice(0, 3);
31095         
31096         items = items.slice(3, items.length);
31097         
31098         var standard = [
31099             ['xs', 'xs', 'xs', 'wide'],
31100             ['xs', 'xs', 'wide'],
31101             ['xs', 'xs', 'sm'],
31102             ['xs', 'xs', 'xs'],
31103             ['xs', 'wide'],
31104             ['xs', 'sm'],
31105             ['xs', 'xs'],
31106             ['xs'],
31107             
31108             ['sm', 'xs', 'xs'],
31109             ['sm', 'xs'],
31110             ['sm'],
31111             
31112             ['wide', 'xs', 'xs', 'xs'],
31113             ['wide', 'xs', 'xs'],
31114             ['wide', 'xs'],
31115             ['wide'],
31116             
31117             ['wide-thin']
31118         ];
31119         
31120         var queue = [];
31121         
31122         var boxes = [];
31123         
31124         var box = [];
31125         
31126         Roo.each(items, function(item, k){
31127             
31128             switch (item.size) {
31129                 case 'md' :
31130                 case 'md-left' :
31131                 case 'md-right' :
31132                 case 'tall' :
31133                     
31134                     if(box.length){
31135                         boxes.push(box);
31136                         box = [];
31137                     }
31138                     
31139                     boxes.push([item]);
31140                     
31141                     break;
31142                     
31143                 case 'xs' :
31144                 case 'sm' :
31145                 case 'wide' :
31146                 case 'wide-thin' :
31147                     
31148                     box.push(item);
31149                     
31150                     break;
31151                 default :
31152                     break;
31153                     
31154             }
31155             
31156         }, this);
31157         
31158         if(box.length){
31159             boxes.push(box);
31160             box = [];
31161         }
31162         
31163         var filterPattern = function(box, length)
31164         {
31165             if(!box.length){
31166                 return;
31167             }
31168             
31169             var match = false;
31170             
31171             var pattern = box.slice(0, length);
31172             
31173             var format = [];
31174             
31175             Roo.each(pattern, function(i){
31176                 format.push(i.size);
31177             }, this);
31178             
31179             Roo.each(standard, function(s){
31180                 
31181                 if(String(s) != String(format)){
31182                     return;
31183                 }
31184                 
31185                 match = true;
31186                 return false;
31187                 
31188             }, this);
31189             
31190             if(!match && length == 1){
31191                 return;
31192             }
31193             
31194             if(!match){
31195                 filterPattern(box, length - 1);
31196                 return;
31197             }
31198                 
31199             queue.push(pattern);
31200
31201             box = box.slice(length, box.length);
31202
31203             filterPattern(box, 4);
31204
31205             return;
31206             
31207         }
31208         
31209         Roo.each(boxes, function(box, k){
31210             
31211             if(!box.length){
31212                 return;
31213             }
31214             
31215             if(box.length == 1){
31216                 queue.push(box);
31217                 return;
31218             }
31219             
31220             filterPattern(box, 4);
31221             
31222         }, this);
31223         
31224         
31225         var prune = [];
31226         
31227         var pos = this.el.getBox(true);
31228         
31229         var minX = pos.x;
31230         
31231         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31232         
31233         var hit_end = false;
31234         
31235         Roo.each(queue, function(box){
31236             
31237             if(hit_end){
31238                 
31239                 Roo.each(box, function(b){
31240                 
31241                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31242                     b.el.hide();
31243
31244                 }, this);
31245
31246                 return;
31247             }
31248             
31249             var mx = 0;
31250             
31251             Roo.each(box, function(b){
31252                 
31253                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31254                 b.el.show();
31255
31256                 mx = Math.max(mx, b.x);
31257                 
31258             }, this);
31259             
31260             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31261             
31262             if(maxX < minX){
31263                 
31264                 Roo.each(box, function(b){
31265                 
31266                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31267                     b.el.hide();
31268                     
31269                 }, this);
31270                 
31271                 hit_end = true;
31272                 
31273                 return;
31274             }
31275             
31276             prune.push(box);
31277             
31278         }, this);
31279         
31280         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31281     },
31282     
31283     /** Sets position of item in DOM
31284     * @param {Element} item
31285     * @param {Number} x - horizontal position
31286     * @param {Number} y - vertical position
31287     * @param {Boolean} isInstant - disables transitions
31288     */
31289     _processVerticalLayoutQueue : function( queue, isInstant )
31290     {
31291         var pos = this.el.getBox(true);
31292         var x = pos.x;
31293         var y = pos.y;
31294         var maxY = [];
31295         
31296         for (var i = 0; i < this.cols; i++){
31297             maxY[i] = pos.y;
31298         }
31299         
31300         Roo.each(queue, function(box, k){
31301             
31302             var col = k % this.cols;
31303             
31304             Roo.each(box, function(b,kk){
31305                 
31306                 b.el.position('absolute');
31307                 
31308                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31309                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31310                 
31311                 if(b.size == 'md-left' || b.size == 'md-right'){
31312                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31313                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31314                 }
31315                 
31316                 b.el.setWidth(width);
31317                 b.el.setHeight(height);
31318                 // iframe?
31319                 b.el.select('iframe',true).setSize(width,height);
31320                 
31321             }, this);
31322             
31323             for (var i = 0; i < this.cols; i++){
31324                 
31325                 if(maxY[i] < maxY[col]){
31326                     col = i;
31327                     continue;
31328                 }
31329                 
31330                 col = Math.min(col, i);
31331                 
31332             }
31333             
31334             x = pos.x + col * (this.colWidth + this.padWidth);
31335             
31336             y = maxY[col];
31337             
31338             var positions = [];
31339             
31340             switch (box.length){
31341                 case 1 :
31342                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31343                     break;
31344                 case 2 :
31345                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31346                     break;
31347                 case 3 :
31348                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31349                     break;
31350                 case 4 :
31351                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31352                     break;
31353                 default :
31354                     break;
31355             }
31356             
31357             Roo.each(box, function(b,kk){
31358                 
31359                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31360                 
31361                 var sz = b.el.getSize();
31362                 
31363                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31364                 
31365             }, this);
31366             
31367         }, this);
31368         
31369         var mY = 0;
31370         
31371         for (var i = 0; i < this.cols; i++){
31372             mY = Math.max(mY, maxY[i]);
31373         }
31374         
31375         this.el.setHeight(mY - pos.y);
31376         
31377     },
31378     
31379 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31380 //    {
31381 //        var pos = this.el.getBox(true);
31382 //        var x = pos.x;
31383 //        var y = pos.y;
31384 //        var maxX = pos.right;
31385 //        
31386 //        var maxHeight = 0;
31387 //        
31388 //        Roo.each(items, function(item, k){
31389 //            
31390 //            var c = k % 2;
31391 //            
31392 //            item.el.position('absolute');
31393 //                
31394 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31395 //
31396 //            item.el.setWidth(width);
31397 //
31398 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31399 //
31400 //            item.el.setHeight(height);
31401 //            
31402 //            if(c == 0){
31403 //                item.el.setXY([x, y], isInstant ? false : true);
31404 //            } else {
31405 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31406 //            }
31407 //            
31408 //            y = y + height + this.alternativePadWidth;
31409 //            
31410 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31411 //            
31412 //        }, this);
31413 //        
31414 //        this.el.setHeight(maxHeight);
31415 //        
31416 //    },
31417     
31418     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31419     {
31420         var pos = this.el.getBox(true);
31421         
31422         var minX = pos.x;
31423         var minY = pos.y;
31424         
31425         var maxX = pos.right;
31426         
31427         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31428         
31429         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31430         
31431         Roo.each(queue, function(box, k){
31432             
31433             Roo.each(box, function(b, kk){
31434                 
31435                 b.el.position('absolute');
31436                 
31437                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31438                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31439                 
31440                 if(b.size == 'md-left' || b.size == 'md-right'){
31441                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31442                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31443                 }
31444                 
31445                 b.el.setWidth(width);
31446                 b.el.setHeight(height);
31447                 
31448             }, this);
31449             
31450             if(!box.length){
31451                 return;
31452             }
31453             
31454             var positions = [];
31455             
31456             switch (box.length){
31457                 case 1 :
31458                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31459                     break;
31460                 case 2 :
31461                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31462                     break;
31463                 case 3 :
31464                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31465                     break;
31466                 case 4 :
31467                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31468                     break;
31469                 default :
31470                     break;
31471             }
31472             
31473             Roo.each(box, function(b,kk){
31474                 
31475                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31476                 
31477                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31478                 
31479             }, this);
31480             
31481         }, this);
31482         
31483     },
31484     
31485     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31486     {
31487         Roo.each(eItems, function(b,k){
31488             
31489             b.size = (k == 0) ? 'sm' : 'xs';
31490             b.x = (k == 0) ? 2 : 1;
31491             b.y = (k == 0) ? 2 : 1;
31492             
31493             b.el.position('absolute');
31494             
31495             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31496                 
31497             b.el.setWidth(width);
31498             
31499             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31500             
31501             b.el.setHeight(height);
31502             
31503         }, this);
31504
31505         var positions = [];
31506         
31507         positions.push({
31508             x : maxX - this.unitWidth * 2 - this.gutter,
31509             y : minY
31510         });
31511         
31512         positions.push({
31513             x : maxX - this.unitWidth,
31514             y : minY + (this.unitWidth + this.gutter) * 2
31515         });
31516         
31517         positions.push({
31518             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31519             y : minY
31520         });
31521         
31522         Roo.each(eItems, function(b,k){
31523             
31524             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31525
31526         }, this);
31527         
31528     },
31529     
31530     getVerticalOneBoxColPositions : function(x, y, box)
31531     {
31532         var pos = [];
31533         
31534         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31535         
31536         if(box[0].size == 'md-left'){
31537             rand = 0;
31538         }
31539         
31540         if(box[0].size == 'md-right'){
31541             rand = 1;
31542         }
31543         
31544         pos.push({
31545             x : x + (this.unitWidth + this.gutter) * rand,
31546             y : y
31547         });
31548         
31549         return pos;
31550     },
31551     
31552     getVerticalTwoBoxColPositions : function(x, y, box)
31553     {
31554         var pos = [];
31555         
31556         if(box[0].size == 'xs'){
31557             
31558             pos.push({
31559                 x : x,
31560                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31561             });
31562
31563             pos.push({
31564                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31565                 y : y
31566             });
31567             
31568             return pos;
31569             
31570         }
31571         
31572         pos.push({
31573             x : x,
31574             y : y
31575         });
31576
31577         pos.push({
31578             x : x + (this.unitWidth + this.gutter) * 2,
31579             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31580         });
31581         
31582         return pos;
31583         
31584     },
31585     
31586     getVerticalThreeBoxColPositions : function(x, y, box)
31587     {
31588         var pos = [];
31589         
31590         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31591             
31592             pos.push({
31593                 x : x,
31594                 y : y
31595             });
31596
31597             pos.push({
31598                 x : x + (this.unitWidth + this.gutter) * 1,
31599                 y : y
31600             });
31601             
31602             pos.push({
31603                 x : x + (this.unitWidth + this.gutter) * 2,
31604                 y : y
31605             });
31606             
31607             return pos;
31608             
31609         }
31610         
31611         if(box[0].size == 'xs' && box[1].size == 'xs'){
31612             
31613             pos.push({
31614                 x : x,
31615                 y : y
31616             });
31617
31618             pos.push({
31619                 x : x,
31620                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31621             });
31622             
31623             pos.push({
31624                 x : x + (this.unitWidth + this.gutter) * 1,
31625                 y : y
31626             });
31627             
31628             return pos;
31629             
31630         }
31631         
31632         pos.push({
31633             x : x,
31634             y : y
31635         });
31636
31637         pos.push({
31638             x : x + (this.unitWidth + this.gutter) * 2,
31639             y : y
31640         });
31641
31642         pos.push({
31643             x : x + (this.unitWidth + this.gutter) * 2,
31644             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31645         });
31646             
31647         return pos;
31648         
31649     },
31650     
31651     getVerticalFourBoxColPositions : function(x, y, box)
31652     {
31653         var pos = [];
31654         
31655         if(box[0].size == 'xs'){
31656             
31657             pos.push({
31658                 x : x,
31659                 y : y
31660             });
31661
31662             pos.push({
31663                 x : x,
31664                 y : y + (this.unitHeight + this.gutter) * 1
31665             });
31666             
31667             pos.push({
31668                 x : x,
31669                 y : y + (this.unitHeight + this.gutter) * 2
31670             });
31671             
31672             pos.push({
31673                 x : x + (this.unitWidth + this.gutter) * 1,
31674                 y : y
31675             });
31676             
31677             return pos;
31678             
31679         }
31680         
31681         pos.push({
31682             x : x,
31683             y : y
31684         });
31685
31686         pos.push({
31687             x : x + (this.unitWidth + this.gutter) * 2,
31688             y : y
31689         });
31690
31691         pos.push({
31692             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31693             y : y + (this.unitHeight + this.gutter) * 1
31694         });
31695
31696         pos.push({
31697             x : x + (this.unitWidth + this.gutter) * 2,
31698             y : y + (this.unitWidth + this.gutter) * 2
31699         });
31700
31701         return pos;
31702         
31703     },
31704     
31705     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31706     {
31707         var pos = [];
31708         
31709         if(box[0].size == 'md-left'){
31710             pos.push({
31711                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31712                 y : minY
31713             });
31714             
31715             return pos;
31716         }
31717         
31718         if(box[0].size == 'md-right'){
31719             pos.push({
31720                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31721                 y : minY + (this.unitWidth + this.gutter) * 1
31722             });
31723             
31724             return pos;
31725         }
31726         
31727         var rand = Math.floor(Math.random() * (4 - box[0].y));
31728         
31729         pos.push({
31730             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31731             y : minY + (this.unitWidth + this.gutter) * rand
31732         });
31733         
31734         return pos;
31735         
31736     },
31737     
31738     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31739     {
31740         var pos = [];
31741         
31742         if(box[0].size == 'xs'){
31743             
31744             pos.push({
31745                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31746                 y : minY
31747             });
31748
31749             pos.push({
31750                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31751                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31752             });
31753             
31754             return pos;
31755             
31756         }
31757         
31758         pos.push({
31759             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31760             y : minY
31761         });
31762
31763         pos.push({
31764             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31765             y : minY + (this.unitWidth + this.gutter) * 2
31766         });
31767         
31768         return pos;
31769         
31770     },
31771     
31772     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31773     {
31774         var pos = [];
31775         
31776         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31777             
31778             pos.push({
31779                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31780                 y : minY
31781             });
31782
31783             pos.push({
31784                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31785                 y : minY + (this.unitWidth + this.gutter) * 1
31786             });
31787             
31788             pos.push({
31789                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31790                 y : minY + (this.unitWidth + this.gutter) * 2
31791             });
31792             
31793             return pos;
31794             
31795         }
31796         
31797         if(box[0].size == 'xs' && box[1].size == 'xs'){
31798             
31799             pos.push({
31800                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31801                 y : minY
31802             });
31803
31804             pos.push({
31805                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31806                 y : minY
31807             });
31808             
31809             pos.push({
31810                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31811                 y : minY + (this.unitWidth + this.gutter) * 1
31812             });
31813             
31814             return pos;
31815             
31816         }
31817         
31818         pos.push({
31819             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31820             y : minY
31821         });
31822
31823         pos.push({
31824             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31825             y : minY + (this.unitWidth + this.gutter) * 2
31826         });
31827
31828         pos.push({
31829             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31830             y : minY + (this.unitWidth + this.gutter) * 2
31831         });
31832             
31833         return pos;
31834         
31835     },
31836     
31837     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31838     {
31839         var pos = [];
31840         
31841         if(box[0].size == 'xs'){
31842             
31843             pos.push({
31844                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31845                 y : minY
31846             });
31847
31848             pos.push({
31849                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31850                 y : minY
31851             });
31852             
31853             pos.push({
31854                 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),
31855                 y : minY
31856             });
31857             
31858             pos.push({
31859                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31860                 y : minY + (this.unitWidth + this.gutter) * 1
31861             });
31862             
31863             return pos;
31864             
31865         }
31866         
31867         pos.push({
31868             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31869             y : minY
31870         });
31871         
31872         pos.push({
31873             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31874             y : minY + (this.unitWidth + this.gutter) * 2
31875         });
31876         
31877         pos.push({
31878             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31879             y : minY + (this.unitWidth + this.gutter) * 2
31880         });
31881         
31882         pos.push({
31883             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),
31884             y : minY + (this.unitWidth + this.gutter) * 2
31885         });
31886
31887         return pos;
31888         
31889     },
31890     
31891     /**
31892     * remove a Masonry Brick
31893     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31894     */
31895     removeBrick : function(brick_id)
31896     {
31897         if (!brick_id) {
31898             return;
31899         }
31900         
31901         for (var i = 0; i<this.bricks.length; i++) {
31902             if (this.bricks[i].id == brick_id) {
31903                 this.bricks.splice(i,1);
31904                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31905                 this.initial();
31906             }
31907         }
31908     },
31909     
31910     /**
31911     * adds a Masonry Brick
31912     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31913     */
31914     addBrick : function(cfg)
31915     {
31916         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31917         //this.register(cn);
31918         cn.parentId = this.id;
31919         cn.onRender(this.el, null);
31920         return cn;
31921     },
31922     
31923     /**
31924     * register a Masonry Brick
31925     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31926     */
31927     
31928     register : function(brick)
31929     {
31930         this.bricks.push(brick);
31931         brick.masonryId = this.id;
31932     },
31933     
31934     /**
31935     * clear all the Masonry Brick
31936     */
31937     clearAll : function()
31938     {
31939         this.bricks = [];
31940         //this.getChildContainer().dom.innerHTML = "";
31941         this.el.dom.innerHTML = '';
31942     },
31943     
31944     getSelected : function()
31945     {
31946         if (!this.selectedBrick) {
31947             return false;
31948         }
31949         
31950         return this.selectedBrick;
31951     }
31952 });
31953
31954 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31955     
31956     groups: {},
31957      /**
31958     * register a Masonry Layout
31959     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31960     */
31961     
31962     register : function(layout)
31963     {
31964         this.groups[layout.id] = layout;
31965     },
31966     /**
31967     * fetch a  Masonry Layout based on the masonry layout ID
31968     * @param {string} the masonry layout to add
31969     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31970     */
31971     
31972     get: function(layout_id) {
31973         if (typeof(this.groups[layout_id]) == 'undefined') {
31974             return false;
31975         }
31976         return this.groups[layout_id] ;
31977     }
31978     
31979     
31980     
31981 });
31982
31983  
31984
31985  /**
31986  *
31987  * This is based on 
31988  * http://masonry.desandro.com
31989  *
31990  * The idea is to render all the bricks based on vertical width...
31991  *
31992  * The original code extends 'outlayer' - we might need to use that....
31993  * 
31994  */
31995
31996
31997 /**
31998  * @class Roo.bootstrap.LayoutMasonryAuto
31999  * @extends Roo.bootstrap.Component
32000  * Bootstrap Layout Masonry class
32001  * 
32002  * @constructor
32003  * Create a new Element
32004  * @param {Object} config The config object
32005  */
32006
32007 Roo.bootstrap.LayoutMasonryAuto = function(config){
32008     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32009 };
32010
32011 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32012     
32013       /**
32014      * @cfg {Boolean} isFitWidth  - resize the width..
32015      */   
32016     isFitWidth : false,  // options..
32017     /**
32018      * @cfg {Boolean} isOriginLeft = left align?
32019      */   
32020     isOriginLeft : true,
32021     /**
32022      * @cfg {Boolean} isOriginTop = top align?
32023      */   
32024     isOriginTop : false,
32025     /**
32026      * @cfg {Boolean} isLayoutInstant = no animation?
32027      */   
32028     isLayoutInstant : false, // needed?
32029     /**
32030      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32031      */   
32032     isResizingContainer : true,
32033     /**
32034      * @cfg {Number} columnWidth  width of the columns 
32035      */   
32036     
32037     columnWidth : 0,
32038     
32039     /**
32040      * @cfg {Number} maxCols maximum number of columns
32041      */   
32042     
32043     maxCols: 0,
32044     /**
32045      * @cfg {Number} padHeight padding below box..
32046      */   
32047     
32048     padHeight : 10, 
32049     
32050     /**
32051      * @cfg {Boolean} isAutoInitial defalut true
32052      */   
32053     
32054     isAutoInitial : true, 
32055     
32056     // private?
32057     gutter : 0,
32058     
32059     containerWidth: 0,
32060     initialColumnWidth : 0,
32061     currentSize : null,
32062     
32063     colYs : null, // array.
32064     maxY : 0,
32065     padWidth: 10,
32066     
32067     
32068     tag: 'div',
32069     cls: '',
32070     bricks: null, //CompositeElement
32071     cols : 0, // array?
32072     // element : null, // wrapped now this.el
32073     _isLayoutInited : null, 
32074     
32075     
32076     getAutoCreate : function(){
32077         
32078         var cfg = {
32079             tag: this.tag,
32080             cls: 'blog-masonary-wrapper ' + this.cls,
32081             cn : {
32082                 cls : 'mas-boxes masonary'
32083             }
32084         };
32085         
32086         return cfg;
32087     },
32088     
32089     getChildContainer: function( )
32090     {
32091         if (this.boxesEl) {
32092             return this.boxesEl;
32093         }
32094         
32095         this.boxesEl = this.el.select('.mas-boxes').first();
32096         
32097         return this.boxesEl;
32098     },
32099     
32100     
32101     initEvents : function()
32102     {
32103         var _this = this;
32104         
32105         if(this.isAutoInitial){
32106             Roo.log('hook children rendered');
32107             this.on('childrenrendered', function() {
32108                 Roo.log('children rendered');
32109                 _this.initial();
32110             } ,this);
32111         }
32112         
32113     },
32114     
32115     initial : function()
32116     {
32117         this.reloadItems();
32118
32119         this.currentSize = this.el.getBox(true);
32120
32121         /// was window resize... - let's see if this works..
32122         Roo.EventManager.onWindowResize(this.resize, this); 
32123
32124         if(!this.isAutoInitial){
32125             this.layout();
32126             return;
32127         }
32128         
32129         this.layout.defer(500,this);
32130     },
32131     
32132     reloadItems: function()
32133     {
32134         this.bricks = this.el.select('.masonry-brick', true);
32135         
32136         this.bricks.each(function(b) {
32137             //Roo.log(b.getSize());
32138             if (!b.attr('originalwidth')) {
32139                 b.attr('originalwidth',  b.getSize().width);
32140             }
32141             
32142         });
32143         
32144         Roo.log(this.bricks.elements.length);
32145     },
32146     
32147     resize : function()
32148     {
32149         Roo.log('resize');
32150         var cs = this.el.getBox(true);
32151         
32152         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32153             Roo.log("no change in with or X");
32154             return;
32155         }
32156         this.currentSize = cs;
32157         this.layout();
32158     },
32159     
32160     layout : function()
32161     {
32162          Roo.log('layout');
32163         this._resetLayout();
32164         //this._manageStamps();
32165       
32166         // don't animate first layout
32167         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32168         this.layoutItems( isInstant );
32169       
32170         // flag for initalized
32171         this._isLayoutInited = true;
32172     },
32173     
32174     layoutItems : function( isInstant )
32175     {
32176         //var items = this._getItemsForLayout( this.items );
32177         // original code supports filtering layout items.. we just ignore it..
32178         
32179         this._layoutItems( this.bricks , isInstant );
32180       
32181         this._postLayout();
32182     },
32183     _layoutItems : function ( items , isInstant)
32184     {
32185        //this.fireEvent( 'layout', this, items );
32186     
32187
32188         if ( !items || !items.elements.length ) {
32189           // no items, emit event with empty array
32190             return;
32191         }
32192
32193         var queue = [];
32194         items.each(function(item) {
32195             Roo.log("layout item");
32196             Roo.log(item);
32197             // get x/y object from method
32198             var position = this._getItemLayoutPosition( item );
32199             // enqueue
32200             position.item = item;
32201             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32202             queue.push( position );
32203         }, this);
32204       
32205         this._processLayoutQueue( queue );
32206     },
32207     /** Sets position of item in DOM
32208     * @param {Element} item
32209     * @param {Number} x - horizontal position
32210     * @param {Number} y - vertical position
32211     * @param {Boolean} isInstant - disables transitions
32212     */
32213     _processLayoutQueue : function( queue )
32214     {
32215         for ( var i=0, len = queue.length; i < len; i++ ) {
32216             var obj = queue[i];
32217             obj.item.position('absolute');
32218             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32219         }
32220     },
32221       
32222     
32223     /**
32224     * Any logic you want to do after each layout,
32225     * i.e. size the container
32226     */
32227     _postLayout : function()
32228     {
32229         this.resizeContainer();
32230     },
32231     
32232     resizeContainer : function()
32233     {
32234         if ( !this.isResizingContainer ) {
32235             return;
32236         }
32237         var size = this._getContainerSize();
32238         if ( size ) {
32239             this.el.setSize(size.width,size.height);
32240             this.boxesEl.setSize(size.width,size.height);
32241         }
32242     },
32243     
32244     
32245     
32246     _resetLayout : function()
32247     {
32248         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32249         this.colWidth = this.el.getWidth();
32250         //this.gutter = this.el.getWidth(); 
32251         
32252         this.measureColumns();
32253
32254         // reset column Y
32255         var i = this.cols;
32256         this.colYs = [];
32257         while (i--) {
32258             this.colYs.push( 0 );
32259         }
32260     
32261         this.maxY = 0;
32262     },
32263
32264     measureColumns : function()
32265     {
32266         this.getContainerWidth();
32267       // if columnWidth is 0, default to outerWidth of first item
32268         if ( !this.columnWidth ) {
32269             var firstItem = this.bricks.first();
32270             Roo.log(firstItem);
32271             this.columnWidth  = this.containerWidth;
32272             if (firstItem && firstItem.attr('originalwidth') ) {
32273                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32274             }
32275             // columnWidth fall back to item of first element
32276             Roo.log("set column width?");
32277                         this.initialColumnWidth = this.columnWidth  ;
32278
32279             // if first elem has no width, default to size of container
32280             
32281         }
32282         
32283         
32284         if (this.initialColumnWidth) {
32285             this.columnWidth = this.initialColumnWidth;
32286         }
32287         
32288         
32289             
32290         // column width is fixed at the top - however if container width get's smaller we should
32291         // reduce it...
32292         
32293         // this bit calcs how man columns..
32294             
32295         var columnWidth = this.columnWidth += this.gutter;
32296       
32297         // calculate columns
32298         var containerWidth = this.containerWidth + this.gutter;
32299         
32300         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32301         // fix rounding errors, typically with gutters
32302         var excess = columnWidth - containerWidth % columnWidth;
32303         
32304         
32305         // if overshoot is less than a pixel, round up, otherwise floor it
32306         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32307         cols = Math[ mathMethod ]( cols );
32308         this.cols = Math.max( cols, 1 );
32309         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32310         
32311          // padding positioning..
32312         var totalColWidth = this.cols * this.columnWidth;
32313         var padavail = this.containerWidth - totalColWidth;
32314         // so for 2 columns - we need 3 'pads'
32315         
32316         var padNeeded = (1+this.cols) * this.padWidth;
32317         
32318         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32319         
32320         this.columnWidth += padExtra
32321         //this.padWidth = Math.floor(padavail /  ( this.cols));
32322         
32323         // adjust colum width so that padding is fixed??
32324         
32325         // we have 3 columns ... total = width * 3
32326         // we have X left over... that should be used by 
32327         
32328         //if (this.expandC) {
32329             
32330         //}
32331         
32332         
32333         
32334     },
32335     
32336     getContainerWidth : function()
32337     {
32338        /* // container is parent if fit width
32339         var container = this.isFitWidth ? this.element.parentNode : this.element;
32340         // check that this.size and size are there
32341         // IE8 triggers resize on body size change, so they might not be
32342         
32343         var size = getSize( container );  //FIXME
32344         this.containerWidth = size && size.innerWidth; //FIXME
32345         */
32346          
32347         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32348         
32349     },
32350     
32351     _getItemLayoutPosition : function( item )  // what is item?
32352     {
32353         // we resize the item to our columnWidth..
32354       
32355         item.setWidth(this.columnWidth);
32356         item.autoBoxAdjust  = false;
32357         
32358         var sz = item.getSize();
32359  
32360         // how many columns does this brick span
32361         var remainder = this.containerWidth % this.columnWidth;
32362         
32363         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32364         // round if off by 1 pixel, otherwise use ceil
32365         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32366         colSpan = Math.min( colSpan, this.cols );
32367         
32368         // normally this should be '1' as we dont' currently allow multi width columns..
32369         
32370         var colGroup = this._getColGroup( colSpan );
32371         // get the minimum Y value from the columns
32372         var minimumY = Math.min.apply( Math, colGroup );
32373         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32374         
32375         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32376          
32377         // position the brick
32378         var position = {
32379             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32380             y: this.currentSize.y + minimumY + this.padHeight
32381         };
32382         
32383         Roo.log(position);
32384         // apply setHeight to necessary columns
32385         var setHeight = minimumY + sz.height + this.padHeight;
32386         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32387         
32388         var setSpan = this.cols + 1 - colGroup.length;
32389         for ( var i = 0; i < setSpan; i++ ) {
32390           this.colYs[ shortColIndex + i ] = setHeight ;
32391         }
32392       
32393         return position;
32394     },
32395     
32396     /**
32397      * @param {Number} colSpan - number of columns the element spans
32398      * @returns {Array} colGroup
32399      */
32400     _getColGroup : function( colSpan )
32401     {
32402         if ( colSpan < 2 ) {
32403           // if brick spans only one column, use all the column Ys
32404           return this.colYs;
32405         }
32406       
32407         var colGroup = [];
32408         // how many different places could this brick fit horizontally
32409         var groupCount = this.cols + 1 - colSpan;
32410         // for each group potential horizontal position
32411         for ( var i = 0; i < groupCount; i++ ) {
32412           // make an array of colY values for that one group
32413           var groupColYs = this.colYs.slice( i, i + colSpan );
32414           // and get the max value of the array
32415           colGroup[i] = Math.max.apply( Math, groupColYs );
32416         }
32417         return colGroup;
32418     },
32419     /*
32420     _manageStamp : function( stamp )
32421     {
32422         var stampSize =  stamp.getSize();
32423         var offset = stamp.getBox();
32424         // get the columns that this stamp affects
32425         var firstX = this.isOriginLeft ? offset.x : offset.right;
32426         var lastX = firstX + stampSize.width;
32427         var firstCol = Math.floor( firstX / this.columnWidth );
32428         firstCol = Math.max( 0, firstCol );
32429         
32430         var lastCol = Math.floor( lastX / this.columnWidth );
32431         // lastCol should not go over if multiple of columnWidth #425
32432         lastCol -= lastX % this.columnWidth ? 0 : 1;
32433         lastCol = Math.min( this.cols - 1, lastCol );
32434         
32435         // set colYs to bottom of the stamp
32436         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32437             stampSize.height;
32438             
32439         for ( var i = firstCol; i <= lastCol; i++ ) {
32440           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32441         }
32442     },
32443     */
32444     
32445     _getContainerSize : function()
32446     {
32447         this.maxY = Math.max.apply( Math, this.colYs );
32448         var size = {
32449             height: this.maxY
32450         };
32451       
32452         if ( this.isFitWidth ) {
32453             size.width = this._getContainerFitWidth();
32454         }
32455       
32456         return size;
32457     },
32458     
32459     _getContainerFitWidth : function()
32460     {
32461         var unusedCols = 0;
32462         // count unused columns
32463         var i = this.cols;
32464         while ( --i ) {
32465           if ( this.colYs[i] !== 0 ) {
32466             break;
32467           }
32468           unusedCols++;
32469         }
32470         // fit container to columns that have been used
32471         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32472     },
32473     
32474     needsResizeLayout : function()
32475     {
32476         var previousWidth = this.containerWidth;
32477         this.getContainerWidth();
32478         return previousWidth !== this.containerWidth;
32479     }
32480  
32481 });
32482
32483  
32484
32485  /*
32486  * - LGPL
32487  *
32488  * element
32489  * 
32490  */
32491
32492 /**
32493  * @class Roo.bootstrap.MasonryBrick
32494  * @extends Roo.bootstrap.Component
32495  * Bootstrap MasonryBrick class
32496  * 
32497  * @constructor
32498  * Create a new MasonryBrick
32499  * @param {Object} config The config object
32500  */
32501
32502 Roo.bootstrap.MasonryBrick = function(config){
32503     
32504     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32505     
32506     Roo.bootstrap.MasonryBrick.register(this);
32507     
32508     this.addEvents({
32509         // raw events
32510         /**
32511          * @event click
32512          * When a MasonryBrick is clcik
32513          * @param {Roo.bootstrap.MasonryBrick} this
32514          * @param {Roo.EventObject} e
32515          */
32516         "click" : true
32517     });
32518 };
32519
32520 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32521     
32522     /**
32523      * @cfg {String} title
32524      */   
32525     title : '',
32526     /**
32527      * @cfg {String} html
32528      */   
32529     html : '',
32530     /**
32531      * @cfg {String} bgimage
32532      */   
32533     bgimage : '',
32534     /**
32535      * @cfg {String} videourl
32536      */   
32537     videourl : '',
32538     /**
32539      * @cfg {String} cls
32540      */   
32541     cls : '',
32542     /**
32543      * @cfg {String} href
32544      */   
32545     href : '',
32546     /**
32547      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32548      */   
32549     size : 'xs',
32550     
32551     /**
32552      * @cfg {String} placetitle (center|bottom)
32553      */   
32554     placetitle : '',
32555     
32556     /**
32557      * @cfg {Boolean} isFitContainer defalut true
32558      */   
32559     isFitContainer : true, 
32560     
32561     /**
32562      * @cfg {Boolean} preventDefault defalut false
32563      */   
32564     preventDefault : false, 
32565     
32566     /**
32567      * @cfg {Boolean} inverse defalut false
32568      */   
32569     maskInverse : false, 
32570     
32571     getAutoCreate : function()
32572     {
32573         if(!this.isFitContainer){
32574             return this.getSplitAutoCreate();
32575         }
32576         
32577         var cls = 'masonry-brick masonry-brick-full';
32578         
32579         if(this.href.length){
32580             cls += ' masonry-brick-link';
32581         }
32582         
32583         if(this.bgimage.length){
32584             cls += ' masonry-brick-image';
32585         }
32586         
32587         if(this.maskInverse){
32588             cls += ' mask-inverse';
32589         }
32590         
32591         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32592             cls += ' enable-mask';
32593         }
32594         
32595         if(this.size){
32596             cls += ' masonry-' + this.size + '-brick';
32597         }
32598         
32599         if(this.placetitle.length){
32600             
32601             switch (this.placetitle) {
32602                 case 'center' :
32603                     cls += ' masonry-center-title';
32604                     break;
32605                 case 'bottom' :
32606                     cls += ' masonry-bottom-title';
32607                     break;
32608                 default:
32609                     break;
32610             }
32611             
32612         } else {
32613             if(!this.html.length && !this.bgimage.length){
32614                 cls += ' masonry-center-title';
32615             }
32616
32617             if(!this.html.length && this.bgimage.length){
32618                 cls += ' masonry-bottom-title';
32619             }
32620         }
32621         
32622         if(this.cls){
32623             cls += ' ' + this.cls;
32624         }
32625         
32626         var cfg = {
32627             tag: (this.href.length) ? 'a' : 'div',
32628             cls: cls,
32629             cn: [
32630                 {
32631                     tag: 'div',
32632                     cls: 'masonry-brick-mask'
32633                 },
32634                 {
32635                     tag: 'div',
32636                     cls: 'masonry-brick-paragraph',
32637                     cn: []
32638                 }
32639             ]
32640         };
32641         
32642         if(this.href.length){
32643             cfg.href = this.href;
32644         }
32645         
32646         var cn = cfg.cn[1].cn;
32647         
32648         if(this.title.length){
32649             cn.push({
32650                 tag: 'h4',
32651                 cls: 'masonry-brick-title',
32652                 html: this.title
32653             });
32654         }
32655         
32656         if(this.html.length){
32657             cn.push({
32658                 tag: 'p',
32659                 cls: 'masonry-brick-text',
32660                 html: this.html
32661             });
32662         }
32663         
32664         if (!this.title.length && !this.html.length) {
32665             cfg.cn[1].cls += ' hide';
32666         }
32667         
32668         if(this.bgimage.length){
32669             cfg.cn.push({
32670                 tag: 'img',
32671                 cls: 'masonry-brick-image-view',
32672                 src: this.bgimage
32673             });
32674         }
32675         
32676         if(this.videourl.length){
32677             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32678             // youtube support only?
32679             cfg.cn.push({
32680                 tag: 'iframe',
32681                 cls: 'masonry-brick-image-view',
32682                 src: vurl,
32683                 frameborder : 0,
32684                 allowfullscreen : true
32685             });
32686         }
32687         
32688         return cfg;
32689         
32690     },
32691     
32692     getSplitAutoCreate : function()
32693     {
32694         var cls = 'masonry-brick masonry-brick-split';
32695         
32696         if(this.href.length){
32697             cls += ' masonry-brick-link';
32698         }
32699         
32700         if(this.bgimage.length){
32701             cls += ' masonry-brick-image';
32702         }
32703         
32704         if(this.size){
32705             cls += ' masonry-' + this.size + '-brick';
32706         }
32707         
32708         switch (this.placetitle) {
32709             case 'center' :
32710                 cls += ' masonry-center-title';
32711                 break;
32712             case 'bottom' :
32713                 cls += ' masonry-bottom-title';
32714                 break;
32715             default:
32716                 if(!this.bgimage.length){
32717                     cls += ' masonry-center-title';
32718                 }
32719
32720                 if(this.bgimage.length){
32721                     cls += ' masonry-bottom-title';
32722                 }
32723                 break;
32724         }
32725         
32726         if(this.cls){
32727             cls += ' ' + this.cls;
32728         }
32729         
32730         var cfg = {
32731             tag: (this.href.length) ? 'a' : 'div',
32732             cls: cls,
32733             cn: [
32734                 {
32735                     tag: 'div',
32736                     cls: 'masonry-brick-split-head',
32737                     cn: [
32738                         {
32739                             tag: 'div',
32740                             cls: 'masonry-brick-paragraph',
32741                             cn: []
32742                         }
32743                     ]
32744                 },
32745                 {
32746                     tag: 'div',
32747                     cls: 'masonry-brick-split-body',
32748                     cn: []
32749                 }
32750             ]
32751         };
32752         
32753         if(this.href.length){
32754             cfg.href = this.href;
32755         }
32756         
32757         if(this.title.length){
32758             cfg.cn[0].cn[0].cn.push({
32759                 tag: 'h4',
32760                 cls: 'masonry-brick-title',
32761                 html: this.title
32762             });
32763         }
32764         
32765         if(this.html.length){
32766             cfg.cn[1].cn.push({
32767                 tag: 'p',
32768                 cls: 'masonry-brick-text',
32769                 html: this.html
32770             });
32771         }
32772
32773         if(this.bgimage.length){
32774             cfg.cn[0].cn.push({
32775                 tag: 'img',
32776                 cls: 'masonry-brick-image-view',
32777                 src: this.bgimage
32778             });
32779         }
32780         
32781         if(this.videourl.length){
32782             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32783             // youtube support only?
32784             cfg.cn[0].cn.cn.push({
32785                 tag: 'iframe',
32786                 cls: 'masonry-brick-image-view',
32787                 src: vurl,
32788                 frameborder : 0,
32789                 allowfullscreen : true
32790             });
32791         }
32792         
32793         return cfg;
32794     },
32795     
32796     initEvents: function() 
32797     {
32798         switch (this.size) {
32799             case 'xs' :
32800                 this.x = 1;
32801                 this.y = 1;
32802                 break;
32803             case 'sm' :
32804                 this.x = 2;
32805                 this.y = 2;
32806                 break;
32807             case 'md' :
32808             case 'md-left' :
32809             case 'md-right' :
32810                 this.x = 3;
32811                 this.y = 3;
32812                 break;
32813             case 'tall' :
32814                 this.x = 2;
32815                 this.y = 3;
32816                 break;
32817             case 'wide' :
32818                 this.x = 3;
32819                 this.y = 2;
32820                 break;
32821             case 'wide-thin' :
32822                 this.x = 3;
32823                 this.y = 1;
32824                 break;
32825                         
32826             default :
32827                 break;
32828         }
32829         
32830         if(Roo.isTouch){
32831             this.el.on('touchstart', this.onTouchStart, this);
32832             this.el.on('touchmove', this.onTouchMove, this);
32833             this.el.on('touchend', this.onTouchEnd, this);
32834             this.el.on('contextmenu', this.onContextMenu, this);
32835         } else {
32836             this.el.on('mouseenter'  ,this.enter, this);
32837             this.el.on('mouseleave', this.leave, this);
32838             this.el.on('click', this.onClick, this);
32839         }
32840         
32841         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32842             this.parent().bricks.push(this);   
32843         }
32844         
32845     },
32846     
32847     onClick: function(e, el)
32848     {
32849         var time = this.endTimer - this.startTimer;
32850         // Roo.log(e.preventDefault());
32851         if(Roo.isTouch){
32852             if(time > 1000){
32853                 e.preventDefault();
32854                 return;
32855             }
32856         }
32857         
32858         if(!this.preventDefault){
32859             return;
32860         }
32861         
32862         e.preventDefault();
32863         
32864         if (this.activeClass != '') {
32865             this.selectBrick();
32866         }
32867         
32868         this.fireEvent('click', this, e);
32869     },
32870     
32871     enter: function(e, el)
32872     {
32873         e.preventDefault();
32874         
32875         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32876             return;
32877         }
32878         
32879         if(this.bgimage.length && this.html.length){
32880             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32881         }
32882     },
32883     
32884     leave: function(e, el)
32885     {
32886         e.preventDefault();
32887         
32888         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32889             return;
32890         }
32891         
32892         if(this.bgimage.length && this.html.length){
32893             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32894         }
32895     },
32896     
32897     onTouchStart: function(e, el)
32898     {
32899 //        e.preventDefault();
32900         
32901         this.touchmoved = false;
32902         
32903         if(!this.isFitContainer){
32904             return;
32905         }
32906         
32907         if(!this.bgimage.length || !this.html.length){
32908             return;
32909         }
32910         
32911         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32912         
32913         this.timer = new Date().getTime();
32914         
32915     },
32916     
32917     onTouchMove: function(e, el)
32918     {
32919         this.touchmoved = true;
32920     },
32921     
32922     onContextMenu : function(e,el)
32923     {
32924         e.preventDefault();
32925         e.stopPropagation();
32926         return false;
32927     },
32928     
32929     onTouchEnd: function(e, el)
32930     {
32931 //        e.preventDefault();
32932         
32933         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32934         
32935             this.leave(e,el);
32936             
32937             return;
32938         }
32939         
32940         if(!this.bgimage.length || !this.html.length){
32941             
32942             if(this.href.length){
32943                 window.location.href = this.href;
32944             }
32945             
32946             return;
32947         }
32948         
32949         if(!this.isFitContainer){
32950             return;
32951         }
32952         
32953         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32954         
32955         window.location.href = this.href;
32956     },
32957     
32958     //selection on single brick only
32959     selectBrick : function() {
32960         
32961         if (!this.parentId) {
32962             return;
32963         }
32964         
32965         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32966         var index = m.selectedBrick.indexOf(this.id);
32967         
32968         if ( index > -1) {
32969             m.selectedBrick.splice(index,1);
32970             this.el.removeClass(this.activeClass);
32971             return;
32972         }
32973         
32974         for(var i = 0; i < m.selectedBrick.length; i++) {
32975             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32976             b.el.removeClass(b.activeClass);
32977         }
32978         
32979         m.selectedBrick = [];
32980         
32981         m.selectedBrick.push(this.id);
32982         this.el.addClass(this.activeClass);
32983         return;
32984     },
32985     
32986     isSelected : function(){
32987         return this.el.hasClass(this.activeClass);
32988         
32989     }
32990 });
32991
32992 Roo.apply(Roo.bootstrap.MasonryBrick, {
32993     
32994     //groups: {},
32995     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32996      /**
32997     * register a Masonry Brick
32998     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32999     */
33000     
33001     register : function(brick)
33002     {
33003         //this.groups[brick.id] = brick;
33004         this.groups.add(brick.id, brick);
33005     },
33006     /**
33007     * fetch a  masonry brick based on the masonry brick ID
33008     * @param {string} the masonry brick to add
33009     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33010     */
33011     
33012     get: function(brick_id) 
33013     {
33014         // if (typeof(this.groups[brick_id]) == 'undefined') {
33015         //     return false;
33016         // }
33017         // return this.groups[brick_id] ;
33018         
33019         if(this.groups.key(brick_id)) {
33020             return this.groups.key(brick_id);
33021         }
33022         
33023         return false;
33024     }
33025     
33026     
33027     
33028 });
33029
33030  /*
33031  * - LGPL
33032  *
33033  * element
33034  * 
33035  */
33036
33037 /**
33038  * @class Roo.bootstrap.Brick
33039  * @extends Roo.bootstrap.Component
33040  * Bootstrap Brick class
33041  * 
33042  * @constructor
33043  * Create a new Brick
33044  * @param {Object} config The config object
33045  */
33046
33047 Roo.bootstrap.Brick = function(config){
33048     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33049     
33050     this.addEvents({
33051         // raw events
33052         /**
33053          * @event click
33054          * When a Brick is click
33055          * @param {Roo.bootstrap.Brick} this
33056          * @param {Roo.EventObject} e
33057          */
33058         "click" : true
33059     });
33060 };
33061
33062 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33063     
33064     /**
33065      * @cfg {String} title
33066      */   
33067     title : '',
33068     /**
33069      * @cfg {String} html
33070      */   
33071     html : '',
33072     /**
33073      * @cfg {String} bgimage
33074      */   
33075     bgimage : '',
33076     /**
33077      * @cfg {String} cls
33078      */   
33079     cls : '',
33080     /**
33081      * @cfg {String} href
33082      */   
33083     href : '',
33084     /**
33085      * @cfg {String} video
33086      */   
33087     video : '',
33088     /**
33089      * @cfg {Boolean} square
33090      */   
33091     square : true,
33092     
33093     getAutoCreate : function()
33094     {
33095         var cls = 'roo-brick';
33096         
33097         if(this.href.length){
33098             cls += ' roo-brick-link';
33099         }
33100         
33101         if(this.bgimage.length){
33102             cls += ' roo-brick-image';
33103         }
33104         
33105         if(!this.html.length && !this.bgimage.length){
33106             cls += ' roo-brick-center-title';
33107         }
33108         
33109         if(!this.html.length && this.bgimage.length){
33110             cls += ' roo-brick-bottom-title';
33111         }
33112         
33113         if(this.cls){
33114             cls += ' ' + this.cls;
33115         }
33116         
33117         var cfg = {
33118             tag: (this.href.length) ? 'a' : 'div',
33119             cls: cls,
33120             cn: [
33121                 {
33122                     tag: 'div',
33123                     cls: 'roo-brick-paragraph',
33124                     cn: []
33125                 }
33126             ]
33127         };
33128         
33129         if(this.href.length){
33130             cfg.href = this.href;
33131         }
33132         
33133         var cn = cfg.cn[0].cn;
33134         
33135         if(this.title.length){
33136             cn.push({
33137                 tag: 'h4',
33138                 cls: 'roo-brick-title',
33139                 html: this.title
33140             });
33141         }
33142         
33143         if(this.html.length){
33144             cn.push({
33145                 tag: 'p',
33146                 cls: 'roo-brick-text',
33147                 html: this.html
33148             });
33149         } else {
33150             cn.cls += ' hide';
33151         }
33152         
33153         if(this.bgimage.length){
33154             cfg.cn.push({
33155                 tag: 'img',
33156                 cls: 'roo-brick-image-view',
33157                 src: this.bgimage
33158             });
33159         }
33160         
33161         return cfg;
33162     },
33163     
33164     initEvents: function() 
33165     {
33166         if(this.title.length || this.html.length){
33167             this.el.on('mouseenter'  ,this.enter, this);
33168             this.el.on('mouseleave', this.leave, this);
33169         }
33170         
33171         Roo.EventManager.onWindowResize(this.resize, this); 
33172         
33173         if(this.bgimage.length){
33174             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33175             this.imageEl.on('load', this.onImageLoad, this);
33176             return;
33177         }
33178         
33179         this.resize();
33180     },
33181     
33182     onImageLoad : function()
33183     {
33184         this.resize();
33185     },
33186     
33187     resize : function()
33188     {
33189         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33190         
33191         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33192         
33193         if(this.bgimage.length){
33194             var image = this.el.select('.roo-brick-image-view', true).first();
33195             
33196             image.setWidth(paragraph.getWidth());
33197             
33198             if(this.square){
33199                 image.setHeight(paragraph.getWidth());
33200             }
33201             
33202             this.el.setHeight(image.getHeight());
33203             paragraph.setHeight(image.getHeight());
33204             
33205         }
33206         
33207     },
33208     
33209     enter: function(e, el)
33210     {
33211         e.preventDefault();
33212         
33213         if(this.bgimage.length){
33214             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33215             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33216         }
33217     },
33218     
33219     leave: function(e, el)
33220     {
33221         e.preventDefault();
33222         
33223         if(this.bgimage.length){
33224             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33225             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33226         }
33227     }
33228     
33229 });
33230
33231  
33232
33233  /*
33234  * - LGPL
33235  *
33236  * Number field 
33237  */
33238
33239 /**
33240  * @class Roo.bootstrap.NumberField
33241  * @extends Roo.bootstrap.Input
33242  * Bootstrap NumberField class
33243  * 
33244  * 
33245  * 
33246  * 
33247  * @constructor
33248  * Create a new NumberField
33249  * @param {Object} config The config object
33250  */
33251
33252 Roo.bootstrap.NumberField = function(config){
33253     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33254 };
33255
33256 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33257     
33258     /**
33259      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33260      */
33261     allowDecimals : true,
33262     /**
33263      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33264      */
33265     decimalSeparator : ".",
33266     /**
33267      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33268      */
33269     decimalPrecision : 2,
33270     /**
33271      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33272      */
33273     allowNegative : true,
33274     
33275     /**
33276      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33277      */
33278     allowZero: true,
33279     /**
33280      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33281      */
33282     minValue : Number.NEGATIVE_INFINITY,
33283     /**
33284      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33285      */
33286     maxValue : Number.MAX_VALUE,
33287     /**
33288      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33289      */
33290     minText : "The minimum value for this field is {0}",
33291     /**
33292      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33293      */
33294     maxText : "The maximum value for this field is {0}",
33295     /**
33296      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33297      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33298      */
33299     nanText : "{0} is not a valid number",
33300     /**
33301      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33302      */
33303     thousandsDelimiter : false,
33304     /**
33305      * @cfg {String} valueAlign alignment of value
33306      */
33307     valueAlign : "left",
33308
33309     getAutoCreate : function()
33310     {
33311         var hiddenInput = {
33312             tag: 'input',
33313             type: 'hidden',
33314             id: Roo.id(),
33315             cls: 'hidden-number-input'
33316         };
33317         
33318         if (this.name) {
33319             hiddenInput.name = this.name;
33320         }
33321         
33322         this.name = '';
33323         
33324         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33325         
33326         this.name = hiddenInput.name;
33327         
33328         if(cfg.cn.length > 0) {
33329             cfg.cn.push(hiddenInput);
33330         }
33331         
33332         return cfg;
33333     },
33334
33335     // private
33336     initEvents : function()
33337     {   
33338         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33339         
33340         var allowed = "0123456789";
33341         
33342         if(this.allowDecimals){
33343             allowed += this.decimalSeparator;
33344         }
33345         
33346         if(this.allowNegative){
33347             allowed += "-";
33348         }
33349         
33350         if(this.thousandsDelimiter) {
33351             allowed += ",";
33352         }
33353         
33354         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33355         
33356         var keyPress = function(e){
33357             
33358             var k = e.getKey();
33359             
33360             var c = e.getCharCode();
33361             
33362             if(
33363                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33364                     allowed.indexOf(String.fromCharCode(c)) === -1
33365             ){
33366                 e.stopEvent();
33367                 return;
33368             }
33369             
33370             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33371                 return;
33372             }
33373             
33374             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33375                 e.stopEvent();
33376             }
33377         };
33378         
33379         this.el.on("keypress", keyPress, this);
33380     },
33381     
33382     validateValue : function(value)
33383     {
33384         
33385         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33386             return false;
33387         }
33388         
33389         var num = this.parseValue(value);
33390         
33391         if(isNaN(num)){
33392             this.markInvalid(String.format(this.nanText, value));
33393             return false;
33394         }
33395         
33396         if(num < this.minValue){
33397             this.markInvalid(String.format(this.minText, this.minValue));
33398             return false;
33399         }
33400         
33401         if(num > this.maxValue){
33402             this.markInvalid(String.format(this.maxText, this.maxValue));
33403             return false;
33404         }
33405         
33406         return true;
33407     },
33408
33409     getValue : function()
33410     {
33411         var v = this.hiddenEl().getValue();
33412         
33413         return this.fixPrecision(this.parseValue(v));
33414     },
33415
33416     parseValue : function(value)
33417     {
33418         if(this.thousandsDelimiter) {
33419             value += "";
33420             r = new RegExp(",", "g");
33421             value = value.replace(r, "");
33422         }
33423         
33424         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33425         return isNaN(value) ? '' : value;
33426     },
33427
33428     fixPrecision : function(value)
33429     {
33430         if(this.thousandsDelimiter) {
33431             value += "";
33432             r = new RegExp(",", "g");
33433             value = value.replace(r, "");
33434         }
33435         
33436         var nan = isNaN(value);
33437         
33438         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33439             return nan ? '' : value;
33440         }
33441         return parseFloat(value).toFixed(this.decimalPrecision);
33442     },
33443
33444     setValue : function(v)
33445     {
33446         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33447         
33448         this.value = v;
33449         
33450         if(this.rendered){
33451             
33452             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33453             
33454             this.inputEl().dom.value = (v == '') ? '' :
33455                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33456             
33457             if(!this.allowZero && v === '0') {
33458                 this.hiddenEl().dom.value = '';
33459                 this.inputEl().dom.value = '';
33460             }
33461             
33462             this.validate();
33463         }
33464     },
33465
33466     decimalPrecisionFcn : function(v)
33467     {
33468         return Math.floor(v);
33469     },
33470
33471     beforeBlur : function()
33472     {
33473         var v = this.parseValue(this.getRawValue());
33474         
33475         if(v || v === 0 || v === ''){
33476             this.setValue(v);
33477         }
33478     },
33479     
33480     hiddenEl : function()
33481     {
33482         return this.el.select('input.hidden-number-input',true).first();
33483     }
33484     
33485 });
33486
33487  
33488
33489 /*
33490 * Licence: LGPL
33491 */
33492
33493 /**
33494  * @class Roo.bootstrap.DocumentSlider
33495  * @extends Roo.bootstrap.Component
33496  * Bootstrap DocumentSlider class
33497  * 
33498  * @constructor
33499  * Create a new DocumentViewer
33500  * @param {Object} config The config object
33501  */
33502
33503 Roo.bootstrap.DocumentSlider = function(config){
33504     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33505     
33506     this.files = [];
33507     
33508     this.addEvents({
33509         /**
33510          * @event initial
33511          * Fire after initEvent
33512          * @param {Roo.bootstrap.DocumentSlider} this
33513          */
33514         "initial" : true,
33515         /**
33516          * @event update
33517          * Fire after update
33518          * @param {Roo.bootstrap.DocumentSlider} this
33519          */
33520         "update" : true,
33521         /**
33522          * @event click
33523          * Fire after click
33524          * @param {Roo.bootstrap.DocumentSlider} this
33525          */
33526         "click" : true
33527     });
33528 };
33529
33530 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33531     
33532     files : false,
33533     
33534     indicator : 0,
33535     
33536     getAutoCreate : function()
33537     {
33538         var cfg = {
33539             tag : 'div',
33540             cls : 'roo-document-slider',
33541             cn : [
33542                 {
33543                     tag : 'div',
33544                     cls : 'roo-document-slider-header',
33545                     cn : [
33546                         {
33547                             tag : 'div',
33548                             cls : 'roo-document-slider-header-title'
33549                         }
33550                     ]
33551                 },
33552                 {
33553                     tag : 'div',
33554                     cls : 'roo-document-slider-body',
33555                     cn : [
33556                         {
33557                             tag : 'div',
33558                             cls : 'roo-document-slider-prev',
33559                             cn : [
33560                                 {
33561                                     tag : 'i',
33562                                     cls : 'fa fa-chevron-left'
33563                                 }
33564                             ]
33565                         },
33566                         {
33567                             tag : 'div',
33568                             cls : 'roo-document-slider-thumb',
33569                             cn : [
33570                                 {
33571                                     tag : 'img',
33572                                     cls : 'roo-document-slider-image'
33573                                 }
33574                             ]
33575                         },
33576                         {
33577                             tag : 'div',
33578                             cls : 'roo-document-slider-next',
33579                             cn : [
33580                                 {
33581                                     tag : 'i',
33582                                     cls : 'fa fa-chevron-right'
33583                                 }
33584                             ]
33585                         }
33586                     ]
33587                 }
33588             ]
33589         };
33590         
33591         return cfg;
33592     },
33593     
33594     initEvents : function()
33595     {
33596         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33597         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33598         
33599         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33600         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33601         
33602         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33603         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33604         
33605         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33606         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33607         
33608         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33609         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33610         
33611         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33612         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33613         
33614         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33615         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33616         
33617         this.thumbEl.on('click', this.onClick, this);
33618         
33619         this.prevIndicator.on('click', this.prev, this);
33620         
33621         this.nextIndicator.on('click', this.next, this);
33622         
33623     },
33624     
33625     initial : function()
33626     {
33627         if(this.files.length){
33628             this.indicator = 1;
33629             this.update()
33630         }
33631         
33632         this.fireEvent('initial', this);
33633     },
33634     
33635     update : function()
33636     {
33637         this.imageEl.attr('src', this.files[this.indicator - 1]);
33638         
33639         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33640         
33641         this.prevIndicator.show();
33642         
33643         if(this.indicator == 1){
33644             this.prevIndicator.hide();
33645         }
33646         
33647         this.nextIndicator.show();
33648         
33649         if(this.indicator == this.files.length){
33650             this.nextIndicator.hide();
33651         }
33652         
33653         this.thumbEl.scrollTo('top');
33654         
33655         this.fireEvent('update', this);
33656     },
33657     
33658     onClick : function(e)
33659     {
33660         e.preventDefault();
33661         
33662         this.fireEvent('click', this);
33663     },
33664     
33665     prev : function(e)
33666     {
33667         e.preventDefault();
33668         
33669         this.indicator = Math.max(1, this.indicator - 1);
33670         
33671         this.update();
33672     },
33673     
33674     next : function(e)
33675     {
33676         e.preventDefault();
33677         
33678         this.indicator = Math.min(this.files.length, this.indicator + 1);
33679         
33680         this.update();
33681     }
33682 });
33683 /*
33684  * - LGPL
33685  *
33686  * RadioSet
33687  *
33688  *
33689  */
33690
33691 /**
33692  * @class Roo.bootstrap.RadioSet
33693  * @extends Roo.bootstrap.Input
33694  * Bootstrap RadioSet class
33695  * @cfg {String} indicatorpos (left|right) default left
33696  * @cfg {Boolean} inline (true|false) inline the element (default true)
33697  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33698  * @constructor
33699  * Create a new RadioSet
33700  * @param {Object} config The config object
33701  */
33702
33703 Roo.bootstrap.RadioSet = function(config){
33704     
33705     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33706     
33707     this.radioes = [];
33708     
33709     Roo.bootstrap.RadioSet.register(this);
33710     
33711     this.addEvents({
33712         /**
33713         * @event check
33714         * Fires when the element is checked or unchecked.
33715         * @param {Roo.bootstrap.RadioSet} this This radio
33716         * @param {Roo.bootstrap.Radio} item The checked item
33717         */
33718        check : true,
33719        /**
33720         * @event click
33721         * Fires when the element is click.
33722         * @param {Roo.bootstrap.RadioSet} this This radio set
33723         * @param {Roo.bootstrap.Radio} item The checked item
33724         * @param {Roo.EventObject} e The event object
33725         */
33726        click : true
33727     });
33728     
33729 };
33730
33731 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33732
33733     radioes : false,
33734     
33735     inline : true,
33736     
33737     weight : '',
33738     
33739     indicatorpos : 'left',
33740     
33741     getAutoCreate : function()
33742     {
33743         var label = {
33744             tag : 'label',
33745             cls : 'roo-radio-set-label',
33746             cn : [
33747                 {
33748                     tag : 'span',
33749                     html : this.fieldLabel
33750                 }
33751             ]
33752         };
33753         
33754         if(this.indicatorpos == 'left'){
33755             label.cn.unshift({
33756                 tag : 'i',
33757                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33758                 tooltip : 'This field is required'
33759             });
33760         } else {
33761             label.cn.push({
33762                 tag : 'i',
33763                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33764                 tooltip : 'This field is required'
33765             });
33766         }
33767         
33768         var items = {
33769             tag : 'div',
33770             cls : 'roo-radio-set-items'
33771         };
33772         
33773         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33774         
33775         if (align === 'left' && this.fieldLabel.length) {
33776             
33777             items = {
33778                 cls : "roo-radio-set-right", 
33779                 cn: [
33780                     items
33781                 ]
33782             };
33783             
33784             if(this.labelWidth > 12){
33785                 label.style = "width: " + this.labelWidth + 'px';
33786             }
33787             
33788             if(this.labelWidth < 13 && this.labelmd == 0){
33789                 this.labelmd = this.labelWidth;
33790             }
33791             
33792             if(this.labellg > 0){
33793                 label.cls += ' col-lg-' + this.labellg;
33794                 items.cls += ' col-lg-' + (12 - this.labellg);
33795             }
33796             
33797             if(this.labelmd > 0){
33798                 label.cls += ' col-md-' + this.labelmd;
33799                 items.cls += ' col-md-' + (12 - this.labelmd);
33800             }
33801             
33802             if(this.labelsm > 0){
33803                 label.cls += ' col-sm-' + this.labelsm;
33804                 items.cls += ' col-sm-' + (12 - this.labelsm);
33805             }
33806             
33807             if(this.labelxs > 0){
33808                 label.cls += ' col-xs-' + this.labelxs;
33809                 items.cls += ' col-xs-' + (12 - this.labelxs);
33810             }
33811         }
33812         
33813         var cfg = {
33814             tag : 'div',
33815             cls : 'roo-radio-set',
33816             cn : [
33817                 {
33818                     tag : 'input',
33819                     cls : 'roo-radio-set-input',
33820                     type : 'hidden',
33821                     name : this.name,
33822                     value : this.value ? this.value :  ''
33823                 },
33824                 label,
33825                 items
33826             ]
33827         };
33828         
33829         if(this.weight.length){
33830             cfg.cls += ' roo-radio-' + this.weight;
33831         }
33832         
33833         if(this.inline) {
33834             cfg.cls += ' roo-radio-set-inline';
33835         }
33836         
33837         var settings=this;
33838         ['xs','sm','md','lg'].map(function(size){
33839             if (settings[size]) {
33840                 cfg.cls += ' col-' + size + '-' + settings[size];
33841             }
33842         });
33843         
33844         return cfg;
33845         
33846     },
33847
33848     initEvents : function()
33849     {
33850         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33851         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33852         
33853         if(!this.fieldLabel.length){
33854             this.labelEl.hide();
33855         }
33856         
33857         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33858         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33859         
33860         this.indicator = this.indicatorEl();
33861         
33862         if(this.indicator){
33863             this.indicator.addClass('invisible');
33864         }
33865         
33866         this.originalValue = this.getValue();
33867         
33868     },
33869     
33870     inputEl: function ()
33871     {
33872         return this.el.select('.roo-radio-set-input', true).first();
33873     },
33874     
33875     getChildContainer : function()
33876     {
33877         return this.itemsEl;
33878     },
33879     
33880     register : function(item)
33881     {
33882         this.radioes.push(item);
33883         
33884     },
33885     
33886     validate : function()
33887     {   
33888         if(this.getVisibilityEl().hasClass('hidden')){
33889             return true;
33890         }
33891         
33892         var valid = false;
33893         
33894         Roo.each(this.radioes, function(i){
33895             if(!i.checked){
33896                 return;
33897             }
33898             
33899             valid = true;
33900             return false;
33901         });
33902         
33903         if(this.allowBlank) {
33904             return true;
33905         }
33906         
33907         if(this.disabled || valid){
33908             this.markValid();
33909             return true;
33910         }
33911         
33912         this.markInvalid();
33913         return false;
33914         
33915     },
33916     
33917     markValid : function()
33918     {
33919         if(this.labelEl.isVisible(true)){
33920             this.indicatorEl().removeClass('visible');
33921             this.indicatorEl().addClass('invisible');
33922         }
33923         
33924         this.el.removeClass([this.invalidClass, this.validClass]);
33925         this.el.addClass(this.validClass);
33926         
33927         this.fireEvent('valid', this);
33928     },
33929     
33930     markInvalid : function(msg)
33931     {
33932         if(this.allowBlank || this.disabled){
33933             return;
33934         }
33935         
33936         if(this.labelEl.isVisible(true)){
33937             this.indicatorEl().removeClass('invisible');
33938             this.indicatorEl().addClass('visible');
33939         }
33940         
33941         this.el.removeClass([this.invalidClass, this.validClass]);
33942         this.el.addClass(this.invalidClass);
33943         
33944         this.fireEvent('invalid', this, msg);
33945         
33946     },
33947     
33948     setValue : function(v, suppressEvent)
33949     {   
33950         if(this.value === v){
33951             return;
33952         }
33953         
33954         this.value = v;
33955         
33956         if(this.rendered){
33957             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33958         }
33959         
33960         Roo.each(this.radioes, function(i){
33961             i.checked = false;
33962             i.el.removeClass('checked');
33963         });
33964         
33965         Roo.each(this.radioes, function(i){
33966             
33967             if(i.value === v || i.value.toString() === v.toString()){
33968                 i.checked = true;
33969                 i.el.addClass('checked');
33970                 
33971                 if(suppressEvent !== true){
33972                     this.fireEvent('check', this, i);
33973                 }
33974                 
33975                 return false;
33976             }
33977             
33978         }, this);
33979         
33980         this.validate();
33981     },
33982     
33983     clearInvalid : function(){
33984         
33985         if(!this.el || this.preventMark){
33986             return;
33987         }
33988         
33989         this.el.removeClass([this.invalidClass]);
33990         
33991         this.fireEvent('valid', this);
33992     }
33993     
33994 });
33995
33996 Roo.apply(Roo.bootstrap.RadioSet, {
33997     
33998     groups: {},
33999     
34000     register : function(set)
34001     {
34002         this.groups[set.name] = set;
34003     },
34004     
34005     get: function(name) 
34006     {
34007         if (typeof(this.groups[name]) == 'undefined') {
34008             return false;
34009         }
34010         
34011         return this.groups[name] ;
34012     }
34013     
34014 });
34015 /*
34016  * Based on:
34017  * Ext JS Library 1.1.1
34018  * Copyright(c) 2006-2007, Ext JS, LLC.
34019  *
34020  * Originally Released Under LGPL - original licence link has changed is not relivant.
34021  *
34022  * Fork - LGPL
34023  * <script type="text/javascript">
34024  */
34025
34026
34027 /**
34028  * @class Roo.bootstrap.SplitBar
34029  * @extends Roo.util.Observable
34030  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34031  * <br><br>
34032  * Usage:
34033  * <pre><code>
34034 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34035                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34036 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34037 split.minSize = 100;
34038 split.maxSize = 600;
34039 split.animate = true;
34040 split.on('moved', splitterMoved);
34041 </code></pre>
34042  * @constructor
34043  * Create a new SplitBar
34044  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34045  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34046  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34047  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34048                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34049                         position of the SplitBar).
34050  */
34051 Roo.bootstrap.SplitBar = function(cfg){
34052     
34053     /** @private */
34054     
34055     //{
34056     //  dragElement : elm
34057     //  resizingElement: el,
34058         // optional..
34059     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34060     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34061         // existingProxy ???
34062     //}
34063     
34064     this.el = Roo.get(cfg.dragElement, true);
34065     this.el.dom.unselectable = "on";
34066     /** @private */
34067     this.resizingEl = Roo.get(cfg.resizingElement, true);
34068
34069     /**
34070      * @private
34071      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34072      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34073      * @type Number
34074      */
34075     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34076     
34077     /**
34078      * The minimum size of the resizing element. (Defaults to 0)
34079      * @type Number
34080      */
34081     this.minSize = 0;
34082     
34083     /**
34084      * The maximum size of the resizing element. (Defaults to 2000)
34085      * @type Number
34086      */
34087     this.maxSize = 2000;
34088     
34089     /**
34090      * Whether to animate the transition to the new size
34091      * @type Boolean
34092      */
34093     this.animate = false;
34094     
34095     /**
34096      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34097      * @type Boolean
34098      */
34099     this.useShim = false;
34100     
34101     /** @private */
34102     this.shim = null;
34103     
34104     if(!cfg.existingProxy){
34105         /** @private */
34106         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34107     }else{
34108         this.proxy = Roo.get(cfg.existingProxy).dom;
34109     }
34110     /** @private */
34111     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34112     
34113     /** @private */
34114     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34115     
34116     /** @private */
34117     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34118     
34119     /** @private */
34120     this.dragSpecs = {};
34121     
34122     /**
34123      * @private The adapter to use to positon and resize elements
34124      */
34125     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34126     this.adapter.init(this);
34127     
34128     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34129         /** @private */
34130         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34131         this.el.addClass("roo-splitbar-h");
34132     }else{
34133         /** @private */
34134         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34135         this.el.addClass("roo-splitbar-v");
34136     }
34137     
34138     this.addEvents({
34139         /**
34140          * @event resize
34141          * Fires when the splitter is moved (alias for {@link #event-moved})
34142          * @param {Roo.bootstrap.SplitBar} this
34143          * @param {Number} newSize the new width or height
34144          */
34145         "resize" : true,
34146         /**
34147          * @event moved
34148          * Fires when the splitter is moved
34149          * @param {Roo.bootstrap.SplitBar} this
34150          * @param {Number} newSize the new width or height
34151          */
34152         "moved" : true,
34153         /**
34154          * @event beforeresize
34155          * Fires before the splitter is dragged
34156          * @param {Roo.bootstrap.SplitBar} this
34157          */
34158         "beforeresize" : true,
34159
34160         "beforeapply" : true
34161     });
34162
34163     Roo.util.Observable.call(this);
34164 };
34165
34166 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34167     onStartProxyDrag : function(x, y){
34168         this.fireEvent("beforeresize", this);
34169         if(!this.overlay){
34170             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34171             o.unselectable();
34172             o.enableDisplayMode("block");
34173             // all splitbars share the same overlay
34174             Roo.bootstrap.SplitBar.prototype.overlay = o;
34175         }
34176         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34177         this.overlay.show();
34178         Roo.get(this.proxy).setDisplayed("block");
34179         var size = this.adapter.getElementSize(this);
34180         this.activeMinSize = this.getMinimumSize();;
34181         this.activeMaxSize = this.getMaximumSize();;
34182         var c1 = size - this.activeMinSize;
34183         var c2 = Math.max(this.activeMaxSize - size, 0);
34184         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34185             this.dd.resetConstraints();
34186             this.dd.setXConstraint(
34187                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34188                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34189             );
34190             this.dd.setYConstraint(0, 0);
34191         }else{
34192             this.dd.resetConstraints();
34193             this.dd.setXConstraint(0, 0);
34194             this.dd.setYConstraint(
34195                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34196                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34197             );
34198          }
34199         this.dragSpecs.startSize = size;
34200         this.dragSpecs.startPoint = [x, y];
34201         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34202     },
34203     
34204     /** 
34205      * @private Called after the drag operation by the DDProxy
34206      */
34207     onEndProxyDrag : function(e){
34208         Roo.get(this.proxy).setDisplayed(false);
34209         var endPoint = Roo.lib.Event.getXY(e);
34210         if(this.overlay){
34211             this.overlay.hide();
34212         }
34213         var newSize;
34214         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34215             newSize = this.dragSpecs.startSize + 
34216                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34217                     endPoint[0] - this.dragSpecs.startPoint[0] :
34218                     this.dragSpecs.startPoint[0] - endPoint[0]
34219                 );
34220         }else{
34221             newSize = this.dragSpecs.startSize + 
34222                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34223                     endPoint[1] - this.dragSpecs.startPoint[1] :
34224                     this.dragSpecs.startPoint[1] - endPoint[1]
34225                 );
34226         }
34227         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34228         if(newSize != this.dragSpecs.startSize){
34229             if(this.fireEvent('beforeapply', this, newSize) !== false){
34230                 this.adapter.setElementSize(this, newSize);
34231                 this.fireEvent("moved", this, newSize);
34232                 this.fireEvent("resize", this, newSize);
34233             }
34234         }
34235     },
34236     
34237     /**
34238      * Get the adapter this SplitBar uses
34239      * @return The adapter object
34240      */
34241     getAdapter : function(){
34242         return this.adapter;
34243     },
34244     
34245     /**
34246      * Set the adapter this SplitBar uses
34247      * @param {Object} adapter A SplitBar adapter object
34248      */
34249     setAdapter : function(adapter){
34250         this.adapter = adapter;
34251         this.adapter.init(this);
34252     },
34253     
34254     /**
34255      * Gets the minimum size for the resizing element
34256      * @return {Number} The minimum size
34257      */
34258     getMinimumSize : function(){
34259         return this.minSize;
34260     },
34261     
34262     /**
34263      * Sets the minimum size for the resizing element
34264      * @param {Number} minSize The minimum size
34265      */
34266     setMinimumSize : function(minSize){
34267         this.minSize = minSize;
34268     },
34269     
34270     /**
34271      * Gets the maximum size for the resizing element
34272      * @return {Number} The maximum size
34273      */
34274     getMaximumSize : function(){
34275         return this.maxSize;
34276     },
34277     
34278     /**
34279      * Sets the maximum size for the resizing element
34280      * @param {Number} maxSize The maximum size
34281      */
34282     setMaximumSize : function(maxSize){
34283         this.maxSize = maxSize;
34284     },
34285     
34286     /**
34287      * Sets the initialize size for the resizing element
34288      * @param {Number} size The initial size
34289      */
34290     setCurrentSize : function(size){
34291         var oldAnimate = this.animate;
34292         this.animate = false;
34293         this.adapter.setElementSize(this, size);
34294         this.animate = oldAnimate;
34295     },
34296     
34297     /**
34298      * Destroy this splitbar. 
34299      * @param {Boolean} removeEl True to remove the element
34300      */
34301     destroy : function(removeEl){
34302         if(this.shim){
34303             this.shim.remove();
34304         }
34305         this.dd.unreg();
34306         this.proxy.parentNode.removeChild(this.proxy);
34307         if(removeEl){
34308             this.el.remove();
34309         }
34310     }
34311 });
34312
34313 /**
34314  * @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.
34315  */
34316 Roo.bootstrap.SplitBar.createProxy = function(dir){
34317     var proxy = new Roo.Element(document.createElement("div"));
34318     proxy.unselectable();
34319     var cls = 'roo-splitbar-proxy';
34320     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34321     document.body.appendChild(proxy.dom);
34322     return proxy.dom;
34323 };
34324
34325 /** 
34326  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34327  * Default Adapter. It assumes the splitter and resizing element are not positioned
34328  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34329  */
34330 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34331 };
34332
34333 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34334     // do nothing for now
34335     init : function(s){
34336     
34337     },
34338     /**
34339      * Called before drag operations to get the current size of the resizing element. 
34340      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34341      */
34342      getElementSize : function(s){
34343         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34344             return s.resizingEl.getWidth();
34345         }else{
34346             return s.resizingEl.getHeight();
34347         }
34348     },
34349     
34350     /**
34351      * Called after drag operations to set the size of the resizing element.
34352      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34353      * @param {Number} newSize The new size to set
34354      * @param {Function} onComplete A function to be invoked when resizing is complete
34355      */
34356     setElementSize : function(s, newSize, onComplete){
34357         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34358             if(!s.animate){
34359                 s.resizingEl.setWidth(newSize);
34360                 if(onComplete){
34361                     onComplete(s, newSize);
34362                 }
34363             }else{
34364                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34365             }
34366         }else{
34367             
34368             if(!s.animate){
34369                 s.resizingEl.setHeight(newSize);
34370                 if(onComplete){
34371                     onComplete(s, newSize);
34372                 }
34373             }else{
34374                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34375             }
34376         }
34377     }
34378 };
34379
34380 /** 
34381  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34382  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34383  * Adapter that  moves the splitter element to align with the resized sizing element. 
34384  * Used with an absolute positioned SplitBar.
34385  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34386  * document.body, make sure you assign an id to the body element.
34387  */
34388 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34389     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34390     this.container = Roo.get(container);
34391 };
34392
34393 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34394     init : function(s){
34395         this.basic.init(s);
34396     },
34397     
34398     getElementSize : function(s){
34399         return this.basic.getElementSize(s);
34400     },
34401     
34402     setElementSize : function(s, newSize, onComplete){
34403         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34404     },
34405     
34406     moveSplitter : function(s){
34407         var yes = Roo.bootstrap.SplitBar;
34408         switch(s.placement){
34409             case yes.LEFT:
34410                 s.el.setX(s.resizingEl.getRight());
34411                 break;
34412             case yes.RIGHT:
34413                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34414                 break;
34415             case yes.TOP:
34416                 s.el.setY(s.resizingEl.getBottom());
34417                 break;
34418             case yes.BOTTOM:
34419                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34420                 break;
34421         }
34422     }
34423 };
34424
34425 /**
34426  * Orientation constant - Create a vertical SplitBar
34427  * @static
34428  * @type Number
34429  */
34430 Roo.bootstrap.SplitBar.VERTICAL = 1;
34431
34432 /**
34433  * Orientation constant - Create a horizontal SplitBar
34434  * @static
34435  * @type Number
34436  */
34437 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34438
34439 /**
34440  * Placement constant - The resizing element is to the left of the splitter element
34441  * @static
34442  * @type Number
34443  */
34444 Roo.bootstrap.SplitBar.LEFT = 1;
34445
34446 /**
34447  * Placement constant - The resizing element is to the right of the splitter element
34448  * @static
34449  * @type Number
34450  */
34451 Roo.bootstrap.SplitBar.RIGHT = 2;
34452
34453 /**
34454  * Placement constant - The resizing element is positioned above the splitter element
34455  * @static
34456  * @type Number
34457  */
34458 Roo.bootstrap.SplitBar.TOP = 3;
34459
34460 /**
34461  * Placement constant - The resizing element is positioned under splitter element
34462  * @static
34463  * @type Number
34464  */
34465 Roo.bootstrap.SplitBar.BOTTOM = 4;
34466 Roo.namespace("Roo.bootstrap.layout");/*
34467  * Based on:
34468  * Ext JS Library 1.1.1
34469  * Copyright(c) 2006-2007, Ext JS, LLC.
34470  *
34471  * Originally Released Under LGPL - original licence link has changed is not relivant.
34472  *
34473  * Fork - LGPL
34474  * <script type="text/javascript">
34475  */
34476
34477 /**
34478  * @class Roo.bootstrap.layout.Manager
34479  * @extends Roo.bootstrap.Component
34480  * Base class for layout managers.
34481  */
34482 Roo.bootstrap.layout.Manager = function(config)
34483 {
34484     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34485
34486
34487
34488
34489
34490     /** false to disable window resize monitoring @type Boolean */
34491     this.monitorWindowResize = true;
34492     this.regions = {};
34493     this.addEvents({
34494         /**
34495          * @event layout
34496          * Fires when a layout is performed.
34497          * @param {Roo.LayoutManager} this
34498          */
34499         "layout" : true,
34500         /**
34501          * @event regionresized
34502          * Fires when the user resizes a region.
34503          * @param {Roo.LayoutRegion} region The resized region
34504          * @param {Number} newSize The new size (width for east/west, height for north/south)
34505          */
34506         "regionresized" : true,
34507         /**
34508          * @event regioncollapsed
34509          * Fires when a region is collapsed.
34510          * @param {Roo.LayoutRegion} region The collapsed region
34511          */
34512         "regioncollapsed" : true,
34513         /**
34514          * @event regionexpanded
34515          * Fires when a region is expanded.
34516          * @param {Roo.LayoutRegion} region The expanded region
34517          */
34518         "regionexpanded" : true
34519     });
34520     this.updating = false;
34521
34522     if (config.el) {
34523         this.el = Roo.get(config.el);
34524         this.initEvents();
34525     }
34526
34527 };
34528
34529 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34530
34531
34532     regions : null,
34533
34534     monitorWindowResize : true,
34535
34536
34537     updating : false,
34538
34539
34540     onRender : function(ct, position)
34541     {
34542         if(!this.el){
34543             this.el = Roo.get(ct);
34544             this.initEvents();
34545         }
34546         //this.fireEvent('render',this);
34547     },
34548
34549
34550     initEvents: function()
34551     {
34552
34553
34554         // ie scrollbar fix
34555         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34556             document.body.scroll = "no";
34557         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34558             this.el.position('relative');
34559         }
34560         this.id = this.el.id;
34561         this.el.addClass("roo-layout-container");
34562         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34563         if(this.el.dom != document.body ) {
34564             this.el.on('resize', this.layout,this);
34565             this.el.on('show', this.layout,this);
34566         }
34567
34568     },
34569
34570     /**
34571      * Returns true if this layout is currently being updated
34572      * @return {Boolean}
34573      */
34574     isUpdating : function(){
34575         return this.updating;
34576     },
34577
34578     /**
34579      * Suspend the LayoutManager from doing auto-layouts while
34580      * making multiple add or remove calls
34581      */
34582     beginUpdate : function(){
34583         this.updating = true;
34584     },
34585
34586     /**
34587      * Restore auto-layouts and optionally disable the manager from performing a layout
34588      * @param {Boolean} noLayout true to disable a layout update
34589      */
34590     endUpdate : function(noLayout){
34591         this.updating = false;
34592         if(!noLayout){
34593             this.layout();
34594         }
34595     },
34596
34597     layout: function(){
34598         // abstract...
34599     },
34600
34601     onRegionResized : function(region, newSize){
34602         this.fireEvent("regionresized", region, newSize);
34603         this.layout();
34604     },
34605
34606     onRegionCollapsed : function(region){
34607         this.fireEvent("regioncollapsed", region);
34608     },
34609
34610     onRegionExpanded : function(region){
34611         this.fireEvent("regionexpanded", region);
34612     },
34613
34614     /**
34615      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34616      * performs box-model adjustments.
34617      * @return {Object} The size as an object {width: (the width), height: (the height)}
34618      */
34619     getViewSize : function()
34620     {
34621         var size;
34622         if(this.el.dom != document.body){
34623             size = this.el.getSize();
34624         }else{
34625             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34626         }
34627         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34628         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34629         return size;
34630     },
34631
34632     /**
34633      * Returns the Element this layout is bound to.
34634      * @return {Roo.Element}
34635      */
34636     getEl : function(){
34637         return this.el;
34638     },
34639
34640     /**
34641      * Returns the specified region.
34642      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34643      * @return {Roo.LayoutRegion}
34644      */
34645     getRegion : function(target){
34646         return this.regions[target.toLowerCase()];
34647     },
34648
34649     onWindowResize : function(){
34650         if(this.monitorWindowResize){
34651             this.layout();
34652         }
34653     }
34654 });
34655 /*
34656  * Based on:
34657  * Ext JS Library 1.1.1
34658  * Copyright(c) 2006-2007, Ext JS, LLC.
34659  *
34660  * Originally Released Under LGPL - original licence link has changed is not relivant.
34661  *
34662  * Fork - LGPL
34663  * <script type="text/javascript">
34664  */
34665 /**
34666  * @class Roo.bootstrap.layout.Border
34667  * @extends Roo.bootstrap.layout.Manager
34668  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34669  * please see: examples/bootstrap/nested.html<br><br>
34670  
34671 <b>The container the layout is rendered into can be either the body element or any other element.
34672 If it is not the body element, the container needs to either be an absolute positioned element,
34673 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34674 the container size if it is not the body element.</b>
34675
34676 * @constructor
34677 * Create a new Border
34678 * @param {Object} config Configuration options
34679  */
34680 Roo.bootstrap.layout.Border = function(config){
34681     config = config || {};
34682     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34683     
34684     
34685     
34686     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34687         if(config[region]){
34688             config[region].region = region;
34689             this.addRegion(config[region]);
34690         }
34691     },this);
34692     
34693 };
34694
34695 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34696
34697 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34698     /**
34699      * Creates and adds a new region if it doesn't already exist.
34700      * @param {String} target The target region key (north, south, east, west or center).
34701      * @param {Object} config The regions config object
34702      * @return {BorderLayoutRegion} The new region
34703      */
34704     addRegion : function(config)
34705     {
34706         if(!this.regions[config.region]){
34707             var r = this.factory(config);
34708             this.bindRegion(r);
34709         }
34710         return this.regions[config.region];
34711     },
34712
34713     // private (kinda)
34714     bindRegion : function(r){
34715         this.regions[r.config.region] = r;
34716         
34717         r.on("visibilitychange",    this.layout, this);
34718         r.on("paneladded",          this.layout, this);
34719         r.on("panelremoved",        this.layout, this);
34720         r.on("invalidated",         this.layout, this);
34721         r.on("resized",             this.onRegionResized, this);
34722         r.on("collapsed",           this.onRegionCollapsed, this);
34723         r.on("expanded",            this.onRegionExpanded, this);
34724     },
34725
34726     /**
34727      * Performs a layout update.
34728      */
34729     layout : function()
34730     {
34731         if(this.updating) {
34732             return;
34733         }
34734         
34735         // render all the rebions if they have not been done alreayd?
34736         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34737             if(this.regions[region] && !this.regions[region].bodyEl){
34738                 this.regions[region].onRender(this.el)
34739             }
34740         },this);
34741         
34742         var size = this.getViewSize();
34743         var w = size.width;
34744         var h = size.height;
34745         var centerW = w;
34746         var centerH = h;
34747         var centerY = 0;
34748         var centerX = 0;
34749         //var x = 0, y = 0;
34750
34751         var rs = this.regions;
34752         var north = rs["north"];
34753         var south = rs["south"]; 
34754         var west = rs["west"];
34755         var east = rs["east"];
34756         var center = rs["center"];
34757         //if(this.hideOnLayout){ // not supported anymore
34758             //c.el.setStyle("display", "none");
34759         //}
34760         if(north && north.isVisible()){
34761             var b = north.getBox();
34762             var m = north.getMargins();
34763             b.width = w - (m.left+m.right);
34764             b.x = m.left;
34765             b.y = m.top;
34766             centerY = b.height + b.y + m.bottom;
34767             centerH -= centerY;
34768             north.updateBox(this.safeBox(b));
34769         }
34770         if(south && south.isVisible()){
34771             var b = south.getBox();
34772             var m = south.getMargins();
34773             b.width = w - (m.left+m.right);
34774             b.x = m.left;
34775             var totalHeight = (b.height + m.top + m.bottom);
34776             b.y = h - totalHeight + m.top;
34777             centerH -= totalHeight;
34778             south.updateBox(this.safeBox(b));
34779         }
34780         if(west && west.isVisible()){
34781             var b = west.getBox();
34782             var m = west.getMargins();
34783             b.height = centerH - (m.top+m.bottom);
34784             b.x = m.left;
34785             b.y = centerY + m.top;
34786             var totalWidth = (b.width + m.left + m.right);
34787             centerX += totalWidth;
34788             centerW -= totalWidth;
34789             west.updateBox(this.safeBox(b));
34790         }
34791         if(east && east.isVisible()){
34792             var b = east.getBox();
34793             var m = east.getMargins();
34794             b.height = centerH - (m.top+m.bottom);
34795             var totalWidth = (b.width + m.left + m.right);
34796             b.x = w - totalWidth + m.left;
34797             b.y = centerY + m.top;
34798             centerW -= totalWidth;
34799             east.updateBox(this.safeBox(b));
34800         }
34801         if(center){
34802             var m = center.getMargins();
34803             var centerBox = {
34804                 x: centerX + m.left,
34805                 y: centerY + m.top,
34806                 width: centerW - (m.left+m.right),
34807                 height: centerH - (m.top+m.bottom)
34808             };
34809             //if(this.hideOnLayout){
34810                 //center.el.setStyle("display", "block");
34811             //}
34812             center.updateBox(this.safeBox(centerBox));
34813         }
34814         this.el.repaint();
34815         this.fireEvent("layout", this);
34816     },
34817
34818     // private
34819     safeBox : function(box){
34820         box.width = Math.max(0, box.width);
34821         box.height = Math.max(0, box.height);
34822         return box;
34823     },
34824
34825     /**
34826      * Adds a ContentPanel (or subclass) to this layout.
34827      * @param {String} target The target region key (north, south, east, west or center).
34828      * @param {Roo.ContentPanel} panel The panel to add
34829      * @return {Roo.ContentPanel} The added panel
34830      */
34831     add : function(target, panel){
34832          
34833         target = target.toLowerCase();
34834         return this.regions[target].add(panel);
34835     },
34836
34837     /**
34838      * Remove a ContentPanel (or subclass) to this layout.
34839      * @param {String} target The target region key (north, south, east, west or center).
34840      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34841      * @return {Roo.ContentPanel} The removed panel
34842      */
34843     remove : function(target, panel){
34844         target = target.toLowerCase();
34845         return this.regions[target].remove(panel);
34846     },
34847
34848     /**
34849      * Searches all regions for a panel with the specified id
34850      * @param {String} panelId
34851      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34852      */
34853     findPanel : function(panelId){
34854         var rs = this.regions;
34855         for(var target in rs){
34856             if(typeof rs[target] != "function"){
34857                 var p = rs[target].getPanel(panelId);
34858                 if(p){
34859                     return p;
34860                 }
34861             }
34862         }
34863         return null;
34864     },
34865
34866     /**
34867      * Searches all regions for a panel with the specified id and activates (shows) it.
34868      * @param {String/ContentPanel} panelId The panels id or the panel itself
34869      * @return {Roo.ContentPanel} The shown panel or null
34870      */
34871     showPanel : function(panelId) {
34872       var rs = this.regions;
34873       for(var target in rs){
34874          var r = rs[target];
34875          if(typeof r != "function"){
34876             if(r.hasPanel(panelId)){
34877                return r.showPanel(panelId);
34878             }
34879          }
34880       }
34881       return null;
34882    },
34883
34884    /**
34885      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34886      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34887      */
34888    /*
34889     restoreState : function(provider){
34890         if(!provider){
34891             provider = Roo.state.Manager;
34892         }
34893         var sm = new Roo.LayoutStateManager();
34894         sm.init(this, provider);
34895     },
34896 */
34897  
34898  
34899     /**
34900      * Adds a xtype elements to the layout.
34901      * <pre><code>
34902
34903 layout.addxtype({
34904        xtype : 'ContentPanel',
34905        region: 'west',
34906        items: [ .... ]
34907    }
34908 );
34909
34910 layout.addxtype({
34911         xtype : 'NestedLayoutPanel',
34912         region: 'west',
34913         layout: {
34914            center: { },
34915            west: { }   
34916         },
34917         items : [ ... list of content panels or nested layout panels.. ]
34918    }
34919 );
34920 </code></pre>
34921      * @param {Object} cfg Xtype definition of item to add.
34922      */
34923     addxtype : function(cfg)
34924     {
34925         // basically accepts a pannel...
34926         // can accept a layout region..!?!?
34927         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34928         
34929         
34930         // theory?  children can only be panels??
34931         
34932         //if (!cfg.xtype.match(/Panel$/)) {
34933         //    return false;
34934         //}
34935         var ret = false;
34936         
34937         if (typeof(cfg.region) == 'undefined') {
34938             Roo.log("Failed to add Panel, region was not set");
34939             Roo.log(cfg);
34940             return false;
34941         }
34942         var region = cfg.region;
34943         delete cfg.region;
34944         
34945           
34946         var xitems = [];
34947         if (cfg.items) {
34948             xitems = cfg.items;
34949             delete cfg.items;
34950         }
34951         var nb = false;
34952         
34953         switch(cfg.xtype) 
34954         {
34955             case 'Content':  // ContentPanel (el, cfg)
34956             case 'Scroll':  // ContentPanel (el, cfg)
34957             case 'View': 
34958                 cfg.autoCreate = true;
34959                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34960                 //} else {
34961                 //    var el = this.el.createChild();
34962                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34963                 //}
34964                 
34965                 this.add(region, ret);
34966                 break;
34967             
34968             /*
34969             case 'TreePanel': // our new panel!
34970                 cfg.el = this.el.createChild();
34971                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34972                 this.add(region, ret);
34973                 break;
34974             */
34975             
34976             case 'Nest': 
34977                 // create a new Layout (which is  a Border Layout...
34978                 
34979                 var clayout = cfg.layout;
34980                 clayout.el  = this.el.createChild();
34981                 clayout.items   = clayout.items  || [];
34982                 
34983                 delete cfg.layout;
34984                 
34985                 // replace this exitems with the clayout ones..
34986                 xitems = clayout.items;
34987                  
34988                 // force background off if it's in center...
34989                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34990                     cfg.background = false;
34991                 }
34992                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34993                 
34994                 
34995                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34996                 //console.log('adding nested layout panel '  + cfg.toSource());
34997                 this.add(region, ret);
34998                 nb = {}; /// find first...
34999                 break;
35000             
35001             case 'Grid':
35002                 
35003                 // needs grid and region
35004                 
35005                 //var el = this.getRegion(region).el.createChild();
35006                 /*
35007                  *var el = this.el.createChild();
35008                 // create the grid first...
35009                 cfg.grid.container = el;
35010                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35011                 */
35012                 
35013                 if (region == 'center' && this.active ) {
35014                     cfg.background = false;
35015                 }
35016                 
35017                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35018                 
35019                 this.add(region, ret);
35020                 /*
35021                 if (cfg.background) {
35022                     // render grid on panel activation (if panel background)
35023                     ret.on('activate', function(gp) {
35024                         if (!gp.grid.rendered) {
35025                     //        gp.grid.render(el);
35026                         }
35027                     });
35028                 } else {
35029                   //  cfg.grid.render(el);
35030                 }
35031                 */
35032                 break;
35033            
35034            
35035             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35036                 // it was the old xcomponent building that caused this before.
35037                 // espeically if border is the top element in the tree.
35038                 ret = this;
35039                 break; 
35040                 
35041                     
35042                 
35043                 
35044                 
35045             default:
35046                 /*
35047                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35048                     
35049                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35050                     this.add(region, ret);
35051                 } else {
35052                 */
35053                     Roo.log(cfg);
35054                     throw "Can not add '" + cfg.xtype + "' to Border";
35055                     return null;
35056              
35057                                 
35058              
35059         }
35060         this.beginUpdate();
35061         // add children..
35062         var region = '';
35063         var abn = {};
35064         Roo.each(xitems, function(i)  {
35065             region = nb && i.region ? i.region : false;
35066             
35067             var add = ret.addxtype(i);
35068            
35069             if (region) {
35070                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35071                 if (!i.background) {
35072                     abn[region] = nb[region] ;
35073                 }
35074             }
35075             
35076         });
35077         this.endUpdate();
35078
35079         // make the last non-background panel active..
35080         //if (nb) { Roo.log(abn); }
35081         if (nb) {
35082             
35083             for(var r in abn) {
35084                 region = this.getRegion(r);
35085                 if (region) {
35086                     // tried using nb[r], but it does not work..
35087                      
35088                     region.showPanel(abn[r]);
35089                    
35090                 }
35091             }
35092         }
35093         return ret;
35094         
35095     },
35096     
35097     
35098 // private
35099     factory : function(cfg)
35100     {
35101         
35102         var validRegions = Roo.bootstrap.layout.Border.regions;
35103
35104         var target = cfg.region;
35105         cfg.mgr = this;
35106         
35107         var r = Roo.bootstrap.layout;
35108         Roo.log(target);
35109         switch(target){
35110             case "north":
35111                 return new r.North(cfg);
35112             case "south":
35113                 return new r.South(cfg);
35114             case "east":
35115                 return new r.East(cfg);
35116             case "west":
35117                 return new r.West(cfg);
35118             case "center":
35119                 return new r.Center(cfg);
35120         }
35121         throw 'Layout region "'+target+'" not supported.';
35122     }
35123     
35124     
35125 });
35126  /*
35127  * Based on:
35128  * Ext JS Library 1.1.1
35129  * Copyright(c) 2006-2007, Ext JS, LLC.
35130  *
35131  * Originally Released Under LGPL - original licence link has changed is not relivant.
35132  *
35133  * Fork - LGPL
35134  * <script type="text/javascript">
35135  */
35136  
35137 /**
35138  * @class Roo.bootstrap.layout.Basic
35139  * @extends Roo.util.Observable
35140  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35141  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35142  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35143  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35144  * @cfg {string}   region  the region that it inhabits..
35145  * @cfg {bool}   skipConfig skip config?
35146  * 
35147
35148  */
35149 Roo.bootstrap.layout.Basic = function(config){
35150     
35151     this.mgr = config.mgr;
35152     
35153     this.position = config.region;
35154     
35155     var skipConfig = config.skipConfig;
35156     
35157     this.events = {
35158         /**
35159          * @scope Roo.BasicLayoutRegion
35160          */
35161         
35162         /**
35163          * @event beforeremove
35164          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35165          * @param {Roo.LayoutRegion} this
35166          * @param {Roo.ContentPanel} panel The panel
35167          * @param {Object} e The cancel event object
35168          */
35169         "beforeremove" : true,
35170         /**
35171          * @event invalidated
35172          * Fires when the layout for this region is changed.
35173          * @param {Roo.LayoutRegion} this
35174          */
35175         "invalidated" : true,
35176         /**
35177          * @event visibilitychange
35178          * Fires when this region is shown or hidden 
35179          * @param {Roo.LayoutRegion} this
35180          * @param {Boolean} visibility true or false
35181          */
35182         "visibilitychange" : true,
35183         /**
35184          * @event paneladded
35185          * Fires when a panel is added. 
35186          * @param {Roo.LayoutRegion} this
35187          * @param {Roo.ContentPanel} panel The panel
35188          */
35189         "paneladded" : true,
35190         /**
35191          * @event panelremoved
35192          * Fires when a panel is removed. 
35193          * @param {Roo.LayoutRegion} this
35194          * @param {Roo.ContentPanel} panel The panel
35195          */
35196         "panelremoved" : true,
35197         /**
35198          * @event beforecollapse
35199          * Fires when this region before collapse.
35200          * @param {Roo.LayoutRegion} this
35201          */
35202         "beforecollapse" : true,
35203         /**
35204          * @event collapsed
35205          * Fires when this region is collapsed.
35206          * @param {Roo.LayoutRegion} this
35207          */
35208         "collapsed" : true,
35209         /**
35210          * @event expanded
35211          * Fires when this region is expanded.
35212          * @param {Roo.LayoutRegion} this
35213          */
35214         "expanded" : true,
35215         /**
35216          * @event slideshow
35217          * Fires when this region is slid into view.
35218          * @param {Roo.LayoutRegion} this
35219          */
35220         "slideshow" : true,
35221         /**
35222          * @event slidehide
35223          * Fires when this region slides out of view. 
35224          * @param {Roo.LayoutRegion} this
35225          */
35226         "slidehide" : true,
35227         /**
35228          * @event panelactivated
35229          * Fires when a panel is activated. 
35230          * @param {Roo.LayoutRegion} this
35231          * @param {Roo.ContentPanel} panel The activated panel
35232          */
35233         "panelactivated" : true,
35234         /**
35235          * @event resized
35236          * Fires when the user resizes this region. 
35237          * @param {Roo.LayoutRegion} this
35238          * @param {Number} newSize The new size (width for east/west, height for north/south)
35239          */
35240         "resized" : true
35241     };
35242     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35243     this.panels = new Roo.util.MixedCollection();
35244     this.panels.getKey = this.getPanelId.createDelegate(this);
35245     this.box = null;
35246     this.activePanel = null;
35247     // ensure listeners are added...
35248     
35249     if (config.listeners || config.events) {
35250         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35251             listeners : config.listeners || {},
35252             events : config.events || {}
35253         });
35254     }
35255     
35256     if(skipConfig !== true){
35257         this.applyConfig(config);
35258     }
35259 };
35260
35261 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35262 {
35263     getPanelId : function(p){
35264         return p.getId();
35265     },
35266     
35267     applyConfig : function(config){
35268         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35269         this.config = config;
35270         
35271     },
35272     
35273     /**
35274      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35275      * the width, for horizontal (north, south) the height.
35276      * @param {Number} newSize The new width or height
35277      */
35278     resizeTo : function(newSize){
35279         var el = this.el ? this.el :
35280                  (this.activePanel ? this.activePanel.getEl() : null);
35281         if(el){
35282             switch(this.position){
35283                 case "east":
35284                 case "west":
35285                     el.setWidth(newSize);
35286                     this.fireEvent("resized", this, newSize);
35287                 break;
35288                 case "north":
35289                 case "south":
35290                     el.setHeight(newSize);
35291                     this.fireEvent("resized", this, newSize);
35292                 break;                
35293             }
35294         }
35295     },
35296     
35297     getBox : function(){
35298         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35299     },
35300     
35301     getMargins : function(){
35302         return this.margins;
35303     },
35304     
35305     updateBox : function(box){
35306         this.box = box;
35307         var el = this.activePanel.getEl();
35308         el.dom.style.left = box.x + "px";
35309         el.dom.style.top = box.y + "px";
35310         this.activePanel.setSize(box.width, box.height);
35311     },
35312     
35313     /**
35314      * Returns the container element for this region.
35315      * @return {Roo.Element}
35316      */
35317     getEl : function(){
35318         return this.activePanel;
35319     },
35320     
35321     /**
35322      * Returns true if this region is currently visible.
35323      * @return {Boolean}
35324      */
35325     isVisible : function(){
35326         return this.activePanel ? true : false;
35327     },
35328     
35329     setActivePanel : function(panel){
35330         panel = this.getPanel(panel);
35331         if(this.activePanel && this.activePanel != panel){
35332             this.activePanel.setActiveState(false);
35333             this.activePanel.getEl().setLeftTop(-10000,-10000);
35334         }
35335         this.activePanel = panel;
35336         panel.setActiveState(true);
35337         if(this.box){
35338             panel.setSize(this.box.width, this.box.height);
35339         }
35340         this.fireEvent("panelactivated", this, panel);
35341         this.fireEvent("invalidated");
35342     },
35343     
35344     /**
35345      * Show the specified panel.
35346      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35347      * @return {Roo.ContentPanel} The shown panel or null
35348      */
35349     showPanel : function(panel){
35350         panel = this.getPanel(panel);
35351         if(panel){
35352             this.setActivePanel(panel);
35353         }
35354         return panel;
35355     },
35356     
35357     /**
35358      * Get the active panel for this region.
35359      * @return {Roo.ContentPanel} The active panel or null
35360      */
35361     getActivePanel : function(){
35362         return this.activePanel;
35363     },
35364     
35365     /**
35366      * Add the passed ContentPanel(s)
35367      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35368      * @return {Roo.ContentPanel} The panel added (if only one was added)
35369      */
35370     add : function(panel){
35371         if(arguments.length > 1){
35372             for(var i = 0, len = arguments.length; i < len; i++) {
35373                 this.add(arguments[i]);
35374             }
35375             return null;
35376         }
35377         if(this.hasPanel(panel)){
35378             this.showPanel(panel);
35379             return panel;
35380         }
35381         var el = panel.getEl();
35382         if(el.dom.parentNode != this.mgr.el.dom){
35383             this.mgr.el.dom.appendChild(el.dom);
35384         }
35385         if(panel.setRegion){
35386             panel.setRegion(this);
35387         }
35388         this.panels.add(panel);
35389         el.setStyle("position", "absolute");
35390         if(!panel.background){
35391             this.setActivePanel(panel);
35392             if(this.config.initialSize && this.panels.getCount()==1){
35393                 this.resizeTo(this.config.initialSize);
35394             }
35395         }
35396         this.fireEvent("paneladded", this, panel);
35397         return panel;
35398     },
35399     
35400     /**
35401      * Returns true if the panel is in this region.
35402      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35403      * @return {Boolean}
35404      */
35405     hasPanel : function(panel){
35406         if(typeof panel == "object"){ // must be panel obj
35407             panel = panel.getId();
35408         }
35409         return this.getPanel(panel) ? true : false;
35410     },
35411     
35412     /**
35413      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35414      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35415      * @param {Boolean} preservePanel Overrides the config preservePanel option
35416      * @return {Roo.ContentPanel} The panel that was removed
35417      */
35418     remove : function(panel, preservePanel){
35419         panel = this.getPanel(panel);
35420         if(!panel){
35421             return null;
35422         }
35423         var e = {};
35424         this.fireEvent("beforeremove", this, panel, e);
35425         if(e.cancel === true){
35426             return null;
35427         }
35428         var panelId = panel.getId();
35429         this.panels.removeKey(panelId);
35430         return panel;
35431     },
35432     
35433     /**
35434      * Returns the panel specified or null if it's not in this region.
35435      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35436      * @return {Roo.ContentPanel}
35437      */
35438     getPanel : function(id){
35439         if(typeof id == "object"){ // must be panel obj
35440             return id;
35441         }
35442         return this.panels.get(id);
35443     },
35444     
35445     /**
35446      * Returns this regions position (north/south/east/west/center).
35447      * @return {String} 
35448      */
35449     getPosition: function(){
35450         return this.position;    
35451     }
35452 });/*
35453  * Based on:
35454  * Ext JS Library 1.1.1
35455  * Copyright(c) 2006-2007, Ext JS, LLC.
35456  *
35457  * Originally Released Under LGPL - original licence link has changed is not relivant.
35458  *
35459  * Fork - LGPL
35460  * <script type="text/javascript">
35461  */
35462  
35463 /**
35464  * @class Roo.bootstrap.layout.Region
35465  * @extends Roo.bootstrap.layout.Basic
35466  * This class represents a region in a layout manager.
35467  
35468  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35469  * @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})
35470  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35471  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35472  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35473  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35474  * @cfg {String}    title           The title for the region (overrides panel titles)
35475  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35476  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35477  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35478  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35479  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35480  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35481  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35482  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35483  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35484  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35485
35486  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35487  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35488  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35489  * @cfg {Number}    width           For East/West panels
35490  * @cfg {Number}    height          For North/South panels
35491  * @cfg {Boolean}   split           To show the splitter
35492  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35493  * 
35494  * @cfg {string}   cls             Extra CSS classes to add to region
35495  * 
35496  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35497  * @cfg {string}   region  the region that it inhabits..
35498  *
35499
35500  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35501  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35502
35503  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35504  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35505  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35506  */
35507 Roo.bootstrap.layout.Region = function(config)
35508 {
35509     this.applyConfig(config);
35510
35511     var mgr = config.mgr;
35512     var pos = config.region;
35513     config.skipConfig = true;
35514     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35515     
35516     if (mgr.el) {
35517         this.onRender(mgr.el);   
35518     }
35519      
35520     this.visible = true;
35521     this.collapsed = false;
35522     this.unrendered_panels = [];
35523 };
35524
35525 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35526
35527     position: '', // set by wrapper (eg. north/south etc..)
35528     unrendered_panels : null,  // unrendered panels.
35529     createBody : function(){
35530         /** This region's body element 
35531         * @type Roo.Element */
35532         this.bodyEl = this.el.createChild({
35533                 tag: "div",
35534                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35535         });
35536     },
35537
35538     onRender: function(ctr, pos)
35539     {
35540         var dh = Roo.DomHelper;
35541         /** This region's container element 
35542         * @type Roo.Element */
35543         this.el = dh.append(ctr.dom, {
35544                 tag: "div",
35545                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35546             }, true);
35547         /** This region's title element 
35548         * @type Roo.Element */
35549     
35550         this.titleEl = dh.append(this.el.dom,
35551             {
35552                     tag: "div",
35553                     unselectable: "on",
35554                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35555                     children:[
35556                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35557                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35558                     ]}, true);
35559         
35560         this.titleEl.enableDisplayMode();
35561         /** This region's title text element 
35562         * @type HTMLElement */
35563         this.titleTextEl = this.titleEl.dom.firstChild;
35564         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35565         /*
35566         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35567         this.closeBtn.enableDisplayMode();
35568         this.closeBtn.on("click", this.closeClicked, this);
35569         this.closeBtn.hide();
35570     */
35571         this.createBody(this.config);
35572         if(this.config.hideWhenEmpty){
35573             this.hide();
35574             this.on("paneladded", this.validateVisibility, this);
35575             this.on("panelremoved", this.validateVisibility, this);
35576         }
35577         if(this.autoScroll){
35578             this.bodyEl.setStyle("overflow", "auto");
35579         }else{
35580             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35581         }
35582         //if(c.titlebar !== false){
35583             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35584                 this.titleEl.hide();
35585             }else{
35586                 this.titleEl.show();
35587                 if(this.config.title){
35588                     this.titleTextEl.innerHTML = this.config.title;
35589                 }
35590             }
35591         //}
35592         if(this.config.collapsed){
35593             this.collapse(true);
35594         }
35595         if(this.config.hidden){
35596             this.hide();
35597         }
35598         
35599         if (this.unrendered_panels && this.unrendered_panels.length) {
35600             for (var i =0;i< this.unrendered_panels.length; i++) {
35601                 this.add(this.unrendered_panels[i]);
35602             }
35603             this.unrendered_panels = null;
35604             
35605         }
35606         
35607     },
35608     
35609     applyConfig : function(c)
35610     {
35611         /*
35612          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35613             var dh = Roo.DomHelper;
35614             if(c.titlebar !== false){
35615                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35616                 this.collapseBtn.on("click", this.collapse, this);
35617                 this.collapseBtn.enableDisplayMode();
35618                 /*
35619                 if(c.showPin === true || this.showPin){
35620                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35621                     this.stickBtn.enableDisplayMode();
35622                     this.stickBtn.on("click", this.expand, this);
35623                     this.stickBtn.hide();
35624                 }
35625                 
35626             }
35627             */
35628             /** This region's collapsed element
35629             * @type Roo.Element */
35630             /*
35631              *
35632             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35633                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35634             ]}, true);
35635             
35636             if(c.floatable !== false){
35637                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35638                this.collapsedEl.on("click", this.collapseClick, this);
35639             }
35640
35641             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35642                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35643                    id: "message", unselectable: "on", style:{"float":"left"}});
35644                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35645              }
35646             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35647             this.expandBtn.on("click", this.expand, this);
35648             
35649         }
35650         
35651         if(this.collapseBtn){
35652             this.collapseBtn.setVisible(c.collapsible == true);
35653         }
35654         
35655         this.cmargins = c.cmargins || this.cmargins ||
35656                          (this.position == "west" || this.position == "east" ?
35657                              {top: 0, left: 2, right:2, bottom: 0} :
35658                              {top: 2, left: 0, right:0, bottom: 2});
35659         */
35660         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35661         
35662         
35663         this.bottomTabs = c.tabPosition != "top";
35664         
35665         this.autoScroll = c.autoScroll || false;
35666         
35667         
35668        
35669         
35670         this.duration = c.duration || .30;
35671         this.slideDuration = c.slideDuration || .45;
35672         this.config = c;
35673        
35674     },
35675     /**
35676      * Returns true if this region is currently visible.
35677      * @return {Boolean}
35678      */
35679     isVisible : function(){
35680         return this.visible;
35681     },
35682
35683     /**
35684      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35685      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35686      */
35687     //setCollapsedTitle : function(title){
35688     //    title = title || "&#160;";
35689      //   if(this.collapsedTitleTextEl){
35690       //      this.collapsedTitleTextEl.innerHTML = title;
35691        // }
35692     //},
35693
35694     getBox : function(){
35695         var b;
35696       //  if(!this.collapsed){
35697             b = this.el.getBox(false, true);
35698        // }else{
35699           //  b = this.collapsedEl.getBox(false, true);
35700         //}
35701         return b;
35702     },
35703
35704     getMargins : function(){
35705         return this.margins;
35706         //return this.collapsed ? this.cmargins : this.margins;
35707     },
35708 /*
35709     highlight : function(){
35710         this.el.addClass("x-layout-panel-dragover");
35711     },
35712
35713     unhighlight : function(){
35714         this.el.removeClass("x-layout-panel-dragover");
35715     },
35716 */
35717     updateBox : function(box)
35718     {
35719         if (!this.bodyEl) {
35720             return; // not rendered yet..
35721         }
35722         
35723         this.box = box;
35724         if(!this.collapsed){
35725             this.el.dom.style.left = box.x + "px";
35726             this.el.dom.style.top = box.y + "px";
35727             this.updateBody(box.width, box.height);
35728         }else{
35729             this.collapsedEl.dom.style.left = box.x + "px";
35730             this.collapsedEl.dom.style.top = box.y + "px";
35731             this.collapsedEl.setSize(box.width, box.height);
35732         }
35733         if(this.tabs){
35734             this.tabs.autoSizeTabs();
35735         }
35736     },
35737
35738     updateBody : function(w, h)
35739     {
35740         if(w !== null){
35741             this.el.setWidth(w);
35742             w -= this.el.getBorderWidth("rl");
35743             if(this.config.adjustments){
35744                 w += this.config.adjustments[0];
35745             }
35746         }
35747         if(h !== null && h > 0){
35748             this.el.setHeight(h);
35749             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35750             h -= this.el.getBorderWidth("tb");
35751             if(this.config.adjustments){
35752                 h += this.config.adjustments[1];
35753             }
35754             this.bodyEl.setHeight(h);
35755             if(this.tabs){
35756                 h = this.tabs.syncHeight(h);
35757             }
35758         }
35759         if(this.panelSize){
35760             w = w !== null ? w : this.panelSize.width;
35761             h = h !== null ? h : this.panelSize.height;
35762         }
35763         if(this.activePanel){
35764             var el = this.activePanel.getEl();
35765             w = w !== null ? w : el.getWidth();
35766             h = h !== null ? h : el.getHeight();
35767             this.panelSize = {width: w, height: h};
35768             this.activePanel.setSize(w, h);
35769         }
35770         if(Roo.isIE && this.tabs){
35771             this.tabs.el.repaint();
35772         }
35773     },
35774
35775     /**
35776      * Returns the container element for this region.
35777      * @return {Roo.Element}
35778      */
35779     getEl : function(){
35780         return this.el;
35781     },
35782
35783     /**
35784      * Hides this region.
35785      */
35786     hide : function(){
35787         //if(!this.collapsed){
35788             this.el.dom.style.left = "-2000px";
35789             this.el.hide();
35790         //}else{
35791          //   this.collapsedEl.dom.style.left = "-2000px";
35792          //   this.collapsedEl.hide();
35793        // }
35794         this.visible = false;
35795         this.fireEvent("visibilitychange", this, false);
35796     },
35797
35798     /**
35799      * Shows this region if it was previously hidden.
35800      */
35801     show : function(){
35802         //if(!this.collapsed){
35803             this.el.show();
35804         //}else{
35805         //    this.collapsedEl.show();
35806        // }
35807         this.visible = true;
35808         this.fireEvent("visibilitychange", this, true);
35809     },
35810 /*
35811     closeClicked : function(){
35812         if(this.activePanel){
35813             this.remove(this.activePanel);
35814         }
35815     },
35816
35817     collapseClick : function(e){
35818         if(this.isSlid){
35819            e.stopPropagation();
35820            this.slideIn();
35821         }else{
35822            e.stopPropagation();
35823            this.slideOut();
35824         }
35825     },
35826 */
35827     /**
35828      * Collapses this region.
35829      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35830      */
35831     /*
35832     collapse : function(skipAnim, skipCheck = false){
35833         if(this.collapsed) {
35834             return;
35835         }
35836         
35837         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35838             
35839             this.collapsed = true;
35840             if(this.split){
35841                 this.split.el.hide();
35842             }
35843             if(this.config.animate && skipAnim !== true){
35844                 this.fireEvent("invalidated", this);
35845                 this.animateCollapse();
35846             }else{
35847                 this.el.setLocation(-20000,-20000);
35848                 this.el.hide();
35849                 this.collapsedEl.show();
35850                 this.fireEvent("collapsed", this);
35851                 this.fireEvent("invalidated", this);
35852             }
35853         }
35854         
35855     },
35856 */
35857     animateCollapse : function(){
35858         // overridden
35859     },
35860
35861     /**
35862      * Expands this region if it was previously collapsed.
35863      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35864      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35865      */
35866     /*
35867     expand : function(e, skipAnim){
35868         if(e) {
35869             e.stopPropagation();
35870         }
35871         if(!this.collapsed || this.el.hasActiveFx()) {
35872             return;
35873         }
35874         if(this.isSlid){
35875             this.afterSlideIn();
35876             skipAnim = true;
35877         }
35878         this.collapsed = false;
35879         if(this.config.animate && skipAnim !== true){
35880             this.animateExpand();
35881         }else{
35882             this.el.show();
35883             if(this.split){
35884                 this.split.el.show();
35885             }
35886             this.collapsedEl.setLocation(-2000,-2000);
35887             this.collapsedEl.hide();
35888             this.fireEvent("invalidated", this);
35889             this.fireEvent("expanded", this);
35890         }
35891     },
35892 */
35893     animateExpand : function(){
35894         // overridden
35895     },
35896
35897     initTabs : function()
35898     {
35899         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35900         
35901         var ts = new Roo.bootstrap.panel.Tabs({
35902                 el: this.bodyEl.dom,
35903                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35904                 disableTooltips: this.config.disableTabTips,
35905                 toolbar : this.config.toolbar
35906             });
35907         
35908         if(this.config.hideTabs){
35909             ts.stripWrap.setDisplayed(false);
35910         }
35911         this.tabs = ts;
35912         ts.resizeTabs = this.config.resizeTabs === true;
35913         ts.minTabWidth = this.config.minTabWidth || 40;
35914         ts.maxTabWidth = this.config.maxTabWidth || 250;
35915         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35916         ts.monitorResize = false;
35917         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35918         ts.bodyEl.addClass('roo-layout-tabs-body');
35919         this.panels.each(this.initPanelAsTab, this);
35920     },
35921
35922     initPanelAsTab : function(panel){
35923         var ti = this.tabs.addTab(
35924             panel.getEl().id,
35925             panel.getTitle(),
35926             null,
35927             this.config.closeOnTab && panel.isClosable(),
35928             panel.tpl
35929         );
35930         if(panel.tabTip !== undefined){
35931             ti.setTooltip(panel.tabTip);
35932         }
35933         ti.on("activate", function(){
35934               this.setActivePanel(panel);
35935         }, this);
35936         
35937         if(this.config.closeOnTab){
35938             ti.on("beforeclose", function(t, e){
35939                 e.cancel = true;
35940                 this.remove(panel);
35941             }, this);
35942         }
35943         
35944         panel.tabItem = ti;
35945         
35946         return ti;
35947     },
35948
35949     updatePanelTitle : function(panel, title)
35950     {
35951         if(this.activePanel == panel){
35952             this.updateTitle(title);
35953         }
35954         if(this.tabs){
35955             var ti = this.tabs.getTab(panel.getEl().id);
35956             ti.setText(title);
35957             if(panel.tabTip !== undefined){
35958                 ti.setTooltip(panel.tabTip);
35959             }
35960         }
35961     },
35962
35963     updateTitle : function(title){
35964         if(this.titleTextEl && !this.config.title){
35965             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35966         }
35967     },
35968
35969     setActivePanel : function(panel)
35970     {
35971         panel = this.getPanel(panel);
35972         if(this.activePanel && this.activePanel != panel){
35973             if(this.activePanel.setActiveState(false) === false){
35974                 return;
35975             }
35976         }
35977         this.activePanel = panel;
35978         panel.setActiveState(true);
35979         if(this.panelSize){
35980             panel.setSize(this.panelSize.width, this.panelSize.height);
35981         }
35982         if(this.closeBtn){
35983             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35984         }
35985         this.updateTitle(panel.getTitle());
35986         if(this.tabs){
35987             this.fireEvent("invalidated", this);
35988         }
35989         this.fireEvent("panelactivated", this, panel);
35990     },
35991
35992     /**
35993      * Shows the specified panel.
35994      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35995      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35996      */
35997     showPanel : function(panel)
35998     {
35999         panel = this.getPanel(panel);
36000         if(panel){
36001             if(this.tabs){
36002                 var tab = this.tabs.getTab(panel.getEl().id);
36003                 if(tab.isHidden()){
36004                     this.tabs.unhideTab(tab.id);
36005                 }
36006                 tab.activate();
36007             }else{
36008                 this.setActivePanel(panel);
36009             }
36010         }
36011         return panel;
36012     },
36013
36014     /**
36015      * Get the active panel for this region.
36016      * @return {Roo.ContentPanel} The active panel or null
36017      */
36018     getActivePanel : function(){
36019         return this.activePanel;
36020     },
36021
36022     validateVisibility : function(){
36023         if(this.panels.getCount() < 1){
36024             this.updateTitle("&#160;");
36025             this.closeBtn.hide();
36026             this.hide();
36027         }else{
36028             if(!this.isVisible()){
36029                 this.show();
36030             }
36031         }
36032     },
36033
36034     /**
36035      * Adds the passed ContentPanel(s) to this region.
36036      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36037      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36038      */
36039     add : function(panel)
36040     {
36041         if(arguments.length > 1){
36042             for(var i = 0, len = arguments.length; i < len; i++) {
36043                 this.add(arguments[i]);
36044             }
36045             return null;
36046         }
36047         
36048         // if we have not been rendered yet, then we can not really do much of this..
36049         if (!this.bodyEl) {
36050             this.unrendered_panels.push(panel);
36051             return panel;
36052         }
36053         
36054         
36055         
36056         
36057         if(this.hasPanel(panel)){
36058             this.showPanel(panel);
36059             return panel;
36060         }
36061         panel.setRegion(this);
36062         this.panels.add(panel);
36063        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36064             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36065             // and hide them... ???
36066             this.bodyEl.dom.appendChild(panel.getEl().dom);
36067             if(panel.background !== true){
36068                 this.setActivePanel(panel);
36069             }
36070             this.fireEvent("paneladded", this, panel);
36071             return panel;
36072         }
36073         */
36074         if(!this.tabs){
36075             this.initTabs();
36076         }else{
36077             this.initPanelAsTab(panel);
36078         }
36079         
36080         
36081         if(panel.background !== true){
36082             this.tabs.activate(panel.getEl().id);
36083         }
36084         this.fireEvent("paneladded", this, panel);
36085         return panel;
36086     },
36087
36088     /**
36089      * Hides the tab for the specified panel.
36090      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36091      */
36092     hidePanel : function(panel){
36093         if(this.tabs && (panel = this.getPanel(panel))){
36094             this.tabs.hideTab(panel.getEl().id);
36095         }
36096     },
36097
36098     /**
36099      * Unhides the tab for a previously hidden panel.
36100      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36101      */
36102     unhidePanel : function(panel){
36103         if(this.tabs && (panel = this.getPanel(panel))){
36104             this.tabs.unhideTab(panel.getEl().id);
36105         }
36106     },
36107
36108     clearPanels : function(){
36109         while(this.panels.getCount() > 0){
36110              this.remove(this.panels.first());
36111         }
36112     },
36113
36114     /**
36115      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36116      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36117      * @param {Boolean} preservePanel Overrides the config preservePanel option
36118      * @return {Roo.ContentPanel} The panel that was removed
36119      */
36120     remove : function(panel, preservePanel)
36121     {
36122         panel = this.getPanel(panel);
36123         if(!panel){
36124             return null;
36125         }
36126         var e = {};
36127         this.fireEvent("beforeremove", this, panel, e);
36128         if(e.cancel === true){
36129             return null;
36130         }
36131         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36132         var panelId = panel.getId();
36133         this.panels.removeKey(panelId);
36134         if(preservePanel){
36135             document.body.appendChild(panel.getEl().dom);
36136         }
36137         if(this.tabs){
36138             this.tabs.removeTab(panel.getEl().id);
36139         }else if (!preservePanel){
36140             this.bodyEl.dom.removeChild(panel.getEl().dom);
36141         }
36142         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36143             var p = this.panels.first();
36144             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36145             tempEl.appendChild(p.getEl().dom);
36146             this.bodyEl.update("");
36147             this.bodyEl.dom.appendChild(p.getEl().dom);
36148             tempEl = null;
36149             this.updateTitle(p.getTitle());
36150             this.tabs = null;
36151             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36152             this.setActivePanel(p);
36153         }
36154         panel.setRegion(null);
36155         if(this.activePanel == panel){
36156             this.activePanel = null;
36157         }
36158         if(this.config.autoDestroy !== false && preservePanel !== true){
36159             try{panel.destroy();}catch(e){}
36160         }
36161         this.fireEvent("panelremoved", this, panel);
36162         return panel;
36163     },
36164
36165     /**
36166      * Returns the TabPanel component used by this region
36167      * @return {Roo.TabPanel}
36168      */
36169     getTabs : function(){
36170         return this.tabs;
36171     },
36172
36173     createTool : function(parentEl, className){
36174         var btn = Roo.DomHelper.append(parentEl, {
36175             tag: "div",
36176             cls: "x-layout-tools-button",
36177             children: [ {
36178                 tag: "div",
36179                 cls: "roo-layout-tools-button-inner " + className,
36180                 html: "&#160;"
36181             }]
36182         }, true);
36183         btn.addClassOnOver("roo-layout-tools-button-over");
36184         return btn;
36185     }
36186 });/*
36187  * Based on:
36188  * Ext JS Library 1.1.1
36189  * Copyright(c) 2006-2007, Ext JS, LLC.
36190  *
36191  * Originally Released Under LGPL - original licence link has changed is not relivant.
36192  *
36193  * Fork - LGPL
36194  * <script type="text/javascript">
36195  */
36196  
36197
36198
36199 /**
36200  * @class Roo.SplitLayoutRegion
36201  * @extends Roo.LayoutRegion
36202  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36203  */
36204 Roo.bootstrap.layout.Split = function(config){
36205     this.cursor = config.cursor;
36206     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36207 };
36208
36209 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36210 {
36211     splitTip : "Drag to resize.",
36212     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36213     useSplitTips : false,
36214
36215     applyConfig : function(config){
36216         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36217     },
36218     
36219     onRender : function(ctr,pos) {
36220         
36221         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36222         if(!this.config.split){
36223             return;
36224         }
36225         if(!this.split){
36226             
36227             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36228                             tag: "div",
36229                             id: this.el.id + "-split",
36230                             cls: "roo-layout-split roo-layout-split-"+this.position,
36231                             html: "&#160;"
36232             });
36233             /** The SplitBar for this region 
36234             * @type Roo.SplitBar */
36235             // does not exist yet...
36236             Roo.log([this.position, this.orientation]);
36237             
36238             this.split = new Roo.bootstrap.SplitBar({
36239                 dragElement : splitEl,
36240                 resizingElement: this.el,
36241                 orientation : this.orientation
36242             });
36243             
36244             this.split.on("moved", this.onSplitMove, this);
36245             this.split.useShim = this.config.useShim === true;
36246             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36247             if(this.useSplitTips){
36248                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36249             }
36250             //if(config.collapsible){
36251             //    this.split.el.on("dblclick", this.collapse,  this);
36252             //}
36253         }
36254         if(typeof this.config.minSize != "undefined"){
36255             this.split.minSize = this.config.minSize;
36256         }
36257         if(typeof this.config.maxSize != "undefined"){
36258             this.split.maxSize = this.config.maxSize;
36259         }
36260         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36261             this.hideSplitter();
36262         }
36263         
36264     },
36265
36266     getHMaxSize : function(){
36267          var cmax = this.config.maxSize || 10000;
36268          var center = this.mgr.getRegion("center");
36269          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36270     },
36271
36272     getVMaxSize : function(){
36273          var cmax = this.config.maxSize || 10000;
36274          var center = this.mgr.getRegion("center");
36275          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36276     },
36277
36278     onSplitMove : function(split, newSize){
36279         this.fireEvent("resized", this, newSize);
36280     },
36281     
36282     /** 
36283      * Returns the {@link Roo.SplitBar} for this region.
36284      * @return {Roo.SplitBar}
36285      */
36286     getSplitBar : function(){
36287         return this.split;
36288     },
36289     
36290     hide : function(){
36291         this.hideSplitter();
36292         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36293     },
36294
36295     hideSplitter : function(){
36296         if(this.split){
36297             this.split.el.setLocation(-2000,-2000);
36298             this.split.el.hide();
36299         }
36300     },
36301
36302     show : function(){
36303         if(this.split){
36304             this.split.el.show();
36305         }
36306         Roo.bootstrap.layout.Split.superclass.show.call(this);
36307     },
36308     
36309     beforeSlide: function(){
36310         if(Roo.isGecko){// firefox overflow auto bug workaround
36311             this.bodyEl.clip();
36312             if(this.tabs) {
36313                 this.tabs.bodyEl.clip();
36314             }
36315             if(this.activePanel){
36316                 this.activePanel.getEl().clip();
36317                 
36318                 if(this.activePanel.beforeSlide){
36319                     this.activePanel.beforeSlide();
36320                 }
36321             }
36322         }
36323     },
36324     
36325     afterSlide : function(){
36326         if(Roo.isGecko){// firefox overflow auto bug workaround
36327             this.bodyEl.unclip();
36328             if(this.tabs) {
36329                 this.tabs.bodyEl.unclip();
36330             }
36331             if(this.activePanel){
36332                 this.activePanel.getEl().unclip();
36333                 if(this.activePanel.afterSlide){
36334                     this.activePanel.afterSlide();
36335                 }
36336             }
36337         }
36338     },
36339
36340     initAutoHide : function(){
36341         if(this.autoHide !== false){
36342             if(!this.autoHideHd){
36343                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36344                 this.autoHideHd = {
36345                     "mouseout": function(e){
36346                         if(!e.within(this.el, true)){
36347                             st.delay(500);
36348                         }
36349                     },
36350                     "mouseover" : function(e){
36351                         st.cancel();
36352                     },
36353                     scope : this
36354                 };
36355             }
36356             this.el.on(this.autoHideHd);
36357         }
36358     },
36359
36360     clearAutoHide : function(){
36361         if(this.autoHide !== false){
36362             this.el.un("mouseout", this.autoHideHd.mouseout);
36363             this.el.un("mouseover", this.autoHideHd.mouseover);
36364         }
36365     },
36366
36367     clearMonitor : function(){
36368         Roo.get(document).un("click", this.slideInIf, this);
36369     },
36370
36371     // these names are backwards but not changed for compat
36372     slideOut : function(){
36373         if(this.isSlid || this.el.hasActiveFx()){
36374             return;
36375         }
36376         this.isSlid = true;
36377         if(this.collapseBtn){
36378             this.collapseBtn.hide();
36379         }
36380         this.closeBtnState = this.closeBtn.getStyle('display');
36381         this.closeBtn.hide();
36382         if(this.stickBtn){
36383             this.stickBtn.show();
36384         }
36385         this.el.show();
36386         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36387         this.beforeSlide();
36388         this.el.setStyle("z-index", 10001);
36389         this.el.slideIn(this.getSlideAnchor(), {
36390             callback: function(){
36391                 this.afterSlide();
36392                 this.initAutoHide();
36393                 Roo.get(document).on("click", this.slideInIf, this);
36394                 this.fireEvent("slideshow", this);
36395             },
36396             scope: this,
36397             block: true
36398         });
36399     },
36400
36401     afterSlideIn : function(){
36402         this.clearAutoHide();
36403         this.isSlid = false;
36404         this.clearMonitor();
36405         this.el.setStyle("z-index", "");
36406         if(this.collapseBtn){
36407             this.collapseBtn.show();
36408         }
36409         this.closeBtn.setStyle('display', this.closeBtnState);
36410         if(this.stickBtn){
36411             this.stickBtn.hide();
36412         }
36413         this.fireEvent("slidehide", this);
36414     },
36415
36416     slideIn : function(cb){
36417         if(!this.isSlid || this.el.hasActiveFx()){
36418             Roo.callback(cb);
36419             return;
36420         }
36421         this.isSlid = false;
36422         this.beforeSlide();
36423         this.el.slideOut(this.getSlideAnchor(), {
36424             callback: function(){
36425                 this.el.setLeftTop(-10000, -10000);
36426                 this.afterSlide();
36427                 this.afterSlideIn();
36428                 Roo.callback(cb);
36429             },
36430             scope: this,
36431             block: true
36432         });
36433     },
36434     
36435     slideInIf : function(e){
36436         if(!e.within(this.el)){
36437             this.slideIn();
36438         }
36439     },
36440
36441     animateCollapse : function(){
36442         this.beforeSlide();
36443         this.el.setStyle("z-index", 20000);
36444         var anchor = this.getSlideAnchor();
36445         this.el.slideOut(anchor, {
36446             callback : function(){
36447                 this.el.setStyle("z-index", "");
36448                 this.collapsedEl.slideIn(anchor, {duration:.3});
36449                 this.afterSlide();
36450                 this.el.setLocation(-10000,-10000);
36451                 this.el.hide();
36452                 this.fireEvent("collapsed", this);
36453             },
36454             scope: this,
36455             block: true
36456         });
36457     },
36458
36459     animateExpand : function(){
36460         this.beforeSlide();
36461         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36462         this.el.setStyle("z-index", 20000);
36463         this.collapsedEl.hide({
36464             duration:.1
36465         });
36466         this.el.slideIn(this.getSlideAnchor(), {
36467             callback : function(){
36468                 this.el.setStyle("z-index", "");
36469                 this.afterSlide();
36470                 if(this.split){
36471                     this.split.el.show();
36472                 }
36473                 this.fireEvent("invalidated", this);
36474                 this.fireEvent("expanded", this);
36475             },
36476             scope: this,
36477             block: true
36478         });
36479     },
36480
36481     anchors : {
36482         "west" : "left",
36483         "east" : "right",
36484         "north" : "top",
36485         "south" : "bottom"
36486     },
36487
36488     sanchors : {
36489         "west" : "l",
36490         "east" : "r",
36491         "north" : "t",
36492         "south" : "b"
36493     },
36494
36495     canchors : {
36496         "west" : "tl-tr",
36497         "east" : "tr-tl",
36498         "north" : "tl-bl",
36499         "south" : "bl-tl"
36500     },
36501
36502     getAnchor : function(){
36503         return this.anchors[this.position];
36504     },
36505
36506     getCollapseAnchor : function(){
36507         return this.canchors[this.position];
36508     },
36509
36510     getSlideAnchor : function(){
36511         return this.sanchors[this.position];
36512     },
36513
36514     getAlignAdj : function(){
36515         var cm = this.cmargins;
36516         switch(this.position){
36517             case "west":
36518                 return [0, 0];
36519             break;
36520             case "east":
36521                 return [0, 0];
36522             break;
36523             case "north":
36524                 return [0, 0];
36525             break;
36526             case "south":
36527                 return [0, 0];
36528             break;
36529         }
36530     },
36531
36532     getExpandAdj : function(){
36533         var c = this.collapsedEl, cm = this.cmargins;
36534         switch(this.position){
36535             case "west":
36536                 return [-(cm.right+c.getWidth()+cm.left), 0];
36537             break;
36538             case "east":
36539                 return [cm.right+c.getWidth()+cm.left, 0];
36540             break;
36541             case "north":
36542                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36543             break;
36544             case "south":
36545                 return [0, cm.top+cm.bottom+c.getHeight()];
36546             break;
36547         }
36548     }
36549 });/*
36550  * Based on:
36551  * Ext JS Library 1.1.1
36552  * Copyright(c) 2006-2007, Ext JS, LLC.
36553  *
36554  * Originally Released Under LGPL - original licence link has changed is not relivant.
36555  *
36556  * Fork - LGPL
36557  * <script type="text/javascript">
36558  */
36559 /*
36560  * These classes are private internal classes
36561  */
36562 Roo.bootstrap.layout.Center = function(config){
36563     config.region = "center";
36564     Roo.bootstrap.layout.Region.call(this, config);
36565     this.visible = true;
36566     this.minWidth = config.minWidth || 20;
36567     this.minHeight = config.minHeight || 20;
36568 };
36569
36570 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36571     hide : function(){
36572         // center panel can't be hidden
36573     },
36574     
36575     show : function(){
36576         // center panel can't be hidden
36577     },
36578     
36579     getMinWidth: function(){
36580         return this.minWidth;
36581     },
36582     
36583     getMinHeight: function(){
36584         return this.minHeight;
36585     }
36586 });
36587
36588
36589
36590
36591  
36592
36593
36594
36595
36596
36597 Roo.bootstrap.layout.North = function(config)
36598 {
36599     config.region = 'north';
36600     config.cursor = 'n-resize';
36601     
36602     Roo.bootstrap.layout.Split.call(this, config);
36603     
36604     
36605     if(this.split){
36606         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36607         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36608         this.split.el.addClass("roo-layout-split-v");
36609     }
36610     var size = config.initialSize || config.height;
36611     if(typeof size != "undefined"){
36612         this.el.setHeight(size);
36613     }
36614 };
36615 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36616 {
36617     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36618     
36619     
36620     
36621     getBox : function(){
36622         if(this.collapsed){
36623             return this.collapsedEl.getBox();
36624         }
36625         var box = this.el.getBox();
36626         if(this.split){
36627             box.height += this.split.el.getHeight();
36628         }
36629         return box;
36630     },
36631     
36632     updateBox : function(box){
36633         if(this.split && !this.collapsed){
36634             box.height -= this.split.el.getHeight();
36635             this.split.el.setLeft(box.x);
36636             this.split.el.setTop(box.y+box.height);
36637             this.split.el.setWidth(box.width);
36638         }
36639         if(this.collapsed){
36640             this.updateBody(box.width, null);
36641         }
36642         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36643     }
36644 });
36645
36646
36647
36648
36649
36650 Roo.bootstrap.layout.South = function(config){
36651     config.region = 'south';
36652     config.cursor = 's-resize';
36653     Roo.bootstrap.layout.Split.call(this, config);
36654     if(this.split){
36655         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36656         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36657         this.split.el.addClass("roo-layout-split-v");
36658     }
36659     var size = config.initialSize || config.height;
36660     if(typeof size != "undefined"){
36661         this.el.setHeight(size);
36662     }
36663 };
36664
36665 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36666     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36667     getBox : function(){
36668         if(this.collapsed){
36669             return this.collapsedEl.getBox();
36670         }
36671         var box = this.el.getBox();
36672         if(this.split){
36673             var sh = this.split.el.getHeight();
36674             box.height += sh;
36675             box.y -= sh;
36676         }
36677         return box;
36678     },
36679     
36680     updateBox : function(box){
36681         if(this.split && !this.collapsed){
36682             var sh = this.split.el.getHeight();
36683             box.height -= sh;
36684             box.y += sh;
36685             this.split.el.setLeft(box.x);
36686             this.split.el.setTop(box.y-sh);
36687             this.split.el.setWidth(box.width);
36688         }
36689         if(this.collapsed){
36690             this.updateBody(box.width, null);
36691         }
36692         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36693     }
36694 });
36695
36696 Roo.bootstrap.layout.East = function(config){
36697     config.region = "east";
36698     config.cursor = "e-resize";
36699     Roo.bootstrap.layout.Split.call(this, config);
36700     if(this.split){
36701         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36702         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36703         this.split.el.addClass("roo-layout-split-h");
36704     }
36705     var size = config.initialSize || config.width;
36706     if(typeof size != "undefined"){
36707         this.el.setWidth(size);
36708     }
36709 };
36710 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36711     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36712     getBox : function(){
36713         if(this.collapsed){
36714             return this.collapsedEl.getBox();
36715         }
36716         var box = this.el.getBox();
36717         if(this.split){
36718             var sw = this.split.el.getWidth();
36719             box.width += sw;
36720             box.x -= sw;
36721         }
36722         return box;
36723     },
36724
36725     updateBox : function(box){
36726         if(this.split && !this.collapsed){
36727             var sw = this.split.el.getWidth();
36728             box.width -= sw;
36729             this.split.el.setLeft(box.x);
36730             this.split.el.setTop(box.y);
36731             this.split.el.setHeight(box.height);
36732             box.x += sw;
36733         }
36734         if(this.collapsed){
36735             this.updateBody(null, box.height);
36736         }
36737         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36738     }
36739 });
36740
36741 Roo.bootstrap.layout.West = function(config){
36742     config.region = "west";
36743     config.cursor = "w-resize";
36744     
36745     Roo.bootstrap.layout.Split.call(this, config);
36746     if(this.split){
36747         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36748         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36749         this.split.el.addClass("roo-layout-split-h");
36750     }
36751     
36752 };
36753 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36754     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36755     
36756     onRender: function(ctr, pos)
36757     {
36758         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36759         var size = this.config.initialSize || this.config.width;
36760         if(typeof size != "undefined"){
36761             this.el.setWidth(size);
36762         }
36763     },
36764     
36765     getBox : function(){
36766         if(this.collapsed){
36767             return this.collapsedEl.getBox();
36768         }
36769         var box = this.el.getBox();
36770         if(this.split){
36771             box.width += this.split.el.getWidth();
36772         }
36773         return box;
36774     },
36775     
36776     updateBox : function(box){
36777         if(this.split && !this.collapsed){
36778             var sw = this.split.el.getWidth();
36779             box.width -= sw;
36780             this.split.el.setLeft(box.x+box.width);
36781             this.split.el.setTop(box.y);
36782             this.split.el.setHeight(box.height);
36783         }
36784         if(this.collapsed){
36785             this.updateBody(null, box.height);
36786         }
36787         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36788     }
36789 });
36790 Roo.namespace("Roo.bootstrap.panel");/*
36791  * Based on:
36792  * Ext JS Library 1.1.1
36793  * Copyright(c) 2006-2007, Ext JS, LLC.
36794  *
36795  * Originally Released Under LGPL - original licence link has changed is not relivant.
36796  *
36797  * Fork - LGPL
36798  * <script type="text/javascript">
36799  */
36800 /**
36801  * @class Roo.ContentPanel
36802  * @extends Roo.util.Observable
36803  * A basic ContentPanel element.
36804  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36805  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36806  * @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
36807  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36808  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36809  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36810  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36811  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36812  * @cfg {String} title          The title for this panel
36813  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36814  * @cfg {String} url            Calls {@link #setUrl} with this value
36815  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36816  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36817  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36818  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36819  * @cfg {Boolean} badges render the badges
36820
36821  * @constructor
36822  * Create a new ContentPanel.
36823  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36824  * @param {String/Object} config A string to set only the title or a config object
36825  * @param {String} content (optional) Set the HTML content for this panel
36826  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36827  */
36828 Roo.bootstrap.panel.Content = function( config){
36829     
36830     this.tpl = config.tpl || false;
36831     
36832     var el = config.el;
36833     var content = config.content;
36834
36835     if(config.autoCreate){ // xtype is available if this is called from factory
36836         el = Roo.id();
36837     }
36838     this.el = Roo.get(el);
36839     if(!this.el && config && config.autoCreate){
36840         if(typeof config.autoCreate == "object"){
36841             if(!config.autoCreate.id){
36842                 config.autoCreate.id = config.id||el;
36843             }
36844             this.el = Roo.DomHelper.append(document.body,
36845                         config.autoCreate, true);
36846         }else{
36847             var elcfg =  {   tag: "div",
36848                             cls: "roo-layout-inactive-content",
36849                             id: config.id||el
36850                             };
36851             if (config.html) {
36852                 elcfg.html = config.html;
36853                 
36854             }
36855                         
36856             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36857         }
36858     } 
36859     this.closable = false;
36860     this.loaded = false;
36861     this.active = false;
36862    
36863       
36864     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36865         
36866         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36867         
36868         this.wrapEl = this.el; //this.el.wrap();
36869         var ti = [];
36870         if (config.toolbar.items) {
36871             ti = config.toolbar.items ;
36872             delete config.toolbar.items ;
36873         }
36874         
36875         var nitems = [];
36876         this.toolbar.render(this.wrapEl, 'before');
36877         for(var i =0;i < ti.length;i++) {
36878           //  Roo.log(['add child', items[i]]);
36879             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36880         }
36881         this.toolbar.items = nitems;
36882         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36883         delete config.toolbar;
36884         
36885     }
36886     /*
36887     // xtype created footer. - not sure if will work as we normally have to render first..
36888     if (this.footer && !this.footer.el && this.footer.xtype) {
36889         if (!this.wrapEl) {
36890             this.wrapEl = this.el.wrap();
36891         }
36892     
36893         this.footer.container = this.wrapEl.createChild();
36894          
36895         this.footer = Roo.factory(this.footer, Roo);
36896         
36897     }
36898     */
36899     
36900      if(typeof config == "string"){
36901         this.title = config;
36902     }else{
36903         Roo.apply(this, config);
36904     }
36905     
36906     if(this.resizeEl){
36907         this.resizeEl = Roo.get(this.resizeEl, true);
36908     }else{
36909         this.resizeEl = this.el;
36910     }
36911     // handle view.xtype
36912     
36913  
36914     
36915     
36916     this.addEvents({
36917         /**
36918          * @event activate
36919          * Fires when this panel is activated. 
36920          * @param {Roo.ContentPanel} this
36921          */
36922         "activate" : true,
36923         /**
36924          * @event deactivate
36925          * Fires when this panel is activated. 
36926          * @param {Roo.ContentPanel} this
36927          */
36928         "deactivate" : true,
36929
36930         /**
36931          * @event resize
36932          * Fires when this panel is resized if fitToFrame is true.
36933          * @param {Roo.ContentPanel} this
36934          * @param {Number} width The width after any component adjustments
36935          * @param {Number} height The height after any component adjustments
36936          */
36937         "resize" : true,
36938         
36939          /**
36940          * @event render
36941          * Fires when this tab is created
36942          * @param {Roo.ContentPanel} this
36943          */
36944         "render" : true
36945         
36946         
36947         
36948     });
36949     
36950
36951     
36952     
36953     if(this.autoScroll){
36954         this.resizeEl.setStyle("overflow", "auto");
36955     } else {
36956         // fix randome scrolling
36957         //this.el.on('scroll', function() {
36958         //    Roo.log('fix random scolling');
36959         //    this.scrollTo('top',0); 
36960         //});
36961     }
36962     content = content || this.content;
36963     if(content){
36964         this.setContent(content);
36965     }
36966     if(config && config.url){
36967         this.setUrl(this.url, this.params, this.loadOnce);
36968     }
36969     
36970     
36971     
36972     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36973     
36974     if (this.view && typeof(this.view.xtype) != 'undefined') {
36975         this.view.el = this.el.appendChild(document.createElement("div"));
36976         this.view = Roo.factory(this.view); 
36977         this.view.render  &&  this.view.render(false, '');  
36978     }
36979     
36980     
36981     this.fireEvent('render', this);
36982 };
36983
36984 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36985     
36986     tabTip : '',
36987     
36988     setRegion : function(region){
36989         this.region = region;
36990         this.setActiveClass(region && !this.background);
36991     },
36992     
36993     
36994     setActiveClass: function(state)
36995     {
36996         if(state){
36997            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36998            this.el.setStyle('position','relative');
36999         }else{
37000            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37001            this.el.setStyle('position', 'absolute');
37002         } 
37003     },
37004     
37005     /**
37006      * Returns the toolbar for this Panel if one was configured. 
37007      * @return {Roo.Toolbar} 
37008      */
37009     getToolbar : function(){
37010         return this.toolbar;
37011     },
37012     
37013     setActiveState : function(active)
37014     {
37015         this.active = active;
37016         this.setActiveClass(active);
37017         if(!active){
37018             if(this.fireEvent("deactivate", this) === false){
37019                 return false;
37020             }
37021             return true;
37022         }
37023         this.fireEvent("activate", this);
37024         return true;
37025     },
37026     /**
37027      * Updates this panel's element
37028      * @param {String} content The new content
37029      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37030     */
37031     setContent : function(content, loadScripts){
37032         this.el.update(content, loadScripts);
37033     },
37034
37035     ignoreResize : function(w, h){
37036         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37037             return true;
37038         }else{
37039             this.lastSize = {width: w, height: h};
37040             return false;
37041         }
37042     },
37043     /**
37044      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37045      * @return {Roo.UpdateManager} The UpdateManager
37046      */
37047     getUpdateManager : function(){
37048         return this.el.getUpdateManager();
37049     },
37050      /**
37051      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37052      * @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:
37053 <pre><code>
37054 panel.load({
37055     url: "your-url.php",
37056     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37057     callback: yourFunction,
37058     scope: yourObject, //(optional scope)
37059     discardUrl: false,
37060     nocache: false,
37061     text: "Loading...",
37062     timeout: 30,
37063     scripts: false
37064 });
37065 </code></pre>
37066      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37067      * 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.
37068      * @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}
37069      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37070      * @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.
37071      * @return {Roo.ContentPanel} this
37072      */
37073     load : function(){
37074         var um = this.el.getUpdateManager();
37075         um.update.apply(um, arguments);
37076         return this;
37077     },
37078
37079
37080     /**
37081      * 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.
37082      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37083      * @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)
37084      * @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)
37085      * @return {Roo.UpdateManager} The UpdateManager
37086      */
37087     setUrl : function(url, params, loadOnce){
37088         if(this.refreshDelegate){
37089             this.removeListener("activate", this.refreshDelegate);
37090         }
37091         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37092         this.on("activate", this.refreshDelegate);
37093         return this.el.getUpdateManager();
37094     },
37095     
37096     _handleRefresh : function(url, params, loadOnce){
37097         if(!loadOnce || !this.loaded){
37098             var updater = this.el.getUpdateManager();
37099             updater.update(url, params, this._setLoaded.createDelegate(this));
37100         }
37101     },
37102     
37103     _setLoaded : function(){
37104         this.loaded = true;
37105     }, 
37106     
37107     /**
37108      * Returns this panel's id
37109      * @return {String} 
37110      */
37111     getId : function(){
37112         return this.el.id;
37113     },
37114     
37115     /** 
37116      * Returns this panel's element - used by regiosn to add.
37117      * @return {Roo.Element} 
37118      */
37119     getEl : function(){
37120         return this.wrapEl || this.el;
37121     },
37122     
37123    
37124     
37125     adjustForComponents : function(width, height)
37126     {
37127         //Roo.log('adjustForComponents ');
37128         if(this.resizeEl != this.el){
37129             width -= this.el.getFrameWidth('lr');
37130             height -= this.el.getFrameWidth('tb');
37131         }
37132         if(this.toolbar){
37133             var te = this.toolbar.getEl();
37134             te.setWidth(width);
37135             height -= te.getHeight();
37136         }
37137         if(this.footer){
37138             var te = this.footer.getEl();
37139             te.setWidth(width);
37140             height -= te.getHeight();
37141         }
37142         
37143         
37144         if(this.adjustments){
37145             width += this.adjustments[0];
37146             height += this.adjustments[1];
37147         }
37148         return {"width": width, "height": height};
37149     },
37150     
37151     setSize : function(width, height){
37152         if(this.fitToFrame && !this.ignoreResize(width, height)){
37153             if(this.fitContainer && this.resizeEl != this.el){
37154                 this.el.setSize(width, height);
37155             }
37156             var size = this.adjustForComponents(width, height);
37157             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37158             this.fireEvent('resize', this, size.width, size.height);
37159         }
37160     },
37161     
37162     /**
37163      * Returns this panel's title
37164      * @return {String} 
37165      */
37166     getTitle : function(){
37167         
37168         if (typeof(this.title) != 'object') {
37169             return this.title;
37170         }
37171         
37172         var t = '';
37173         for (var k in this.title) {
37174             if (!this.title.hasOwnProperty(k)) {
37175                 continue;
37176             }
37177             
37178             if (k.indexOf('-') >= 0) {
37179                 var s = k.split('-');
37180                 for (var i = 0; i<s.length; i++) {
37181                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37182                 }
37183             } else {
37184                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37185             }
37186         }
37187         return t;
37188     },
37189     
37190     /**
37191      * Set this panel's title
37192      * @param {String} title
37193      */
37194     setTitle : function(title){
37195         this.title = title;
37196         if(this.region){
37197             this.region.updatePanelTitle(this, title);
37198         }
37199     },
37200     
37201     /**
37202      * Returns true is this panel was configured to be closable
37203      * @return {Boolean} 
37204      */
37205     isClosable : function(){
37206         return this.closable;
37207     },
37208     
37209     beforeSlide : function(){
37210         this.el.clip();
37211         this.resizeEl.clip();
37212     },
37213     
37214     afterSlide : function(){
37215         this.el.unclip();
37216         this.resizeEl.unclip();
37217     },
37218     
37219     /**
37220      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37221      *   Will fail silently if the {@link #setUrl} method has not been called.
37222      *   This does not activate the panel, just updates its content.
37223      */
37224     refresh : function(){
37225         if(this.refreshDelegate){
37226            this.loaded = false;
37227            this.refreshDelegate();
37228         }
37229     },
37230     
37231     /**
37232      * Destroys this panel
37233      */
37234     destroy : function(){
37235         this.el.removeAllListeners();
37236         var tempEl = document.createElement("span");
37237         tempEl.appendChild(this.el.dom);
37238         tempEl.innerHTML = "";
37239         this.el.remove();
37240         this.el = null;
37241     },
37242     
37243     /**
37244      * form - if the content panel contains a form - this is a reference to it.
37245      * @type {Roo.form.Form}
37246      */
37247     form : false,
37248     /**
37249      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37250      *    This contains a reference to it.
37251      * @type {Roo.View}
37252      */
37253     view : false,
37254     
37255       /**
37256      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37257      * <pre><code>
37258
37259 layout.addxtype({
37260        xtype : 'Form',
37261        items: [ .... ]
37262    }
37263 );
37264
37265 </code></pre>
37266      * @param {Object} cfg Xtype definition of item to add.
37267      */
37268     
37269     
37270     getChildContainer: function () {
37271         return this.getEl();
37272     }
37273     
37274     
37275     /*
37276         var  ret = new Roo.factory(cfg);
37277         return ret;
37278         
37279         
37280         // add form..
37281         if (cfg.xtype.match(/^Form$/)) {
37282             
37283             var el;
37284             //if (this.footer) {
37285             //    el = this.footer.container.insertSibling(false, 'before');
37286             //} else {
37287                 el = this.el.createChild();
37288             //}
37289
37290             this.form = new  Roo.form.Form(cfg);
37291             
37292             
37293             if ( this.form.allItems.length) {
37294                 this.form.render(el.dom);
37295             }
37296             return this.form;
37297         }
37298         // should only have one of theses..
37299         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37300             // views.. should not be just added - used named prop 'view''
37301             
37302             cfg.el = this.el.appendChild(document.createElement("div"));
37303             // factory?
37304             
37305             var ret = new Roo.factory(cfg);
37306              
37307              ret.render && ret.render(false, ''); // render blank..
37308             this.view = ret;
37309             return ret;
37310         }
37311         return false;
37312     }
37313     \*/
37314 });
37315  
37316 /**
37317  * @class Roo.bootstrap.panel.Grid
37318  * @extends Roo.bootstrap.panel.Content
37319  * @constructor
37320  * Create a new GridPanel.
37321  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37322  * @param {Object} config A the config object
37323   
37324  */
37325
37326
37327
37328 Roo.bootstrap.panel.Grid = function(config)
37329 {
37330     
37331       
37332     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37333         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37334
37335     config.el = this.wrapper;
37336     //this.el = this.wrapper;
37337     
37338       if (config.container) {
37339         // ctor'ed from a Border/panel.grid
37340         
37341         
37342         this.wrapper.setStyle("overflow", "hidden");
37343         this.wrapper.addClass('roo-grid-container');
37344
37345     }
37346     
37347     
37348     if(config.toolbar){
37349         var tool_el = this.wrapper.createChild();    
37350         this.toolbar = Roo.factory(config.toolbar);
37351         var ti = [];
37352         if (config.toolbar.items) {
37353             ti = config.toolbar.items ;
37354             delete config.toolbar.items ;
37355         }
37356         
37357         var nitems = [];
37358         this.toolbar.render(tool_el);
37359         for(var i =0;i < ti.length;i++) {
37360           //  Roo.log(['add child', items[i]]);
37361             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37362         }
37363         this.toolbar.items = nitems;
37364         
37365         delete config.toolbar;
37366     }
37367     
37368     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37369     config.grid.scrollBody = true;;
37370     config.grid.monitorWindowResize = false; // turn off autosizing
37371     config.grid.autoHeight = false;
37372     config.grid.autoWidth = false;
37373     
37374     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37375     
37376     if (config.background) {
37377         // render grid on panel activation (if panel background)
37378         this.on('activate', function(gp) {
37379             if (!gp.grid.rendered) {
37380                 gp.grid.render(this.wrapper);
37381                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37382             }
37383         });
37384             
37385     } else {
37386         this.grid.render(this.wrapper);
37387         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37388
37389     }
37390     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37391     // ??? needed ??? config.el = this.wrapper;
37392     
37393     
37394     
37395   
37396     // xtype created footer. - not sure if will work as we normally have to render first..
37397     if (this.footer && !this.footer.el && this.footer.xtype) {
37398         
37399         var ctr = this.grid.getView().getFooterPanel(true);
37400         this.footer.dataSource = this.grid.dataSource;
37401         this.footer = Roo.factory(this.footer, Roo);
37402         this.footer.render(ctr);
37403         
37404     }
37405     
37406     
37407     
37408     
37409      
37410 };
37411
37412 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37413     getId : function(){
37414         return this.grid.id;
37415     },
37416     
37417     /**
37418      * Returns the grid for this panel
37419      * @return {Roo.bootstrap.Table} 
37420      */
37421     getGrid : function(){
37422         return this.grid;    
37423     },
37424     
37425     setSize : function(width, height){
37426         if(!this.ignoreResize(width, height)){
37427             var grid = this.grid;
37428             var size = this.adjustForComponents(width, height);
37429             var gridel = grid.getGridEl();
37430             gridel.setSize(size.width, size.height);
37431             /*
37432             var thd = grid.getGridEl().select('thead',true).first();
37433             var tbd = grid.getGridEl().select('tbody', true).first();
37434             if (tbd) {
37435                 tbd.setSize(width, height - thd.getHeight());
37436             }
37437             */
37438             grid.autoSize();
37439         }
37440     },
37441      
37442     
37443     
37444     beforeSlide : function(){
37445         this.grid.getView().scroller.clip();
37446     },
37447     
37448     afterSlide : function(){
37449         this.grid.getView().scroller.unclip();
37450     },
37451     
37452     destroy : function(){
37453         this.grid.destroy();
37454         delete this.grid;
37455         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37456     }
37457 });
37458
37459 /**
37460  * @class Roo.bootstrap.panel.Nest
37461  * @extends Roo.bootstrap.panel.Content
37462  * @constructor
37463  * Create a new Panel, that can contain a layout.Border.
37464  * 
37465  * 
37466  * @param {Roo.BorderLayout} layout The layout for this panel
37467  * @param {String/Object} config A string to set only the title or a config object
37468  */
37469 Roo.bootstrap.panel.Nest = function(config)
37470 {
37471     // construct with only one argument..
37472     /* FIXME - implement nicer consturctors
37473     if (layout.layout) {
37474         config = layout;
37475         layout = config.layout;
37476         delete config.layout;
37477     }
37478     if (layout.xtype && !layout.getEl) {
37479         // then layout needs constructing..
37480         layout = Roo.factory(layout, Roo);
37481     }
37482     */
37483     
37484     config.el =  config.layout.getEl();
37485     
37486     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37487     
37488     config.layout.monitorWindowResize = false; // turn off autosizing
37489     this.layout = config.layout;
37490     this.layout.getEl().addClass("roo-layout-nested-layout");
37491     
37492     
37493     
37494     
37495 };
37496
37497 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37498
37499     setSize : function(width, height){
37500         if(!this.ignoreResize(width, height)){
37501             var size = this.adjustForComponents(width, height);
37502             var el = this.layout.getEl();
37503             if (size.height < 1) {
37504                 el.setWidth(size.width);   
37505             } else {
37506                 el.setSize(size.width, size.height);
37507             }
37508             var touch = el.dom.offsetWidth;
37509             this.layout.layout();
37510             // ie requires a double layout on the first pass
37511             if(Roo.isIE && !this.initialized){
37512                 this.initialized = true;
37513                 this.layout.layout();
37514             }
37515         }
37516     },
37517     
37518     // activate all subpanels if not currently active..
37519     
37520     setActiveState : function(active){
37521         this.active = active;
37522         this.setActiveClass(active);
37523         
37524         if(!active){
37525             this.fireEvent("deactivate", this);
37526             return;
37527         }
37528         
37529         this.fireEvent("activate", this);
37530         // not sure if this should happen before or after..
37531         if (!this.layout) {
37532             return; // should not happen..
37533         }
37534         var reg = false;
37535         for (var r in this.layout.regions) {
37536             reg = this.layout.getRegion(r);
37537             if (reg.getActivePanel()) {
37538                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37539                 reg.setActivePanel(reg.getActivePanel());
37540                 continue;
37541             }
37542             if (!reg.panels.length) {
37543                 continue;
37544             }
37545             reg.showPanel(reg.getPanel(0));
37546         }
37547         
37548         
37549         
37550         
37551     },
37552     
37553     /**
37554      * Returns the nested BorderLayout for this panel
37555      * @return {Roo.BorderLayout} 
37556      */
37557     getLayout : function(){
37558         return this.layout;
37559     },
37560     
37561      /**
37562      * Adds a xtype elements to the layout of the nested panel
37563      * <pre><code>
37564
37565 panel.addxtype({
37566        xtype : 'ContentPanel',
37567        region: 'west',
37568        items: [ .... ]
37569    }
37570 );
37571
37572 panel.addxtype({
37573         xtype : 'NestedLayoutPanel',
37574         region: 'west',
37575         layout: {
37576            center: { },
37577            west: { }   
37578         },
37579         items : [ ... list of content panels or nested layout panels.. ]
37580    }
37581 );
37582 </code></pre>
37583      * @param {Object} cfg Xtype definition of item to add.
37584      */
37585     addxtype : function(cfg) {
37586         return this.layout.addxtype(cfg);
37587     
37588     }
37589 });        /*
37590  * Based on:
37591  * Ext JS Library 1.1.1
37592  * Copyright(c) 2006-2007, Ext JS, LLC.
37593  *
37594  * Originally Released Under LGPL - original licence link has changed is not relivant.
37595  *
37596  * Fork - LGPL
37597  * <script type="text/javascript">
37598  */
37599 /**
37600  * @class Roo.TabPanel
37601  * @extends Roo.util.Observable
37602  * A lightweight tab container.
37603  * <br><br>
37604  * Usage:
37605  * <pre><code>
37606 // basic tabs 1, built from existing content
37607 var tabs = new Roo.TabPanel("tabs1");
37608 tabs.addTab("script", "View Script");
37609 tabs.addTab("markup", "View Markup");
37610 tabs.activate("script");
37611
37612 // more advanced tabs, built from javascript
37613 var jtabs = new Roo.TabPanel("jtabs");
37614 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37615
37616 // set up the UpdateManager
37617 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37618 var updater = tab2.getUpdateManager();
37619 updater.setDefaultUrl("ajax1.htm");
37620 tab2.on('activate', updater.refresh, updater, true);
37621
37622 // Use setUrl for Ajax loading
37623 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37624 tab3.setUrl("ajax2.htm", null, true);
37625
37626 // Disabled tab
37627 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37628 tab4.disable();
37629
37630 jtabs.activate("jtabs-1");
37631  * </code></pre>
37632  * @constructor
37633  * Create a new TabPanel.
37634  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37635  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37636  */
37637 Roo.bootstrap.panel.Tabs = function(config){
37638     /**
37639     * The container element for this TabPanel.
37640     * @type Roo.Element
37641     */
37642     this.el = Roo.get(config.el);
37643     delete config.el;
37644     if(config){
37645         if(typeof config == "boolean"){
37646             this.tabPosition = config ? "bottom" : "top";
37647         }else{
37648             Roo.apply(this, config);
37649         }
37650     }
37651     
37652     if(this.tabPosition == "bottom"){
37653         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37654         this.el.addClass("roo-tabs-bottom");
37655     }
37656     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37657     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37658     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37659     if(Roo.isIE){
37660         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37661     }
37662     if(this.tabPosition != "bottom"){
37663         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37664          * @type Roo.Element
37665          */
37666         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37667         this.el.addClass("roo-tabs-top");
37668     }
37669     this.items = [];
37670
37671     this.bodyEl.setStyle("position", "relative");
37672
37673     this.active = null;
37674     this.activateDelegate = this.activate.createDelegate(this);
37675
37676     this.addEvents({
37677         /**
37678          * @event tabchange
37679          * Fires when the active tab changes
37680          * @param {Roo.TabPanel} this
37681          * @param {Roo.TabPanelItem} activePanel The new active tab
37682          */
37683         "tabchange": true,
37684         /**
37685          * @event beforetabchange
37686          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37687          * @param {Roo.TabPanel} this
37688          * @param {Object} e Set cancel to true on this object to cancel the tab change
37689          * @param {Roo.TabPanelItem} tab The tab being changed to
37690          */
37691         "beforetabchange" : true
37692     });
37693
37694     Roo.EventManager.onWindowResize(this.onResize, this);
37695     this.cpad = this.el.getPadding("lr");
37696     this.hiddenCount = 0;
37697
37698
37699     // toolbar on the tabbar support...
37700     if (this.toolbar) {
37701         alert("no toolbar support yet");
37702         this.toolbar  = false;
37703         /*
37704         var tcfg = this.toolbar;
37705         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37706         this.toolbar = new Roo.Toolbar(tcfg);
37707         if (Roo.isSafari) {
37708             var tbl = tcfg.container.child('table', true);
37709             tbl.setAttribute('width', '100%');
37710         }
37711         */
37712         
37713     }
37714    
37715
37716
37717     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37718 };
37719
37720 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37721     /*
37722      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37723      */
37724     tabPosition : "top",
37725     /*
37726      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37727      */
37728     currentTabWidth : 0,
37729     /*
37730      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37731      */
37732     minTabWidth : 40,
37733     /*
37734      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37735      */
37736     maxTabWidth : 250,
37737     /*
37738      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37739      */
37740     preferredTabWidth : 175,
37741     /*
37742      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37743      */
37744     resizeTabs : false,
37745     /*
37746      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37747      */
37748     monitorResize : true,
37749     /*
37750      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37751      */
37752     toolbar : false,
37753
37754     /**
37755      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37756      * @param {String} id The id of the div to use <b>or create</b>
37757      * @param {String} text The text for the tab
37758      * @param {String} content (optional) Content to put in the TabPanelItem body
37759      * @param {Boolean} closable (optional) True to create a close icon on the tab
37760      * @return {Roo.TabPanelItem} The created TabPanelItem
37761      */
37762     addTab : function(id, text, content, closable, tpl)
37763     {
37764         var item = new Roo.bootstrap.panel.TabItem({
37765             panel: this,
37766             id : id,
37767             text : text,
37768             closable : closable,
37769             tpl : tpl
37770         });
37771         this.addTabItem(item);
37772         if(content){
37773             item.setContent(content);
37774         }
37775         return item;
37776     },
37777
37778     /**
37779      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37780      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37781      * @return {Roo.TabPanelItem}
37782      */
37783     getTab : function(id){
37784         return this.items[id];
37785     },
37786
37787     /**
37788      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37789      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37790      */
37791     hideTab : function(id){
37792         var t = this.items[id];
37793         if(!t.isHidden()){
37794            t.setHidden(true);
37795            this.hiddenCount++;
37796            this.autoSizeTabs();
37797         }
37798     },
37799
37800     /**
37801      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37802      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37803      */
37804     unhideTab : function(id){
37805         var t = this.items[id];
37806         if(t.isHidden()){
37807            t.setHidden(false);
37808            this.hiddenCount--;
37809            this.autoSizeTabs();
37810         }
37811     },
37812
37813     /**
37814      * Adds an existing {@link Roo.TabPanelItem}.
37815      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37816      */
37817     addTabItem : function(item){
37818         this.items[item.id] = item;
37819         this.items.push(item);
37820       //  if(this.resizeTabs){
37821     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37822   //         this.autoSizeTabs();
37823 //        }else{
37824 //            item.autoSize();
37825        // }
37826     },
37827
37828     /**
37829      * Removes a {@link Roo.TabPanelItem}.
37830      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37831      */
37832     removeTab : function(id){
37833         var items = this.items;
37834         var tab = items[id];
37835         if(!tab) { return; }
37836         var index = items.indexOf(tab);
37837         if(this.active == tab && items.length > 1){
37838             var newTab = this.getNextAvailable(index);
37839             if(newTab) {
37840                 newTab.activate();
37841             }
37842         }
37843         this.stripEl.dom.removeChild(tab.pnode.dom);
37844         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37845             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37846         }
37847         items.splice(index, 1);
37848         delete this.items[tab.id];
37849         tab.fireEvent("close", tab);
37850         tab.purgeListeners();
37851         this.autoSizeTabs();
37852     },
37853
37854     getNextAvailable : function(start){
37855         var items = this.items;
37856         var index = start;
37857         // look for a next tab that will slide over to
37858         // replace the one being removed
37859         while(index < items.length){
37860             var item = items[++index];
37861             if(item && !item.isHidden()){
37862                 return item;
37863             }
37864         }
37865         // if one isn't found select the previous tab (on the left)
37866         index = start;
37867         while(index >= 0){
37868             var item = items[--index];
37869             if(item && !item.isHidden()){
37870                 return item;
37871             }
37872         }
37873         return null;
37874     },
37875
37876     /**
37877      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37878      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37879      */
37880     disableTab : function(id){
37881         var tab = this.items[id];
37882         if(tab && this.active != tab){
37883             tab.disable();
37884         }
37885     },
37886
37887     /**
37888      * Enables a {@link Roo.TabPanelItem} that is disabled.
37889      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37890      */
37891     enableTab : function(id){
37892         var tab = this.items[id];
37893         tab.enable();
37894     },
37895
37896     /**
37897      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37898      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37899      * @return {Roo.TabPanelItem} The TabPanelItem.
37900      */
37901     activate : function(id){
37902         var tab = this.items[id];
37903         if(!tab){
37904             return null;
37905         }
37906         if(tab == this.active || tab.disabled){
37907             return tab;
37908         }
37909         var e = {};
37910         this.fireEvent("beforetabchange", this, e, tab);
37911         if(e.cancel !== true && !tab.disabled){
37912             if(this.active){
37913                 this.active.hide();
37914             }
37915             this.active = this.items[id];
37916             this.active.show();
37917             this.fireEvent("tabchange", this, this.active);
37918         }
37919         return tab;
37920     },
37921
37922     /**
37923      * Gets the active {@link Roo.TabPanelItem}.
37924      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37925      */
37926     getActiveTab : function(){
37927         return this.active;
37928     },
37929
37930     /**
37931      * Updates the tab body element to fit the height of the container element
37932      * for overflow scrolling
37933      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37934      */
37935     syncHeight : function(targetHeight){
37936         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37937         var bm = this.bodyEl.getMargins();
37938         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37939         this.bodyEl.setHeight(newHeight);
37940         return newHeight;
37941     },
37942
37943     onResize : function(){
37944         if(this.monitorResize){
37945             this.autoSizeTabs();
37946         }
37947     },
37948
37949     /**
37950      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37951      */
37952     beginUpdate : function(){
37953         this.updating = true;
37954     },
37955
37956     /**
37957      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37958      */
37959     endUpdate : function(){
37960         this.updating = false;
37961         this.autoSizeTabs();
37962     },
37963
37964     /**
37965      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37966      */
37967     autoSizeTabs : function(){
37968         var count = this.items.length;
37969         var vcount = count - this.hiddenCount;
37970         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37971             return;
37972         }
37973         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37974         var availWidth = Math.floor(w / vcount);
37975         var b = this.stripBody;
37976         if(b.getWidth() > w){
37977             var tabs = this.items;
37978             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37979             if(availWidth < this.minTabWidth){
37980                 /*if(!this.sleft){    // incomplete scrolling code
37981                     this.createScrollButtons();
37982                 }
37983                 this.showScroll();
37984                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37985             }
37986         }else{
37987             if(this.currentTabWidth < this.preferredTabWidth){
37988                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37989             }
37990         }
37991     },
37992
37993     /**
37994      * Returns the number of tabs in this TabPanel.
37995      * @return {Number}
37996      */
37997      getCount : function(){
37998          return this.items.length;
37999      },
38000
38001     /**
38002      * Resizes all the tabs to the passed width
38003      * @param {Number} The new width
38004      */
38005     setTabWidth : function(width){
38006         this.currentTabWidth = width;
38007         for(var i = 0, len = this.items.length; i < len; i++) {
38008                 if(!this.items[i].isHidden()) {
38009                 this.items[i].setWidth(width);
38010             }
38011         }
38012     },
38013
38014     /**
38015      * Destroys this TabPanel
38016      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38017      */
38018     destroy : function(removeEl){
38019         Roo.EventManager.removeResizeListener(this.onResize, this);
38020         for(var i = 0, len = this.items.length; i < len; i++){
38021             this.items[i].purgeListeners();
38022         }
38023         if(removeEl === true){
38024             this.el.update("");
38025             this.el.remove();
38026         }
38027     },
38028     
38029     createStrip : function(container)
38030     {
38031         var strip = document.createElement("nav");
38032         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38033         container.appendChild(strip);
38034         return strip;
38035     },
38036     
38037     createStripList : function(strip)
38038     {
38039         // div wrapper for retard IE
38040         // returns the "tr" element.
38041         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38042         //'<div class="x-tabs-strip-wrap">'+
38043           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38044           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38045         return strip.firstChild; //.firstChild.firstChild.firstChild;
38046     },
38047     createBody : function(container)
38048     {
38049         var body = document.createElement("div");
38050         Roo.id(body, "tab-body");
38051         //Roo.fly(body).addClass("x-tabs-body");
38052         Roo.fly(body).addClass("tab-content");
38053         container.appendChild(body);
38054         return body;
38055     },
38056     createItemBody :function(bodyEl, id){
38057         var body = Roo.getDom(id);
38058         if(!body){
38059             body = document.createElement("div");
38060             body.id = id;
38061         }
38062         //Roo.fly(body).addClass("x-tabs-item-body");
38063         Roo.fly(body).addClass("tab-pane");
38064          bodyEl.insertBefore(body, bodyEl.firstChild);
38065         return body;
38066     },
38067     /** @private */
38068     createStripElements :  function(stripEl, text, closable, tpl)
38069     {
38070         var td = document.createElement("li"); // was td..
38071         
38072         
38073         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38074         
38075         
38076         stripEl.appendChild(td);
38077         /*if(closable){
38078             td.className = "x-tabs-closable";
38079             if(!this.closeTpl){
38080                 this.closeTpl = new Roo.Template(
38081                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38082                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38083                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38084                 );
38085             }
38086             var el = this.closeTpl.overwrite(td, {"text": text});
38087             var close = el.getElementsByTagName("div")[0];
38088             var inner = el.getElementsByTagName("em")[0];
38089             return {"el": el, "close": close, "inner": inner};
38090         } else {
38091         */
38092         // not sure what this is..
38093 //            if(!this.tabTpl){
38094                 //this.tabTpl = new Roo.Template(
38095                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38096                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38097                 //);
38098 //                this.tabTpl = new Roo.Template(
38099 //                   '<a href="#">' +
38100 //                   '<span unselectable="on"' +
38101 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38102 //                            ' >{text}</span></a>'
38103 //                );
38104 //                
38105 //            }
38106
38107
38108             var template = tpl || this.tabTpl || false;
38109             
38110             if(!template){
38111                 
38112                 template = new Roo.Template(
38113                    '<a href="#">' +
38114                    '<span unselectable="on"' +
38115                             (this.disableTooltips ? '' : ' title="{text}"') +
38116                             ' >{text}</span></a>'
38117                 );
38118             }
38119             
38120             switch (typeof(template)) {
38121                 case 'object' :
38122                     break;
38123                 case 'string' :
38124                     template = new Roo.Template(template);
38125                     break;
38126                 default :
38127                     break;
38128             }
38129             
38130             var el = template.overwrite(td, {"text": text});
38131             
38132             var inner = el.getElementsByTagName("span")[0];
38133             
38134             return {"el": el, "inner": inner};
38135             
38136     }
38137         
38138     
38139 });
38140
38141 /**
38142  * @class Roo.TabPanelItem
38143  * @extends Roo.util.Observable
38144  * Represents an individual item (tab plus body) in a TabPanel.
38145  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38146  * @param {String} id The id of this TabPanelItem
38147  * @param {String} text The text for the tab of this TabPanelItem
38148  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38149  */
38150 Roo.bootstrap.panel.TabItem = function(config){
38151     /**
38152      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38153      * @type Roo.TabPanel
38154      */
38155     this.tabPanel = config.panel;
38156     /**
38157      * The id for this TabPanelItem
38158      * @type String
38159      */
38160     this.id = config.id;
38161     /** @private */
38162     this.disabled = false;
38163     /** @private */
38164     this.text = config.text;
38165     /** @private */
38166     this.loaded = false;
38167     this.closable = config.closable;
38168
38169     /**
38170      * The body element for this TabPanelItem.
38171      * @type Roo.Element
38172      */
38173     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38174     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38175     this.bodyEl.setStyle("display", "block");
38176     this.bodyEl.setStyle("zoom", "1");
38177     //this.hideAction();
38178
38179     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38180     /** @private */
38181     this.el = Roo.get(els.el);
38182     this.inner = Roo.get(els.inner, true);
38183     this.textEl = Roo.get(this.el.dom.firstChild, true);
38184     this.pnode = Roo.get(els.el.parentNode, true);
38185 //    this.el.on("mousedown", this.onTabMouseDown, this);
38186     this.el.on("click", this.onTabClick, this);
38187     /** @private */
38188     if(config.closable){
38189         var c = Roo.get(els.close, true);
38190         c.dom.title = this.closeText;
38191         c.addClassOnOver("close-over");
38192         c.on("click", this.closeClick, this);
38193      }
38194
38195     this.addEvents({
38196          /**
38197          * @event activate
38198          * Fires when this tab becomes the active tab.
38199          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38200          * @param {Roo.TabPanelItem} this
38201          */
38202         "activate": true,
38203         /**
38204          * @event beforeclose
38205          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38206          * @param {Roo.TabPanelItem} this
38207          * @param {Object} e Set cancel to true on this object to cancel the close.
38208          */
38209         "beforeclose": true,
38210         /**
38211          * @event close
38212          * Fires when this tab is closed.
38213          * @param {Roo.TabPanelItem} this
38214          */
38215          "close": true,
38216         /**
38217          * @event deactivate
38218          * Fires when this tab is no longer the active tab.
38219          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38220          * @param {Roo.TabPanelItem} this
38221          */
38222          "deactivate" : true
38223     });
38224     this.hidden = false;
38225
38226     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38227 };
38228
38229 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38230            {
38231     purgeListeners : function(){
38232        Roo.util.Observable.prototype.purgeListeners.call(this);
38233        this.el.removeAllListeners();
38234     },
38235     /**
38236      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38237      */
38238     show : function(){
38239         this.pnode.addClass("active");
38240         this.showAction();
38241         if(Roo.isOpera){
38242             this.tabPanel.stripWrap.repaint();
38243         }
38244         this.fireEvent("activate", this.tabPanel, this);
38245     },
38246
38247     /**
38248      * Returns true if this tab is the active tab.
38249      * @return {Boolean}
38250      */
38251     isActive : function(){
38252         return this.tabPanel.getActiveTab() == this;
38253     },
38254
38255     /**
38256      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38257      */
38258     hide : function(){
38259         this.pnode.removeClass("active");
38260         this.hideAction();
38261         this.fireEvent("deactivate", this.tabPanel, this);
38262     },
38263
38264     hideAction : function(){
38265         this.bodyEl.hide();
38266         this.bodyEl.setStyle("position", "absolute");
38267         this.bodyEl.setLeft("-20000px");
38268         this.bodyEl.setTop("-20000px");
38269     },
38270
38271     showAction : function(){
38272         this.bodyEl.setStyle("position", "relative");
38273         this.bodyEl.setTop("");
38274         this.bodyEl.setLeft("");
38275         this.bodyEl.show();
38276     },
38277
38278     /**
38279      * Set the tooltip for the tab.
38280      * @param {String} tooltip The tab's tooltip
38281      */
38282     setTooltip : function(text){
38283         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38284             this.textEl.dom.qtip = text;
38285             this.textEl.dom.removeAttribute('title');
38286         }else{
38287             this.textEl.dom.title = text;
38288         }
38289     },
38290
38291     onTabClick : function(e){
38292         e.preventDefault();
38293         this.tabPanel.activate(this.id);
38294     },
38295
38296     onTabMouseDown : function(e){
38297         e.preventDefault();
38298         this.tabPanel.activate(this.id);
38299     },
38300 /*
38301     getWidth : function(){
38302         return this.inner.getWidth();
38303     },
38304
38305     setWidth : function(width){
38306         var iwidth = width - this.pnode.getPadding("lr");
38307         this.inner.setWidth(iwidth);
38308         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38309         this.pnode.setWidth(width);
38310     },
38311 */
38312     /**
38313      * Show or hide the tab
38314      * @param {Boolean} hidden True to hide or false to show.
38315      */
38316     setHidden : function(hidden){
38317         this.hidden = hidden;
38318         this.pnode.setStyle("display", hidden ? "none" : "");
38319     },
38320
38321     /**
38322      * Returns true if this tab is "hidden"
38323      * @return {Boolean}
38324      */
38325     isHidden : function(){
38326         return this.hidden;
38327     },
38328
38329     /**
38330      * Returns the text for this tab
38331      * @return {String}
38332      */
38333     getText : function(){
38334         return this.text;
38335     },
38336     /*
38337     autoSize : function(){
38338         //this.el.beginMeasure();
38339         this.textEl.setWidth(1);
38340         /*
38341          *  #2804 [new] Tabs in Roojs
38342          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38343          */
38344         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38345         //this.el.endMeasure();
38346     //},
38347
38348     /**
38349      * Sets the text for the tab (Note: this also sets the tooltip text)
38350      * @param {String} text The tab's text and tooltip
38351      */
38352     setText : function(text){
38353         this.text = text;
38354         this.textEl.update(text);
38355         this.setTooltip(text);
38356         //if(!this.tabPanel.resizeTabs){
38357         //    this.autoSize();
38358         //}
38359     },
38360     /**
38361      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38362      */
38363     activate : function(){
38364         this.tabPanel.activate(this.id);
38365     },
38366
38367     /**
38368      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38369      */
38370     disable : function(){
38371         if(this.tabPanel.active != this){
38372             this.disabled = true;
38373             this.pnode.addClass("disabled");
38374         }
38375     },
38376
38377     /**
38378      * Enables this TabPanelItem if it was previously disabled.
38379      */
38380     enable : function(){
38381         this.disabled = false;
38382         this.pnode.removeClass("disabled");
38383     },
38384
38385     /**
38386      * Sets the content for this TabPanelItem.
38387      * @param {String} content The content
38388      * @param {Boolean} loadScripts true to look for and load scripts
38389      */
38390     setContent : function(content, loadScripts){
38391         this.bodyEl.update(content, loadScripts);
38392     },
38393
38394     /**
38395      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38396      * @return {Roo.UpdateManager} The UpdateManager
38397      */
38398     getUpdateManager : function(){
38399         return this.bodyEl.getUpdateManager();
38400     },
38401
38402     /**
38403      * Set a URL to be used to load the content for this TabPanelItem.
38404      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38405      * @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)
38406      * @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)
38407      * @return {Roo.UpdateManager} The UpdateManager
38408      */
38409     setUrl : function(url, params, loadOnce){
38410         if(this.refreshDelegate){
38411             this.un('activate', this.refreshDelegate);
38412         }
38413         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38414         this.on("activate", this.refreshDelegate);
38415         return this.bodyEl.getUpdateManager();
38416     },
38417
38418     /** @private */
38419     _handleRefresh : function(url, params, loadOnce){
38420         if(!loadOnce || !this.loaded){
38421             var updater = this.bodyEl.getUpdateManager();
38422             updater.update(url, params, this._setLoaded.createDelegate(this));
38423         }
38424     },
38425
38426     /**
38427      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38428      *   Will fail silently if the setUrl method has not been called.
38429      *   This does not activate the panel, just updates its content.
38430      */
38431     refresh : function(){
38432         if(this.refreshDelegate){
38433            this.loaded = false;
38434            this.refreshDelegate();
38435         }
38436     },
38437
38438     /** @private */
38439     _setLoaded : function(){
38440         this.loaded = true;
38441     },
38442
38443     /** @private */
38444     closeClick : function(e){
38445         var o = {};
38446         e.stopEvent();
38447         this.fireEvent("beforeclose", this, o);
38448         if(o.cancel !== true){
38449             this.tabPanel.removeTab(this.id);
38450         }
38451     },
38452     /**
38453      * The text displayed in the tooltip for the close icon.
38454      * @type String
38455      */
38456     closeText : "Close this tab"
38457 });
38458 /**
38459 *    This script refer to:
38460 *    Title: International Telephone Input
38461 *    Author: Jack O'Connor
38462 *    Code version:  v12.1.12
38463 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38464 **/
38465
38466 Roo.bootstrap.PhoneInputData = function() {
38467     var d = [
38468       [
38469         "Afghanistan (‫افغانستان‬‎)",
38470         "af",
38471         "93"
38472       ],
38473       [
38474         "Albania (Shqipëri)",
38475         "al",
38476         "355"
38477       ],
38478       [
38479         "Algeria (‫الجزائر‬‎)",
38480         "dz",
38481         "213"
38482       ],
38483       [
38484         "American Samoa",
38485         "as",
38486         "1684"
38487       ],
38488       [
38489         "Andorra",
38490         "ad",
38491         "376"
38492       ],
38493       [
38494         "Angola",
38495         "ao",
38496         "244"
38497       ],
38498       [
38499         "Anguilla",
38500         "ai",
38501         "1264"
38502       ],
38503       [
38504         "Antigua and Barbuda",
38505         "ag",
38506         "1268"
38507       ],
38508       [
38509         "Argentina",
38510         "ar",
38511         "54"
38512       ],
38513       [
38514         "Armenia (Հայաստան)",
38515         "am",
38516         "374"
38517       ],
38518       [
38519         "Aruba",
38520         "aw",
38521         "297"
38522       ],
38523       [
38524         "Australia",
38525         "au",
38526         "61",
38527         0
38528       ],
38529       [
38530         "Austria (Österreich)",
38531         "at",
38532         "43"
38533       ],
38534       [
38535         "Azerbaijan (Azərbaycan)",
38536         "az",
38537         "994"
38538       ],
38539       [
38540         "Bahamas",
38541         "bs",
38542         "1242"
38543       ],
38544       [
38545         "Bahrain (‫البحرين‬‎)",
38546         "bh",
38547         "973"
38548       ],
38549       [
38550         "Bangladesh (বাংলাদেশ)",
38551         "bd",
38552         "880"
38553       ],
38554       [
38555         "Barbados",
38556         "bb",
38557         "1246"
38558       ],
38559       [
38560         "Belarus (Беларусь)",
38561         "by",
38562         "375"
38563       ],
38564       [
38565         "Belgium (België)",
38566         "be",
38567         "32"
38568       ],
38569       [
38570         "Belize",
38571         "bz",
38572         "501"
38573       ],
38574       [
38575         "Benin (Bénin)",
38576         "bj",
38577         "229"
38578       ],
38579       [
38580         "Bermuda",
38581         "bm",
38582         "1441"
38583       ],
38584       [
38585         "Bhutan (འབྲུག)",
38586         "bt",
38587         "975"
38588       ],
38589       [
38590         "Bolivia",
38591         "bo",
38592         "591"
38593       ],
38594       [
38595         "Bosnia and Herzegovina (Босна и Херцеговина)",
38596         "ba",
38597         "387"
38598       ],
38599       [
38600         "Botswana",
38601         "bw",
38602         "267"
38603       ],
38604       [
38605         "Brazil (Brasil)",
38606         "br",
38607         "55"
38608       ],
38609       [
38610         "British Indian Ocean Territory",
38611         "io",
38612         "246"
38613       ],
38614       [
38615         "British Virgin Islands",
38616         "vg",
38617         "1284"
38618       ],
38619       [
38620         "Brunei",
38621         "bn",
38622         "673"
38623       ],
38624       [
38625         "Bulgaria (България)",
38626         "bg",
38627         "359"
38628       ],
38629       [
38630         "Burkina Faso",
38631         "bf",
38632         "226"
38633       ],
38634       [
38635         "Burundi (Uburundi)",
38636         "bi",
38637         "257"
38638       ],
38639       [
38640         "Cambodia (កម្ពុជា)",
38641         "kh",
38642         "855"
38643       ],
38644       [
38645         "Cameroon (Cameroun)",
38646         "cm",
38647         "237"
38648       ],
38649       [
38650         "Canada",
38651         "ca",
38652         "1",
38653         1,
38654         ["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"]
38655       ],
38656       [
38657         "Cape Verde (Kabu Verdi)",
38658         "cv",
38659         "238"
38660       ],
38661       [
38662         "Caribbean Netherlands",
38663         "bq",
38664         "599",
38665         1
38666       ],
38667       [
38668         "Cayman Islands",
38669         "ky",
38670         "1345"
38671       ],
38672       [
38673         "Central African Republic (République centrafricaine)",
38674         "cf",
38675         "236"
38676       ],
38677       [
38678         "Chad (Tchad)",
38679         "td",
38680         "235"
38681       ],
38682       [
38683         "Chile",
38684         "cl",
38685         "56"
38686       ],
38687       [
38688         "China (中国)",
38689         "cn",
38690         "86"
38691       ],
38692       [
38693         "Christmas Island",
38694         "cx",
38695         "61",
38696         2
38697       ],
38698       [
38699         "Cocos (Keeling) Islands",
38700         "cc",
38701         "61",
38702         1
38703       ],
38704       [
38705         "Colombia",
38706         "co",
38707         "57"
38708       ],
38709       [
38710         "Comoros (‫جزر القمر‬‎)",
38711         "km",
38712         "269"
38713       ],
38714       [
38715         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38716         "cd",
38717         "243"
38718       ],
38719       [
38720         "Congo (Republic) (Congo-Brazzaville)",
38721         "cg",
38722         "242"
38723       ],
38724       [
38725         "Cook Islands",
38726         "ck",
38727         "682"
38728       ],
38729       [
38730         "Costa Rica",
38731         "cr",
38732         "506"
38733       ],
38734       [
38735         "Côte d’Ivoire",
38736         "ci",
38737         "225"
38738       ],
38739       [
38740         "Croatia (Hrvatska)",
38741         "hr",
38742         "385"
38743       ],
38744       [
38745         "Cuba",
38746         "cu",
38747         "53"
38748       ],
38749       [
38750         "Curaçao",
38751         "cw",
38752         "599",
38753         0
38754       ],
38755       [
38756         "Cyprus (Κύπρος)",
38757         "cy",
38758         "357"
38759       ],
38760       [
38761         "Czech Republic (Česká republika)",
38762         "cz",
38763         "420"
38764       ],
38765       [
38766         "Denmark (Danmark)",
38767         "dk",
38768         "45"
38769       ],
38770       [
38771         "Djibouti",
38772         "dj",
38773         "253"
38774       ],
38775       [
38776         "Dominica",
38777         "dm",
38778         "1767"
38779       ],
38780       [
38781         "Dominican Republic (República Dominicana)",
38782         "do",
38783         "1",
38784         2,
38785         ["809", "829", "849"]
38786       ],
38787       [
38788         "Ecuador",
38789         "ec",
38790         "593"
38791       ],
38792       [
38793         "Egypt (‫مصر‬‎)",
38794         "eg",
38795         "20"
38796       ],
38797       [
38798         "El Salvador",
38799         "sv",
38800         "503"
38801       ],
38802       [
38803         "Equatorial Guinea (Guinea Ecuatorial)",
38804         "gq",
38805         "240"
38806       ],
38807       [
38808         "Eritrea",
38809         "er",
38810         "291"
38811       ],
38812       [
38813         "Estonia (Eesti)",
38814         "ee",
38815         "372"
38816       ],
38817       [
38818         "Ethiopia",
38819         "et",
38820         "251"
38821       ],
38822       [
38823         "Falkland Islands (Islas Malvinas)",
38824         "fk",
38825         "500"
38826       ],
38827       [
38828         "Faroe Islands (Føroyar)",
38829         "fo",
38830         "298"
38831       ],
38832       [
38833         "Fiji",
38834         "fj",
38835         "679"
38836       ],
38837       [
38838         "Finland (Suomi)",
38839         "fi",
38840         "358",
38841         0
38842       ],
38843       [
38844         "France",
38845         "fr",
38846         "33"
38847       ],
38848       [
38849         "French Guiana (Guyane française)",
38850         "gf",
38851         "594"
38852       ],
38853       [
38854         "French Polynesia (Polynésie française)",
38855         "pf",
38856         "689"
38857       ],
38858       [
38859         "Gabon",
38860         "ga",
38861         "241"
38862       ],
38863       [
38864         "Gambia",
38865         "gm",
38866         "220"
38867       ],
38868       [
38869         "Georgia (საქართველო)",
38870         "ge",
38871         "995"
38872       ],
38873       [
38874         "Germany (Deutschland)",
38875         "de",
38876         "49"
38877       ],
38878       [
38879         "Ghana (Gaana)",
38880         "gh",
38881         "233"
38882       ],
38883       [
38884         "Gibraltar",
38885         "gi",
38886         "350"
38887       ],
38888       [
38889         "Greece (Ελλάδα)",
38890         "gr",
38891         "30"
38892       ],
38893       [
38894         "Greenland (Kalaallit Nunaat)",
38895         "gl",
38896         "299"
38897       ],
38898       [
38899         "Grenada",
38900         "gd",
38901         "1473"
38902       ],
38903       [
38904         "Guadeloupe",
38905         "gp",
38906         "590",
38907         0
38908       ],
38909       [
38910         "Guam",
38911         "gu",
38912         "1671"
38913       ],
38914       [
38915         "Guatemala",
38916         "gt",
38917         "502"
38918       ],
38919       [
38920         "Guernsey",
38921         "gg",
38922         "44",
38923         1
38924       ],
38925       [
38926         "Guinea (Guinée)",
38927         "gn",
38928         "224"
38929       ],
38930       [
38931         "Guinea-Bissau (Guiné Bissau)",
38932         "gw",
38933         "245"
38934       ],
38935       [
38936         "Guyana",
38937         "gy",
38938         "592"
38939       ],
38940       [
38941         "Haiti",
38942         "ht",
38943         "509"
38944       ],
38945       [
38946         "Honduras",
38947         "hn",
38948         "504"
38949       ],
38950       [
38951         "Hong Kong (香港)",
38952         "hk",
38953         "852"
38954       ],
38955       [
38956         "Hungary (Magyarország)",
38957         "hu",
38958         "36"
38959       ],
38960       [
38961         "Iceland (Ísland)",
38962         "is",
38963         "354"
38964       ],
38965       [
38966         "India (भारत)",
38967         "in",
38968         "91"
38969       ],
38970       [
38971         "Indonesia",
38972         "id",
38973         "62"
38974       ],
38975       [
38976         "Iran (‫ایران‬‎)",
38977         "ir",
38978         "98"
38979       ],
38980       [
38981         "Iraq (‫العراق‬‎)",
38982         "iq",
38983         "964"
38984       ],
38985       [
38986         "Ireland",
38987         "ie",
38988         "353"
38989       ],
38990       [
38991         "Isle of Man",
38992         "im",
38993         "44",
38994         2
38995       ],
38996       [
38997         "Israel (‫ישראל‬‎)",
38998         "il",
38999         "972"
39000       ],
39001       [
39002         "Italy (Italia)",
39003         "it",
39004         "39",
39005         0
39006       ],
39007       [
39008         "Jamaica",
39009         "jm",
39010         "1876"
39011       ],
39012       [
39013         "Japan (日本)",
39014         "jp",
39015         "81"
39016       ],
39017       [
39018         "Jersey",
39019         "je",
39020         "44",
39021         3
39022       ],
39023       [
39024         "Jordan (‫الأردن‬‎)",
39025         "jo",
39026         "962"
39027       ],
39028       [
39029         "Kazakhstan (Казахстан)",
39030         "kz",
39031         "7",
39032         1
39033       ],
39034       [
39035         "Kenya",
39036         "ke",
39037         "254"
39038       ],
39039       [
39040         "Kiribati",
39041         "ki",
39042         "686"
39043       ],
39044       [
39045         "Kosovo",
39046         "xk",
39047         "383"
39048       ],
39049       [
39050         "Kuwait (‫الكويت‬‎)",
39051         "kw",
39052         "965"
39053       ],
39054       [
39055         "Kyrgyzstan (Кыргызстан)",
39056         "kg",
39057         "996"
39058       ],
39059       [
39060         "Laos (ລາວ)",
39061         "la",
39062         "856"
39063       ],
39064       [
39065         "Latvia (Latvija)",
39066         "lv",
39067         "371"
39068       ],
39069       [
39070         "Lebanon (‫لبنان‬‎)",
39071         "lb",
39072         "961"
39073       ],
39074       [
39075         "Lesotho",
39076         "ls",
39077         "266"
39078       ],
39079       [
39080         "Liberia",
39081         "lr",
39082         "231"
39083       ],
39084       [
39085         "Libya (‫ليبيا‬‎)",
39086         "ly",
39087         "218"
39088       ],
39089       [
39090         "Liechtenstein",
39091         "li",
39092         "423"
39093       ],
39094       [
39095         "Lithuania (Lietuva)",
39096         "lt",
39097         "370"
39098       ],
39099       [
39100         "Luxembourg",
39101         "lu",
39102         "352"
39103       ],
39104       [
39105         "Macau (澳門)",
39106         "mo",
39107         "853"
39108       ],
39109       [
39110         "Macedonia (FYROM) (Македонија)",
39111         "mk",
39112         "389"
39113       ],
39114       [
39115         "Madagascar (Madagasikara)",
39116         "mg",
39117         "261"
39118       ],
39119       [
39120         "Malawi",
39121         "mw",
39122         "265"
39123       ],
39124       [
39125         "Malaysia",
39126         "my",
39127         "60"
39128       ],
39129       [
39130         "Maldives",
39131         "mv",
39132         "960"
39133       ],
39134       [
39135         "Mali",
39136         "ml",
39137         "223"
39138       ],
39139       [
39140         "Malta",
39141         "mt",
39142         "356"
39143       ],
39144       [
39145         "Marshall Islands",
39146         "mh",
39147         "692"
39148       ],
39149       [
39150         "Martinique",
39151         "mq",
39152         "596"
39153       ],
39154       [
39155         "Mauritania (‫موريتانيا‬‎)",
39156         "mr",
39157         "222"
39158       ],
39159       [
39160         "Mauritius (Moris)",
39161         "mu",
39162         "230"
39163       ],
39164       [
39165         "Mayotte",
39166         "yt",
39167         "262",
39168         1
39169       ],
39170       [
39171         "Mexico (México)",
39172         "mx",
39173         "52"
39174       ],
39175       [
39176         "Micronesia",
39177         "fm",
39178         "691"
39179       ],
39180       [
39181         "Moldova (Republica Moldova)",
39182         "md",
39183         "373"
39184       ],
39185       [
39186         "Monaco",
39187         "mc",
39188         "377"
39189       ],
39190       [
39191         "Mongolia (Монгол)",
39192         "mn",
39193         "976"
39194       ],
39195       [
39196         "Montenegro (Crna Gora)",
39197         "me",
39198         "382"
39199       ],
39200       [
39201         "Montserrat",
39202         "ms",
39203         "1664"
39204       ],
39205       [
39206         "Morocco (‫المغرب‬‎)",
39207         "ma",
39208         "212",
39209         0
39210       ],
39211       [
39212         "Mozambique (Moçambique)",
39213         "mz",
39214         "258"
39215       ],
39216       [
39217         "Myanmar (Burma) (မြန်မာ)",
39218         "mm",
39219         "95"
39220       ],
39221       [
39222         "Namibia (Namibië)",
39223         "na",
39224         "264"
39225       ],
39226       [
39227         "Nauru",
39228         "nr",
39229         "674"
39230       ],
39231       [
39232         "Nepal (नेपाल)",
39233         "np",
39234         "977"
39235       ],
39236       [
39237         "Netherlands (Nederland)",
39238         "nl",
39239         "31"
39240       ],
39241       [
39242         "New Caledonia (Nouvelle-Calédonie)",
39243         "nc",
39244         "687"
39245       ],
39246       [
39247         "New Zealand",
39248         "nz",
39249         "64"
39250       ],
39251       [
39252         "Nicaragua",
39253         "ni",
39254         "505"
39255       ],
39256       [
39257         "Niger (Nijar)",
39258         "ne",
39259         "227"
39260       ],
39261       [
39262         "Nigeria",
39263         "ng",
39264         "234"
39265       ],
39266       [
39267         "Niue",
39268         "nu",
39269         "683"
39270       ],
39271       [
39272         "Norfolk Island",
39273         "nf",
39274         "672"
39275       ],
39276       [
39277         "North Korea (조선 민주주의 인민 공화국)",
39278         "kp",
39279         "850"
39280       ],
39281       [
39282         "Northern Mariana Islands",
39283         "mp",
39284         "1670"
39285       ],
39286       [
39287         "Norway (Norge)",
39288         "no",
39289         "47",
39290         0
39291       ],
39292       [
39293         "Oman (‫عُمان‬‎)",
39294         "om",
39295         "968"
39296       ],
39297       [
39298         "Pakistan (‫پاکستان‬‎)",
39299         "pk",
39300         "92"
39301       ],
39302       [
39303         "Palau",
39304         "pw",
39305         "680"
39306       ],
39307       [
39308         "Palestine (‫فلسطين‬‎)",
39309         "ps",
39310         "970"
39311       ],
39312       [
39313         "Panama (Panamá)",
39314         "pa",
39315         "507"
39316       ],
39317       [
39318         "Papua New Guinea",
39319         "pg",
39320         "675"
39321       ],
39322       [
39323         "Paraguay",
39324         "py",
39325         "595"
39326       ],
39327       [
39328         "Peru (Perú)",
39329         "pe",
39330         "51"
39331       ],
39332       [
39333         "Philippines",
39334         "ph",
39335         "63"
39336       ],
39337       [
39338         "Poland (Polska)",
39339         "pl",
39340         "48"
39341       ],
39342       [
39343         "Portugal",
39344         "pt",
39345         "351"
39346       ],
39347       [
39348         "Puerto Rico",
39349         "pr",
39350         "1",
39351         3,
39352         ["787", "939"]
39353       ],
39354       [
39355         "Qatar (‫قطر‬‎)",
39356         "qa",
39357         "974"
39358       ],
39359       [
39360         "Réunion (La Réunion)",
39361         "re",
39362         "262",
39363         0
39364       ],
39365       [
39366         "Romania (România)",
39367         "ro",
39368         "40"
39369       ],
39370       [
39371         "Russia (Россия)",
39372         "ru",
39373         "7",
39374         0
39375       ],
39376       [
39377         "Rwanda",
39378         "rw",
39379         "250"
39380       ],
39381       [
39382         "Saint Barthélemy",
39383         "bl",
39384         "590",
39385         1
39386       ],
39387       [
39388         "Saint Helena",
39389         "sh",
39390         "290"
39391       ],
39392       [
39393         "Saint Kitts and Nevis",
39394         "kn",
39395         "1869"
39396       ],
39397       [
39398         "Saint Lucia",
39399         "lc",
39400         "1758"
39401       ],
39402       [
39403         "Saint Martin (Saint-Martin (partie française))",
39404         "mf",
39405         "590",
39406         2
39407       ],
39408       [
39409         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39410         "pm",
39411         "508"
39412       ],
39413       [
39414         "Saint Vincent and the Grenadines",
39415         "vc",
39416         "1784"
39417       ],
39418       [
39419         "Samoa",
39420         "ws",
39421         "685"
39422       ],
39423       [
39424         "San Marino",
39425         "sm",
39426         "378"
39427       ],
39428       [
39429         "São Tomé and Príncipe (São Tomé e Príncipe)",
39430         "st",
39431         "239"
39432       ],
39433       [
39434         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39435         "sa",
39436         "966"
39437       ],
39438       [
39439         "Senegal (Sénégal)",
39440         "sn",
39441         "221"
39442       ],
39443       [
39444         "Serbia (Србија)",
39445         "rs",
39446         "381"
39447       ],
39448       [
39449         "Seychelles",
39450         "sc",
39451         "248"
39452       ],
39453       [
39454         "Sierra Leone",
39455         "sl",
39456         "232"
39457       ],
39458       [
39459         "Singapore",
39460         "sg",
39461         "65"
39462       ],
39463       [
39464         "Sint Maarten",
39465         "sx",
39466         "1721"
39467       ],
39468       [
39469         "Slovakia (Slovensko)",
39470         "sk",
39471         "421"
39472       ],
39473       [
39474         "Slovenia (Slovenija)",
39475         "si",
39476         "386"
39477       ],
39478       [
39479         "Solomon Islands",
39480         "sb",
39481         "677"
39482       ],
39483       [
39484         "Somalia (Soomaaliya)",
39485         "so",
39486         "252"
39487       ],
39488       [
39489         "South Africa",
39490         "za",
39491         "27"
39492       ],
39493       [
39494         "South Korea (대한민국)",
39495         "kr",
39496         "82"
39497       ],
39498       [
39499         "South Sudan (‫جنوب السودان‬‎)",
39500         "ss",
39501         "211"
39502       ],
39503       [
39504         "Spain (España)",
39505         "es",
39506         "34"
39507       ],
39508       [
39509         "Sri Lanka (ශ්‍රී ලංකාව)",
39510         "lk",
39511         "94"
39512       ],
39513       [
39514         "Sudan (‫السودان‬‎)",
39515         "sd",
39516         "249"
39517       ],
39518       [
39519         "Suriname",
39520         "sr",
39521         "597"
39522       ],
39523       [
39524         "Svalbard and Jan Mayen",
39525         "sj",
39526         "47",
39527         1
39528       ],
39529       [
39530         "Swaziland",
39531         "sz",
39532         "268"
39533       ],
39534       [
39535         "Sweden (Sverige)",
39536         "se",
39537         "46"
39538       ],
39539       [
39540         "Switzerland (Schweiz)",
39541         "ch",
39542         "41"
39543       ],
39544       [
39545         "Syria (‫سوريا‬‎)",
39546         "sy",
39547         "963"
39548       ],
39549       [
39550         "Taiwan (台灣)",
39551         "tw",
39552         "886"
39553       ],
39554       [
39555         "Tajikistan",
39556         "tj",
39557         "992"
39558       ],
39559       [
39560         "Tanzania",
39561         "tz",
39562         "255"
39563       ],
39564       [
39565         "Thailand (ไทย)",
39566         "th",
39567         "66"
39568       ],
39569       [
39570         "Timor-Leste",
39571         "tl",
39572         "670"
39573       ],
39574       [
39575         "Togo",
39576         "tg",
39577         "228"
39578       ],
39579       [
39580         "Tokelau",
39581         "tk",
39582         "690"
39583       ],
39584       [
39585         "Tonga",
39586         "to",
39587         "676"
39588       ],
39589       [
39590         "Trinidad and Tobago",
39591         "tt",
39592         "1868"
39593       ],
39594       [
39595         "Tunisia (‫تونس‬‎)",
39596         "tn",
39597         "216"
39598       ],
39599       [
39600         "Turkey (Türkiye)",
39601         "tr",
39602         "90"
39603       ],
39604       [
39605         "Turkmenistan",
39606         "tm",
39607         "993"
39608       ],
39609       [
39610         "Turks and Caicos Islands",
39611         "tc",
39612         "1649"
39613       ],
39614       [
39615         "Tuvalu",
39616         "tv",
39617         "688"
39618       ],
39619       [
39620         "U.S. Virgin Islands",
39621         "vi",
39622         "1340"
39623       ],
39624       [
39625         "Uganda",
39626         "ug",
39627         "256"
39628       ],
39629       [
39630         "Ukraine (Україна)",
39631         "ua",
39632         "380"
39633       ],
39634       [
39635         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39636         "ae",
39637         "971"
39638       ],
39639       [
39640         "United Kingdom",
39641         "gb",
39642         "44",
39643         0
39644       ],
39645       [
39646         "United States",
39647         "us",
39648         "1",
39649         0
39650       ],
39651       [
39652         "Uruguay",
39653         "uy",
39654         "598"
39655       ],
39656       [
39657         "Uzbekistan (Oʻzbekiston)",
39658         "uz",
39659         "998"
39660       ],
39661       [
39662         "Vanuatu",
39663         "vu",
39664         "678"
39665       ],
39666       [
39667         "Vatican City (Città del Vaticano)",
39668         "va",
39669         "39",
39670         1
39671       ],
39672       [
39673         "Venezuela",
39674         "ve",
39675         "58"
39676       ],
39677       [
39678         "Vietnam (Việt Nam)",
39679         "vn",
39680         "84"
39681       ],
39682       [
39683         "Wallis and Futuna (Wallis-et-Futuna)",
39684         "wf",
39685         "681"
39686       ],
39687       [
39688         "Western Sahara (‫الصحراء الغربية‬‎)",
39689         "eh",
39690         "212",
39691         1
39692       ],
39693       [
39694         "Yemen (‫اليمن‬‎)",
39695         "ye",
39696         "967"
39697       ],
39698       [
39699         "Zambia",
39700         "zm",
39701         "260"
39702       ],
39703       [
39704         "Zimbabwe",
39705         "zw",
39706         "263"
39707       ],
39708       [
39709         "Åland Islands",
39710         "ax",
39711         "358",
39712         1
39713       ]
39714   ];
39715   
39716   return d;
39717 }/**
39718 *    This script refer to:
39719 *    Title: International Telephone Input
39720 *    Author: Jack O'Connor
39721 *    Code version:  v12.1.12
39722 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39723 **/
39724
39725 /**
39726  * @class Roo.bootstrap.PhoneInput
39727  * @extends Roo.bootstrap.TriggerField
39728  * An input with International dial-code selection
39729  
39730  * @cfg {String} defaultDialCode default '+852'
39731  * @cfg {Array} preferedCountries default []
39732   
39733  * @constructor
39734  * Create a new PhoneInput.
39735  * @param {Object} config Configuration options
39736  */
39737
39738 Roo.bootstrap.PhoneInput = function(config) {
39739     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39740 };
39741
39742 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39743         
39744         listWidth: undefined,
39745         
39746         selectedClass: 'active',
39747         
39748         invalidClass : "has-warning",
39749         
39750         validClass: 'has-success',
39751         
39752         allowed: '0123456789',
39753         
39754         /**
39755          * @cfg {String} defaultDialCode The default dial code when initializing the input
39756          */
39757         defaultDialCode: '+852',
39758         
39759         /**
39760          * @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
39761          */
39762         preferedCountries: false,
39763         
39764         getAutoCreate : function()
39765         {
39766             var data = Roo.bootstrap.PhoneInputData();
39767             var align = this.labelAlign || this.parentLabelAlign();
39768             var id = Roo.id();
39769             
39770             this.allCountries = [];
39771             this.dialCodeMapping = [];
39772             
39773             for (var i = 0; i < data.length; i++) {
39774               var c = data[i];
39775               this.allCountries[i] = {
39776                 name: c[0],
39777                 iso2: c[1],
39778                 dialCode: c[2],
39779                 priority: c[3] || 0,
39780                 areaCodes: c[4] || null
39781               };
39782               this.dialCodeMapping[c[2]] = {
39783                   name: c[0],
39784                   iso2: c[1],
39785                   priority: c[3] || 0,
39786                   areaCodes: c[4] || null
39787               };
39788             }
39789             
39790             var cfg = {
39791                 cls: 'form-group',
39792                 cn: []
39793             };
39794             
39795             var input =  {
39796                 tag: 'input',
39797                 id : id,
39798                 cls : 'form-control tel-input',
39799                 autocomplete: 'new-password'
39800             };
39801             
39802             var hiddenInput = {
39803                 tag: 'input',
39804                 type: 'hidden',
39805                 cls: 'hidden-tel-input'
39806             };
39807             
39808             if (this.name) {
39809                 hiddenInput.name = this.name;
39810             }
39811             
39812             if (this.disabled) {
39813                 input.disabled = true;
39814             }
39815             
39816             var flag_container = {
39817                 tag: 'div',
39818                 cls: 'flag-box',
39819                 cn: [
39820                     {
39821                         tag: 'div',
39822                         cls: 'flag'
39823                     },
39824                     {
39825                         tag: 'div',
39826                         cls: 'caret'
39827                     }
39828                 ]
39829             };
39830             
39831             var box = {
39832                 tag: 'div',
39833                 cls: this.hasFeedback ? 'has-feedback' : '',
39834                 cn: [
39835                     hiddenInput,
39836                     input,
39837                     {
39838                         tag: 'input',
39839                         cls: 'dial-code-holder',
39840                         disabled: true
39841                     }
39842                 ]
39843             };
39844             
39845             var container = {
39846                 cls: 'roo-select2-container input-group',
39847                 cn: [
39848                     flag_container,
39849                     box
39850                 ]
39851             };
39852             
39853             if (this.fieldLabel.length) {
39854                 var indicator = {
39855                     tag: 'i',
39856                     tooltip: 'This field is required'
39857                 };
39858                 
39859                 var label = {
39860                     tag: 'label',
39861                     'for':  id,
39862                     cls: 'control-label',
39863                     cn: []
39864                 };
39865                 
39866                 var label_text = {
39867                     tag: 'span',
39868                     html: this.fieldLabel
39869                 };
39870                 
39871                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39872                 label.cn = [
39873                     indicator,
39874                     label_text
39875                 ];
39876                 
39877                 if(this.indicatorpos == 'right') {
39878                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39879                     label.cn = [
39880                         label_text,
39881                         indicator
39882                     ];
39883                 }
39884                 
39885                 if(align == 'left') {
39886                     container = {
39887                         tag: 'div',
39888                         cn: [
39889                             container
39890                         ]
39891                     };
39892                     
39893                     if(this.labelWidth > 12){
39894                         label.style = "width: " + this.labelWidth + 'px';
39895                     }
39896                     if(this.labelWidth < 13 && this.labelmd == 0){
39897                         this.labelmd = this.labelWidth;
39898                     }
39899                     if(this.labellg > 0){
39900                         label.cls += ' col-lg-' + this.labellg;
39901                         input.cls += ' col-lg-' + (12 - this.labellg);
39902                     }
39903                     if(this.labelmd > 0){
39904                         label.cls += ' col-md-' + this.labelmd;
39905                         container.cls += ' col-md-' + (12 - this.labelmd);
39906                     }
39907                     if(this.labelsm > 0){
39908                         label.cls += ' col-sm-' + this.labelsm;
39909                         container.cls += ' col-sm-' + (12 - this.labelsm);
39910                     }
39911                     if(this.labelxs > 0){
39912                         label.cls += ' col-xs-' + this.labelxs;
39913                         container.cls += ' col-xs-' + (12 - this.labelxs);
39914                     }
39915                 }
39916             }
39917             
39918             cfg.cn = [
39919                 label,
39920                 container
39921             ];
39922             
39923             var settings = this;
39924             
39925             ['xs','sm','md','lg'].map(function(size){
39926                 if (settings[size]) {
39927                     cfg.cls += ' col-' + size + '-' + settings[size];
39928                 }
39929             });
39930             
39931             this.store = new Roo.data.Store({
39932                 proxy : new Roo.data.MemoryProxy({}),
39933                 reader : new Roo.data.JsonReader({
39934                     fields : [
39935                         {
39936                             'name' : 'name',
39937                             'type' : 'string'
39938                         },
39939                         {
39940                             'name' : 'iso2',
39941                             'type' : 'string'
39942                         },
39943                         {
39944                             'name' : 'dialCode',
39945                             'type' : 'string'
39946                         },
39947                         {
39948                             'name' : 'priority',
39949                             'type' : 'string'
39950                         },
39951                         {
39952                             'name' : 'areaCodes',
39953                             'type' : 'string'
39954                         }
39955                     ]
39956                 })
39957             });
39958             
39959             if(!this.preferedCountries) {
39960                 this.preferedCountries = [
39961                     'hk',
39962                     'gb',
39963                     'us'
39964                 ];
39965             }
39966             
39967             var p = this.preferedCountries.reverse();
39968             
39969             if(p) {
39970                 for (var i = 0; i < p.length; i++) {
39971                     for (var j = 0; j < this.allCountries.length; j++) {
39972                         if(this.allCountries[j].iso2 == p[i]) {
39973                             var t = this.allCountries[j];
39974                             this.allCountries.splice(j,1);
39975                             this.allCountries.unshift(t);
39976                         }
39977                     } 
39978                 }
39979             }
39980             
39981             this.store.proxy.data = {
39982                 success: true,
39983                 data: this.allCountries
39984             };
39985             
39986             return cfg;
39987         },
39988         
39989         initEvents : function()
39990         {
39991             this.createList();
39992             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39993             
39994             this.indicator = this.indicatorEl();
39995             this.flag = this.flagEl();
39996             this.dialCodeHolder = this.dialCodeHolderEl();
39997             
39998             this.trigger = this.el.select('div.flag-box',true).first();
39999             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40000             
40001             var _this = this;
40002             
40003             (function(){
40004                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40005                 _this.list.setWidth(lw);
40006             }).defer(100);
40007             
40008             this.list.on('mouseover', this.onViewOver, this);
40009             this.list.on('mousemove', this.onViewMove, this);
40010             this.inputEl().on("keyup", this.onKeyUp, this);
40011             
40012             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40013
40014             this.view = new Roo.View(this.list, this.tpl, {
40015                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40016             });
40017             
40018             this.view.on('click', this.onViewClick, this);
40019             this.setValue(this.defaultDialCode);
40020         },
40021         
40022         onTriggerClick : function(e)
40023         {
40024             Roo.log('trigger click');
40025             if(this.disabled){
40026                 return;
40027             }
40028             
40029             if(this.isExpanded()){
40030                 this.collapse();
40031                 this.hasFocus = false;
40032             }else {
40033                 this.store.load({});
40034                 this.hasFocus = true;
40035                 this.expand();
40036             }
40037         },
40038         
40039         isExpanded : function()
40040         {
40041             return this.list.isVisible();
40042         },
40043         
40044         collapse : function()
40045         {
40046             if(!this.isExpanded()){
40047                 return;
40048             }
40049             this.list.hide();
40050             Roo.get(document).un('mousedown', this.collapseIf, this);
40051             Roo.get(document).un('mousewheel', this.collapseIf, this);
40052             this.fireEvent('collapse', this);
40053             this.validate();
40054         },
40055         
40056         expand : function()
40057         {
40058             Roo.log('expand');
40059
40060             if(this.isExpanded() || !this.hasFocus){
40061                 return;
40062             }
40063             
40064             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40065             this.list.setWidth(lw);
40066             
40067             this.list.show();
40068             this.restrictHeight();
40069             
40070             Roo.get(document).on('mousedown', this.collapseIf, this);
40071             Roo.get(document).on('mousewheel', this.collapseIf, this);
40072             
40073             this.fireEvent('expand', this);
40074         },
40075         
40076         restrictHeight : function()
40077         {
40078             this.list.alignTo(this.inputEl(), this.listAlign);
40079             this.list.alignTo(this.inputEl(), this.listAlign);
40080         },
40081         
40082         onViewOver : function(e, t)
40083         {
40084             if(this.inKeyMode){
40085                 return;
40086             }
40087             var item = this.view.findItemFromChild(t);
40088             
40089             if(item){
40090                 var index = this.view.indexOf(item);
40091                 this.select(index, false);
40092             }
40093         },
40094
40095         // private
40096         onViewClick : function(view, doFocus, el, e)
40097         {
40098             var index = this.view.getSelectedIndexes()[0];
40099             
40100             var r = this.store.getAt(index);
40101             
40102             if(r){
40103                 this.onSelect(r, index);
40104             }
40105             if(doFocus !== false && !this.blockFocus){
40106                 this.inputEl().focus();
40107             }
40108         },
40109         
40110         onViewMove : function(e, t)
40111         {
40112             this.inKeyMode = false;
40113         },
40114         
40115         select : function(index, scrollIntoView)
40116         {
40117             this.selectedIndex = index;
40118             this.view.select(index);
40119             if(scrollIntoView !== false){
40120                 var el = this.view.getNode(index);
40121                 if(el){
40122                     this.list.scrollChildIntoView(el, false);
40123                 }
40124             }
40125         },
40126         
40127         createList : function()
40128         {
40129             this.list = Roo.get(document.body).createChild({
40130                 tag: 'ul',
40131                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40132                 style: 'display:none'
40133             });
40134             
40135             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40136         },
40137         
40138         collapseIf : function(e)
40139         {
40140             var in_combo  = e.within(this.el);
40141             var in_list =  e.within(this.list);
40142             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40143             
40144             if (in_combo || in_list || is_list) {
40145                 return;
40146             }
40147             this.collapse();
40148         },
40149         
40150         onSelect : function(record, index)
40151         {
40152             if(this.fireEvent('beforeselect', this, record, index) !== false){
40153                 
40154                 this.setFlagClass(record.data.iso2);
40155                 this.setDialCode(record.data.dialCode);
40156                 this.hasFocus = false;
40157                 this.collapse();
40158                 this.fireEvent('select', this, record, index);
40159             }
40160         },
40161         
40162         flagEl : function()
40163         {
40164             var flag = this.el.select('div.flag',true).first();
40165             if(!flag){
40166                 return false;
40167             }
40168             return flag;
40169         },
40170         
40171         dialCodeHolderEl : function()
40172         {
40173             var d = this.el.select('input.dial-code-holder',true).first();
40174             if(!d){
40175                 return false;
40176             }
40177             return d;
40178         },
40179         
40180         setDialCode : function(v)
40181         {
40182             this.dialCodeHolder.dom.value = '+'+v;
40183         },
40184         
40185         setFlagClass : function(n)
40186         {
40187             this.flag.dom.className = 'flag '+n;
40188         },
40189         
40190         getValue : function()
40191         {
40192             var v = this.inputEl().getValue();
40193             if(this.dialCodeHolder) {
40194                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40195             }
40196             return v;
40197         },
40198         
40199         setValue : function(v)
40200         {
40201             var d = this.getDialCode(v);
40202             
40203             //invalid dial code
40204             if(v.length == 0 || !d || d.length == 0) {
40205                 if(this.rendered){
40206                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40207                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40208                 }
40209                 return;
40210             }
40211             
40212             //valid dial code
40213             this.setFlagClass(this.dialCodeMapping[d].iso2);
40214             this.setDialCode(d);
40215             this.inputEl().dom.value = v.replace('+'+d,'');
40216             this.hiddenEl().dom.value = this.getValue();
40217             
40218             this.validate();
40219         },
40220         
40221         getDialCode : function(v)
40222         {
40223             v = v ||  '';
40224             
40225             if (v.length == 0) {
40226                 return this.dialCodeHolder.dom.value;
40227             }
40228             
40229             var dialCode = "";
40230             if (v.charAt(0) != "+") {
40231                 return false;
40232             }
40233             var numericChars = "";
40234             for (var i = 1; i < v.length; i++) {
40235               var c = v.charAt(i);
40236               if (!isNaN(c)) {
40237                 numericChars += c;
40238                 if (this.dialCodeMapping[numericChars]) {
40239                   dialCode = v.substr(1, i);
40240                 }
40241                 if (numericChars.length == 4) {
40242                   break;
40243                 }
40244               }
40245             }
40246             return dialCode;
40247         },
40248         
40249         reset : function()
40250         {
40251             this.setValue(this.defaultDialCode);
40252             this.validate();
40253         },
40254         
40255         hiddenEl : function()
40256         {
40257             return this.el.select('input.hidden-tel-input',true).first();
40258         },
40259         
40260         onKeyUp : function(e){
40261             
40262             var k = e.getKey();
40263             var c = e.getCharCode();
40264             
40265             if(
40266                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40267                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40268             ){
40269                 e.stopEvent();
40270             }
40271             
40272             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40273             //     return;
40274             // }
40275             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40276                 e.stopEvent();
40277             }
40278             
40279             this.setValue(this.getValue());
40280         }
40281         
40282 });
40283 /**
40284  * @class Roo.bootstrap.MoneyField
40285  * @extends Roo.bootstrap.ComboBox
40286  * Bootstrap MoneyField class
40287  * 
40288  * @constructor
40289  * Create a new MoneyField.
40290  * @param {Object} config Configuration options
40291  */
40292
40293 Roo.bootstrap.MoneyField = function(config) {
40294     
40295     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40296     
40297 };
40298
40299 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40300     
40301     /**
40302      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40303      */
40304     allowDecimals : true,
40305     /**
40306      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40307      */
40308     decimalSeparator : ".",
40309     /**
40310      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40311      */
40312     decimalPrecision : 0,
40313     /**
40314      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40315      */
40316     allowNegative : true,
40317     /**
40318      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40319      */
40320     allowZero: true,
40321     /**
40322      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40323      */
40324     minValue : Number.NEGATIVE_INFINITY,
40325     /**
40326      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40327      */
40328     maxValue : Number.MAX_VALUE,
40329     /**
40330      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40331      */
40332     minText : "The minimum value for this field is {0}",
40333     /**
40334      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40335      */
40336     maxText : "The maximum value for this field is {0}",
40337     /**
40338      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40339      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40340      */
40341     nanText : "{0} is not a valid number",
40342     /**
40343      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40344      */
40345     castInt : true,
40346     /**
40347      * @cfg {String} defaults currency of the MoneyField
40348      * value should be in lkey
40349      */
40350     defaultCurrency : false,
40351     /**
40352      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40353      */
40354     thousandsDelimiter : false,
40355     
40356     
40357     inputlg : 9,
40358     inputmd : 9,
40359     inputsm : 9,
40360     inputxs : 6,
40361     
40362     store : false,
40363     
40364     getAutoCreate : function()
40365     {
40366         var align = this.labelAlign || this.parentLabelAlign();
40367         
40368         var id = Roo.id();
40369
40370         var cfg = {
40371             cls: 'form-group',
40372             cn: []
40373         };
40374
40375         var input =  {
40376             tag: 'input',
40377             id : id,
40378             cls : 'form-control roo-money-amount-input',
40379             autocomplete: 'new-password'
40380         };
40381         
40382         var hiddenInput = {
40383             tag: 'input',
40384             type: 'hidden',
40385             id: Roo.id(),
40386             cls: 'hidden-number-input'
40387         };
40388         
40389         if (this.name) {
40390             hiddenInput.name = this.name;
40391         }
40392
40393         if (this.disabled) {
40394             input.disabled = true;
40395         }
40396
40397         var clg = 12 - this.inputlg;
40398         var cmd = 12 - this.inputmd;
40399         var csm = 12 - this.inputsm;
40400         var cxs = 12 - this.inputxs;
40401         
40402         var container = {
40403             tag : 'div',
40404             cls : 'row roo-money-field',
40405             cn : [
40406                 {
40407                     tag : 'div',
40408                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40409                     cn : [
40410                         {
40411                             tag : 'div',
40412                             cls: 'roo-select2-container input-group',
40413                             cn: [
40414                                 {
40415                                     tag : 'input',
40416                                     cls : 'form-control roo-money-currency-input',
40417                                     autocomplete: 'new-password',
40418                                     readOnly : 1,
40419                                     name : this.currencyName
40420                                 },
40421                                 {
40422                                     tag :'span',
40423                                     cls : 'input-group-addon',
40424                                     cn : [
40425                                         {
40426                                             tag: 'span',
40427                                             cls: 'caret'
40428                                         }
40429                                     ]
40430                                 }
40431                             ]
40432                         }
40433                     ]
40434                 },
40435                 {
40436                     tag : 'div',
40437                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40438                     cn : [
40439                         {
40440                             tag: 'div',
40441                             cls: this.hasFeedback ? 'has-feedback' : '',
40442                             cn: [
40443                                 input
40444                             ]
40445                         }
40446                     ]
40447                 }
40448             ]
40449             
40450         };
40451         
40452         if (this.fieldLabel.length) {
40453             var indicator = {
40454                 tag: 'i',
40455                 tooltip: 'This field is required'
40456             };
40457
40458             var label = {
40459                 tag: 'label',
40460                 'for':  id,
40461                 cls: 'control-label',
40462                 cn: []
40463             };
40464
40465             var label_text = {
40466                 tag: 'span',
40467                 html: this.fieldLabel
40468             };
40469
40470             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40471             label.cn = [
40472                 indicator,
40473                 label_text
40474             ];
40475
40476             if(this.indicatorpos == 'right') {
40477                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40478                 label.cn = [
40479                     label_text,
40480                     indicator
40481                 ];
40482             }
40483
40484             if(align == 'left') {
40485                 container = {
40486                     tag: 'div',
40487                     cn: [
40488                         container
40489                     ]
40490                 };
40491
40492                 if(this.labelWidth > 12){
40493                     label.style = "width: " + this.labelWidth + 'px';
40494                 }
40495                 if(this.labelWidth < 13 && this.labelmd == 0){
40496                     this.labelmd = this.labelWidth;
40497                 }
40498                 if(this.labellg > 0){
40499                     label.cls += ' col-lg-' + this.labellg;
40500                     input.cls += ' col-lg-' + (12 - this.labellg);
40501                 }
40502                 if(this.labelmd > 0){
40503                     label.cls += ' col-md-' + this.labelmd;
40504                     container.cls += ' col-md-' + (12 - this.labelmd);
40505                 }
40506                 if(this.labelsm > 0){
40507                     label.cls += ' col-sm-' + this.labelsm;
40508                     container.cls += ' col-sm-' + (12 - this.labelsm);
40509                 }
40510                 if(this.labelxs > 0){
40511                     label.cls += ' col-xs-' + this.labelxs;
40512                     container.cls += ' col-xs-' + (12 - this.labelxs);
40513                 }
40514             }
40515         }
40516
40517         cfg.cn = [
40518             label,
40519             container,
40520             hiddenInput
40521         ];
40522         
40523         var settings = this;
40524
40525         ['xs','sm','md','lg'].map(function(size){
40526             if (settings[size]) {
40527                 cfg.cls += ' col-' + size + '-' + settings[size];
40528             }
40529         });
40530         
40531         return cfg;
40532     },
40533     
40534     initEvents : function()
40535     {
40536         this.indicator = this.indicatorEl();
40537         
40538         this.initCurrencyEvent();
40539         
40540         this.initNumberEvent();
40541     },
40542     
40543     initCurrencyEvent : function()
40544     {
40545         if (!this.store) {
40546             throw "can not find store for combo";
40547         }
40548         
40549         this.store = Roo.factory(this.store, Roo.data);
40550         this.store.parent = this;
40551         
40552         this.createList();
40553         
40554         this.triggerEl = this.el.select('.input-group-addon', true).first();
40555         
40556         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40557         
40558         var _this = this;
40559         
40560         (function(){
40561             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40562             _this.list.setWidth(lw);
40563         }).defer(100);
40564         
40565         this.list.on('mouseover', this.onViewOver, this);
40566         this.list.on('mousemove', this.onViewMove, this);
40567         this.list.on('scroll', this.onViewScroll, this);
40568         
40569         if(!this.tpl){
40570             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40571         }
40572         
40573         this.view = new Roo.View(this.list, this.tpl, {
40574             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40575         });
40576         
40577         this.view.on('click', this.onViewClick, this);
40578         
40579         this.store.on('beforeload', this.onBeforeLoad, this);
40580         this.store.on('load', this.onLoad, this);
40581         this.store.on('loadexception', this.onLoadException, this);
40582         
40583         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40584             "up" : function(e){
40585                 this.inKeyMode = true;
40586                 this.selectPrev();
40587             },
40588
40589             "down" : function(e){
40590                 if(!this.isExpanded()){
40591                     this.onTriggerClick();
40592                 }else{
40593                     this.inKeyMode = true;
40594                     this.selectNext();
40595                 }
40596             },
40597
40598             "enter" : function(e){
40599                 this.collapse();
40600                 
40601                 if(this.fireEvent("specialkey", this, e)){
40602                     this.onViewClick(false);
40603                 }
40604                 
40605                 return true;
40606             },
40607
40608             "esc" : function(e){
40609                 this.collapse();
40610             },
40611
40612             "tab" : function(e){
40613                 this.collapse();
40614                 
40615                 if(this.fireEvent("specialkey", this, e)){
40616                     this.onViewClick(false);
40617                 }
40618                 
40619                 return true;
40620             },
40621
40622             scope : this,
40623
40624             doRelay : function(foo, bar, hname){
40625                 if(hname == 'down' || this.scope.isExpanded()){
40626                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40627                 }
40628                 return true;
40629             },
40630
40631             forceKeyDown: true
40632         });
40633         
40634         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40635         
40636     },
40637     
40638     initNumberEvent : function(e)
40639     {
40640         this.inputEl().on("keydown" , this.fireKey,  this);
40641         this.inputEl().on("focus", this.onFocus,  this);
40642         this.inputEl().on("blur", this.onBlur,  this);
40643         
40644         this.inputEl().relayEvent('keyup', this);
40645         
40646         if(this.indicator){
40647             this.indicator.addClass('invisible');
40648         }
40649  
40650         this.originalValue = this.getValue();
40651         
40652         if(this.validationEvent == 'keyup'){
40653             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40654             this.inputEl().on('keyup', this.filterValidation, this);
40655         }
40656         else if(this.validationEvent !== false){
40657             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40658         }
40659         
40660         if(this.selectOnFocus){
40661             this.on("focus", this.preFocus, this);
40662             
40663         }
40664         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40665             this.inputEl().on("keypress", this.filterKeys, this);
40666         } else {
40667             this.inputEl().relayEvent('keypress', this);
40668         }
40669         
40670         var allowed = "0123456789";
40671         
40672         if(this.allowDecimals){
40673             allowed += this.decimalSeparator;
40674         }
40675         
40676         if(this.allowNegative){
40677             allowed += "-";
40678         }
40679         
40680         if(this.thousandsDelimiter) {
40681             allowed += ",";
40682         }
40683         
40684         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40685         
40686         var keyPress = function(e){
40687             
40688             var k = e.getKey();
40689             
40690             var c = e.getCharCode();
40691             
40692             if(
40693                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40694                     allowed.indexOf(String.fromCharCode(c)) === -1
40695             ){
40696                 e.stopEvent();
40697                 return;
40698             }
40699             
40700             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40701                 return;
40702             }
40703             
40704             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40705                 e.stopEvent();
40706             }
40707         };
40708         
40709         this.inputEl().on("keypress", keyPress, this);
40710         
40711     },
40712     
40713     onTriggerClick : function(e)
40714     {   
40715         if(this.disabled){
40716             return;
40717         }
40718         
40719         this.page = 0;
40720         this.loadNext = false;
40721         
40722         if(this.isExpanded()){
40723             this.collapse();
40724             return;
40725         }
40726         
40727         this.hasFocus = true;
40728         
40729         if(this.triggerAction == 'all') {
40730             this.doQuery(this.allQuery, true);
40731             return;
40732         }
40733         
40734         this.doQuery(this.getRawValue());
40735     },
40736     
40737     getCurrency : function()
40738     {   
40739         var v = this.currencyEl().getValue();
40740         
40741         return v;
40742     },
40743     
40744     restrictHeight : function()
40745     {
40746         this.list.alignTo(this.currencyEl(), this.listAlign);
40747         this.list.alignTo(this.currencyEl(), this.listAlign);
40748     },
40749     
40750     onViewClick : function(view, doFocus, el, e)
40751     {
40752         var index = this.view.getSelectedIndexes()[0];
40753         
40754         var r = this.store.getAt(index);
40755         
40756         if(r){
40757             this.onSelect(r, index);
40758         }
40759     },
40760     
40761     onSelect : function(record, index){
40762         
40763         if(this.fireEvent('beforeselect', this, record, index) !== false){
40764         
40765             this.setFromCurrencyData(index > -1 ? record.data : false);
40766             
40767             this.collapse();
40768             
40769             this.fireEvent('select', this, record, index);
40770         }
40771     },
40772     
40773     setFromCurrencyData : function(o)
40774     {
40775         var currency = '';
40776         
40777         this.lastCurrency = o;
40778         
40779         if (this.currencyField) {
40780             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40781         } else {
40782             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40783         }
40784         
40785         this.lastSelectionText = currency;
40786         
40787         //setting default currency
40788         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40789             this.setCurrency(this.defaultCurrency);
40790             return;
40791         }
40792         
40793         this.setCurrency(currency);
40794     },
40795     
40796     setFromData : function(o)
40797     {
40798         var c = {};
40799         
40800         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40801         
40802         this.setFromCurrencyData(c);
40803         
40804         var value = '';
40805         
40806         if (this.name) {
40807             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40808         } else {
40809             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40810         }
40811         
40812         this.setValue(value);
40813         
40814     },
40815     
40816     setCurrency : function(v)
40817     {   
40818         this.currencyValue = v;
40819         
40820         if(this.rendered){
40821             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40822             this.validate();
40823         }
40824     },
40825     
40826     setValue : function(v)
40827     {
40828         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40829         
40830         this.value = v;
40831         
40832         if(this.rendered){
40833             
40834             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40835             
40836             this.inputEl().dom.value = (v == '') ? '' :
40837                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40838             
40839             if(!this.allowZero && v === '0') {
40840                 this.hiddenEl().dom.value = '';
40841                 this.inputEl().dom.value = '';
40842             }
40843             
40844             this.validate();
40845         }
40846     },
40847     
40848     getRawValue : function()
40849     {
40850         var v = this.inputEl().getValue();
40851         
40852         return v;
40853     },
40854     
40855     getValue : function()
40856     {
40857         return this.fixPrecision(this.parseValue(this.getRawValue()));
40858     },
40859     
40860     parseValue : function(value)
40861     {
40862         if(this.thousandsDelimiter) {
40863             value += "";
40864             r = new RegExp(",", "g");
40865             value = value.replace(r, "");
40866         }
40867         
40868         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40869         return isNaN(value) ? '' : value;
40870         
40871     },
40872     
40873     fixPrecision : function(value)
40874     {
40875         if(this.thousandsDelimiter) {
40876             value += "";
40877             r = new RegExp(",", "g");
40878             value = value.replace(r, "");
40879         }
40880         
40881         var nan = isNaN(value);
40882         
40883         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40884             return nan ? '' : value;
40885         }
40886         return parseFloat(value).toFixed(this.decimalPrecision);
40887     },
40888     
40889     decimalPrecisionFcn : function(v)
40890     {
40891         return Math.floor(v);
40892     },
40893     
40894     validateValue : function(value)
40895     {
40896         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40897             return false;
40898         }
40899         
40900         var num = this.parseValue(value);
40901         
40902         if(isNaN(num)){
40903             this.markInvalid(String.format(this.nanText, value));
40904             return false;
40905         }
40906         
40907         if(num < this.minValue){
40908             this.markInvalid(String.format(this.minText, this.minValue));
40909             return false;
40910         }
40911         
40912         if(num > this.maxValue){
40913             this.markInvalid(String.format(this.maxText, this.maxValue));
40914             return false;
40915         }
40916         
40917         return true;
40918     },
40919     
40920     validate : function()
40921     {
40922         if(this.disabled || this.allowBlank){
40923             this.markValid();
40924             return true;
40925         }
40926         
40927         var currency = this.getCurrency();
40928         
40929         if(this.validateValue(this.getRawValue()) && currency.length){
40930             this.markValid();
40931             return true;
40932         }
40933         
40934         this.markInvalid();
40935         return false;
40936     },
40937     
40938     getName: function()
40939     {
40940         return this.name;
40941     },
40942     
40943     beforeBlur : function()
40944     {
40945         if(!this.castInt){
40946             return;
40947         }
40948         
40949         var v = this.parseValue(this.getRawValue());
40950         
40951         if(v || v == 0){
40952             this.setValue(v);
40953         }
40954     },
40955     
40956     onBlur : function()
40957     {
40958         this.beforeBlur();
40959         
40960         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40961             //this.el.removeClass(this.focusClass);
40962         }
40963         
40964         this.hasFocus = false;
40965         
40966         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40967             this.validate();
40968         }
40969         
40970         var v = this.getValue();
40971         
40972         if(String(v) !== String(this.startValue)){
40973             this.fireEvent('change', this, v, this.startValue);
40974         }
40975         
40976         this.fireEvent("blur", this);
40977     },
40978     
40979     inputEl : function()
40980     {
40981         return this.el.select('.roo-money-amount-input', true).first();
40982     },
40983     
40984     currencyEl : function()
40985     {
40986         return this.el.select('.roo-money-currency-input', true).first();
40987     },
40988     
40989     hiddenEl : function()
40990     {
40991         return this.el.select('input.hidden-number-input',true).first();
40992     }
40993     
40994 });