d36ef0aeb842016235441f8c15adf7c360d2506f
[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
2650     onRender : function(ct, position)
2651     {
2652         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2653
2654         if(!this.el){
2655             var cfg = Roo.apply({},  this.getAutoCreate());
2656             cfg.id = Roo.id();
2657             //if(!cfg.name){
2658             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2659             //}
2660             //if (!cfg.name.length) {
2661             //    delete cfg.name;
2662            // }
2663             if (this.cls) {
2664                 cfg.cls += ' ' + this.cls;
2665             }
2666             if (this.style) {
2667                 cfg.style = this.style;
2668             }
2669             this.el = Roo.get(document.body).createChild(cfg, position);
2670         }
2671         //var type = this.el.dom.type;
2672
2673
2674         if(this.tabIndex !== undefined){
2675             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2676         }
2677
2678         this.dialogEl = this.el.select('.modal-dialog',true).first();
2679         this.bodyEl = this.el.select('.modal-body',true).first();
2680         this.closeEl = this.el.select('.modal-header .close', true).first();
2681         this.headerEl = this.el.select('.modal-header',true).first();
2682         this.titleEl = this.el.select('.modal-title',true).first();
2683         this.footerEl = this.el.select('.modal-footer',true).first();
2684
2685         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2686         
2687         //this.el.addClass("x-dlg-modal");
2688
2689         if (this.buttons.length) {
2690             Roo.each(this.buttons, function(bb) {
2691                 var b = Roo.apply({}, bb);
2692                 b.xns = b.xns || Roo.bootstrap;
2693                 b.xtype = b.xtype || 'Button';
2694                 if (typeof(b.listeners) == 'undefined') {
2695                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2696                 }
2697
2698                 var btn = Roo.factory(b);
2699
2700                 btn.render(this.el.select('.modal-footer div').first());
2701
2702             },this);
2703         }
2704         // render the children.
2705         var nitems = [];
2706
2707         if(typeof(this.items) != 'undefined'){
2708             var items = this.items;
2709             delete this.items;
2710
2711             for(var i =0;i < items.length;i++) {
2712                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2713             }
2714         }
2715
2716         this.items = nitems;
2717
2718         // where are these used - they used to be body/close/footer
2719
2720
2721         this.initEvents();
2722         //this.el.addClass([this.fieldClass, this.cls]);
2723
2724     },
2725
2726     getAutoCreate : function()
2727     {
2728         var bdy = {
2729                 cls : 'modal-body',
2730                 html : this.html || ''
2731         };
2732
2733         var title = {
2734             tag: 'h4',
2735             cls : 'modal-title',
2736             html : this.title
2737         };
2738
2739         if(this.specificTitle){
2740             title = this.title;
2741
2742         };
2743
2744         var header = [];
2745         if (this.allow_close) {
2746             header.push({
2747                 tag: 'button',
2748                 cls : 'close',
2749                 html : '&times'
2750             });
2751         }
2752
2753         header.push(title);
2754
2755         var size = '';
2756
2757         if(this.size.length){
2758             size = 'modal-' + this.size;
2759         }
2760
2761         var modal = {
2762             cls: "modal",
2763              cn : [
2764                 {
2765                     cls: "modal-dialog " + size,
2766                     cn : [
2767                         {
2768                             cls : "modal-content",
2769                             cn : [
2770                                 {
2771                                     cls : 'modal-header',
2772                                     cn : header
2773                                 },
2774                                 bdy,
2775                                 {
2776                                     cls : 'modal-footer',
2777                                     cn : [
2778                                         {
2779                                             tag: 'div',
2780                                             cls: 'btn-' + this.buttonPosition
2781                                         }
2782                                     ]
2783
2784                                 }
2785
2786
2787                             ]
2788
2789                         }
2790                     ]
2791
2792                 }
2793             ]
2794         };
2795
2796         if(this.animate){
2797             modal.cls += ' fade';
2798         }
2799
2800         return modal;
2801
2802     },
2803     getChildContainer : function() {
2804
2805          return this.bodyEl;
2806
2807     },
2808     getButtonContainer : function() {
2809          return this.el.select('.modal-footer div',true).first();
2810
2811     },
2812     initEvents : function()
2813     {
2814         if (this.allow_close) {
2815             this.closeEl.on('click', this.hide, this);
2816         }
2817         Roo.EventManager.onWindowResize(this.resize, this, true);
2818
2819
2820     },
2821
2822     resize : function()
2823     {
2824         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2825         
2826         if (this.fitwindow) {
2827             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2828             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2829             this.setSize(w,h);
2830         }
2831         
2832         if(!this.fitwindow && this.max_width !== 0){
2833             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2834             // var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2835             this.setSize(w,this.height);
2836             Roo.log(this.bodyEl.getHeight());
2837             // var view_height = Roo.lib.Dom.getViewportHeight(true) - 60;
2838             // 
2839             // if(
2840             //     (
2841             //         this.headerEl.getHeight() + 
2842             //         this.bodyEl.getHeight() + 
2843             //         this.footerEl.getHeight()
2844             //     ) > view_height) {
2845             // } {
2846             //     this.setSize(w,view_height);
2847             // }
2848         }
2849         
2850     },
2851
2852     setSize : function(w,h)
2853     {
2854         if (!w && !h) {
2855             return;
2856         }
2857         this.resizeTo(w,h);
2858     },
2859
2860     show : function() {
2861
2862         if (!this.rendered) {
2863             this.render();
2864         }
2865
2866         //this.el.setStyle('display', 'block');
2867         this.el.removeClass('hideing');        
2868         this.el.addClass('show');
2869  
2870         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2871             var _this = this;
2872             (function(){
2873                 this.el.addClass('in');
2874             }).defer(50, this);
2875         }else{
2876             this.el.addClass('in');
2877         }
2878
2879         // not sure how we can show data in here..
2880         //if (this.tmpl) {
2881         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2882         //}
2883
2884         Roo.get(document.body).addClass("x-body-masked");
2885         
2886         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2887         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2888         this.maskEl.addClass('show');
2889         
2890         this.resize();
2891         
2892         this.fireEvent('show', this);
2893
2894         // set zindex here - otherwise it appears to be ignored...
2895         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2896
2897         (function () {
2898             this.items.forEach( function(e) {
2899                 e.layout ? e.layout() : false;
2900
2901             });
2902         }).defer(100,this);
2903
2904     },
2905     hide : function()
2906     {
2907         if(this.fireEvent("beforehide", this) !== false){
2908             this.maskEl.removeClass('show');
2909             Roo.get(document.body).removeClass("x-body-masked");
2910             this.el.removeClass('in');
2911             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2912
2913             if(this.animate){ // why
2914                 this.el.addClass('hideing');
2915                 (function(){
2916                     if (!this.el.hasClass('hideing')) {
2917                         return; // it's been shown again...
2918                     }
2919                     this.el.removeClass('show');
2920                     this.el.removeClass('hideing');
2921                 }).defer(150,this);
2922                 
2923             }else{
2924                  this.el.removeClass('show');
2925             }
2926             this.fireEvent('hide', this);
2927         }
2928     },
2929     isVisible : function()
2930     {
2931         
2932         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2933         
2934     },
2935
2936     addButton : function(str, cb)
2937     {
2938
2939
2940         var b = Roo.apply({}, { html : str } );
2941         b.xns = b.xns || Roo.bootstrap;
2942         b.xtype = b.xtype || 'Button';
2943         if (typeof(b.listeners) == 'undefined') {
2944             b.listeners = { click : cb.createDelegate(this)  };
2945         }
2946
2947         var btn = Roo.factory(b);
2948
2949         btn.render(this.el.select('.modal-footer div').first());
2950
2951         return btn;
2952
2953     },
2954
2955     setDefaultButton : function(btn)
2956     {
2957         //this.el.select('.modal-footer').()
2958     },
2959     diff : false,
2960
2961     resizeTo: function(w,h)
2962     {
2963         // skip.. ?? why??
2964
2965         this.dialogEl.setWidth(w);
2966         if (this.diff === false) {
2967             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2968         }
2969
2970         this.bodyEl.setHeight(h-this.diff);
2971
2972         this.fireEvent('resize', this);
2973
2974     },
2975     setContentSize  : function(w, h)
2976     {
2977
2978     },
2979     onButtonClick: function(btn,e)
2980     {
2981         //Roo.log([a,b,c]);
2982         this.fireEvent('btnclick', btn.name, e);
2983     },
2984      /**
2985      * Set the title of the Dialog
2986      * @param {String} str new Title
2987      */
2988     setTitle: function(str) {
2989         this.titleEl.dom.innerHTML = str;
2990     },
2991     /**
2992      * Set the body of the Dialog
2993      * @param {String} str new Title
2994      */
2995     setBody: function(str) {
2996         this.bodyEl.dom.innerHTML = str;
2997     },
2998     /**
2999      * Set the body of the Dialog using the template
3000      * @param {Obj} data - apply this data to the template and replace the body contents.
3001      */
3002     applyBody: function(obj)
3003     {
3004         if (!this.tmpl) {
3005             Roo.log("Error - using apply Body without a template");
3006             //code
3007         }
3008         this.tmpl.overwrite(this.bodyEl, obj);
3009     }
3010
3011 });
3012
3013
3014 Roo.apply(Roo.bootstrap.Modal,  {
3015     /**
3016          * Button config that displays a single OK button
3017          * @type Object
3018          */
3019         OK :  [{
3020             name : 'ok',
3021             weight : 'primary',
3022             html : 'OK'
3023         }],
3024         /**
3025          * Button config that displays Yes and No buttons
3026          * @type Object
3027          */
3028         YESNO : [
3029             {
3030                 name  : 'no',
3031                 html : 'No'
3032             },
3033             {
3034                 name  :'yes',
3035                 weight : 'primary',
3036                 html : 'Yes'
3037             }
3038         ],
3039
3040         /**
3041          * Button config that displays OK and Cancel buttons
3042          * @type Object
3043          */
3044         OKCANCEL : [
3045             {
3046                name : 'cancel',
3047                 html : 'Cancel'
3048             },
3049             {
3050                 name : 'ok',
3051                 weight : 'primary',
3052                 html : 'OK'
3053             }
3054         ],
3055         /**
3056          * Button config that displays Yes, No and Cancel buttons
3057          * @type Object
3058          */
3059         YESNOCANCEL : [
3060             {
3061                 name : 'yes',
3062                 weight : 'primary',
3063                 html : 'Yes'
3064             },
3065             {
3066                 name : 'no',
3067                 html : 'No'
3068             },
3069             {
3070                 name : 'cancel',
3071                 html : 'Cancel'
3072             }
3073         ],
3074         
3075         zIndex : 10001
3076 });
3077 /*
3078  * - LGPL
3079  *
3080  * messagebox - can be used as a replace
3081  * 
3082  */
3083 /**
3084  * @class Roo.MessageBox
3085  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3086  * Example usage:
3087  *<pre><code>
3088 // Basic alert:
3089 Roo.Msg.alert('Status', 'Changes saved successfully.');
3090
3091 // Prompt for user data:
3092 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3093     if (btn == 'ok'){
3094         // process text value...
3095     }
3096 });
3097
3098 // Show a dialog using config options:
3099 Roo.Msg.show({
3100    title:'Save Changes?',
3101    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3102    buttons: Roo.Msg.YESNOCANCEL,
3103    fn: processResult,
3104    animEl: 'elId'
3105 });
3106 </code></pre>
3107  * @singleton
3108  */
3109 Roo.bootstrap.MessageBox = function(){
3110     var dlg, opt, mask, waitTimer;
3111     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3112     var buttons, activeTextEl, bwidth;
3113
3114     
3115     // private
3116     var handleButton = function(button){
3117         dlg.hide();
3118         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3119     };
3120
3121     // private
3122     var handleHide = function(){
3123         if(opt && opt.cls){
3124             dlg.el.removeClass(opt.cls);
3125         }
3126         //if(waitTimer){
3127         //    Roo.TaskMgr.stop(waitTimer);
3128         //    waitTimer = null;
3129         //}
3130     };
3131
3132     // private
3133     var updateButtons = function(b){
3134         var width = 0;
3135         if(!b){
3136             buttons["ok"].hide();
3137             buttons["cancel"].hide();
3138             buttons["yes"].hide();
3139             buttons["no"].hide();
3140             //dlg.footer.dom.style.display = 'none';
3141             return width;
3142         }
3143         dlg.footerEl.dom.style.display = '';
3144         for(var k in buttons){
3145             if(typeof buttons[k] != "function"){
3146                 if(b[k]){
3147                     buttons[k].show();
3148                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3149                     width += buttons[k].el.getWidth()+15;
3150                 }else{
3151                     buttons[k].hide();
3152                 }
3153             }
3154         }
3155         return width;
3156     };
3157
3158     // private
3159     var handleEsc = function(d, k, e){
3160         if(opt && opt.closable !== false){
3161             dlg.hide();
3162         }
3163         if(e){
3164             e.stopEvent();
3165         }
3166     };
3167
3168     return {
3169         /**
3170          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3171          * @return {Roo.BasicDialog} The BasicDialog element
3172          */
3173         getDialog : function(){
3174            if(!dlg){
3175                 dlg = new Roo.bootstrap.Modal( {
3176                     //draggable: true,
3177                     //resizable:false,
3178                     //constraintoviewport:false,
3179                     //fixedcenter:true,
3180                     //collapsible : false,
3181                     //shim:true,
3182                     //modal: true,
3183                 //    width: 'auto',
3184                   //  height:100,
3185                     //buttonAlign:"center",
3186                     closeClick : function(){
3187                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3188                             handleButton("no");
3189                         }else{
3190                             handleButton("cancel");
3191                         }
3192                     }
3193                 });
3194                 dlg.render();
3195                 dlg.on("hide", handleHide);
3196                 mask = dlg.mask;
3197                 //dlg.addKeyListener(27, handleEsc);
3198                 buttons = {};
3199                 this.buttons = buttons;
3200                 var bt = this.buttonText;
3201                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3202                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3203                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3204                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3205                 //Roo.log(buttons);
3206                 bodyEl = dlg.bodyEl.createChild({
3207
3208                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3209                         '<textarea class="roo-mb-textarea"></textarea>' +
3210                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3211                 });
3212                 msgEl = bodyEl.dom.firstChild;
3213                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3214                 textboxEl.enableDisplayMode();
3215                 textboxEl.addKeyListener([10,13], function(){
3216                     if(dlg.isVisible() && opt && opt.buttons){
3217                         if(opt.buttons.ok){
3218                             handleButton("ok");
3219                         }else if(opt.buttons.yes){
3220                             handleButton("yes");
3221                         }
3222                     }
3223                 });
3224                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3225                 textareaEl.enableDisplayMode();
3226                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3227                 progressEl.enableDisplayMode();
3228                 
3229                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3230                 var pf = progressEl.dom.firstChild;
3231                 if (pf) {
3232                     pp = Roo.get(pf.firstChild);
3233                     pp.setHeight(pf.offsetHeight);
3234                 }
3235                 
3236             }
3237             return dlg;
3238         },
3239
3240         /**
3241          * Updates the message box body text
3242          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3243          * the XHTML-compliant non-breaking space character '&amp;#160;')
3244          * @return {Roo.MessageBox} This message box
3245          */
3246         updateText : function(text)
3247         {
3248             if(!dlg.isVisible() && !opt.width){
3249                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3250                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3251             }
3252             msgEl.innerHTML = text || '&#160;';
3253       
3254             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3255             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3256             var w = Math.max(
3257                     Math.min(opt.width || cw , this.maxWidth), 
3258                     Math.max(opt.minWidth || this.minWidth, bwidth)
3259             );
3260             if(opt.prompt){
3261                 activeTextEl.setWidth(w);
3262             }
3263             if(dlg.isVisible()){
3264                 dlg.fixedcenter = false;
3265             }
3266             // to big, make it scroll. = But as usual stupid IE does not support
3267             // !important..
3268             
3269             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3270                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3271                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3272             } else {
3273                 bodyEl.dom.style.height = '';
3274                 bodyEl.dom.style.overflowY = '';
3275             }
3276             if (cw > w) {
3277                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3278             } else {
3279                 bodyEl.dom.style.overflowX = '';
3280             }
3281             
3282             dlg.setContentSize(w, bodyEl.getHeight());
3283             if(dlg.isVisible()){
3284                 dlg.fixedcenter = true;
3285             }
3286             return this;
3287         },
3288
3289         /**
3290          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3291          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3292          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3293          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3294          * @return {Roo.MessageBox} This message box
3295          */
3296         updateProgress : function(value, text){
3297             if(text){
3298                 this.updateText(text);
3299             }
3300             
3301             if (pp) { // weird bug on my firefox - for some reason this is not defined
3302                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3303                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3304             }
3305             return this;
3306         },        
3307
3308         /**
3309          * Returns true if the message box is currently displayed
3310          * @return {Boolean} True if the message box is visible, else false
3311          */
3312         isVisible : function(){
3313             return dlg && dlg.isVisible();  
3314         },
3315
3316         /**
3317          * Hides the message box if it is displayed
3318          */
3319         hide : function(){
3320             if(this.isVisible()){
3321                 dlg.hide();
3322             }  
3323         },
3324
3325         /**
3326          * Displays a new message box, or reinitializes an existing message box, based on the config options
3327          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3328          * The following config object properties are supported:
3329          * <pre>
3330 Property    Type             Description
3331 ----------  ---------------  ------------------------------------------------------------------------------------
3332 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3333                                    closes (defaults to undefined)
3334 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3335                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3336 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3337                                    progress and wait dialogs will ignore this property and always hide the
3338                                    close button as they can only be closed programmatically.
3339 cls               String           A custom CSS class to apply to the message box element
3340 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3341                                    displayed (defaults to 75)
3342 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3343                                    function will be btn (the name of the button that was clicked, if applicable,
3344                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3345                                    Progress and wait dialogs will ignore this option since they do not respond to
3346                                    user actions and can only be closed programmatically, so any required function
3347                                    should be called by the same code after it closes the dialog.
3348 icon              String           A CSS class that provides a background image to be used as an icon for
3349                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3350 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3351 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3352 modal             Boolean          False to allow user interaction with the page while the message box is
3353                                    displayed (defaults to true)
3354 msg               String           A string that will replace the existing message box body text (defaults
3355                                    to the XHTML-compliant non-breaking space character '&#160;')
3356 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3357 progress          Boolean          True to display a progress bar (defaults to false)
3358 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3359 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3360 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3361 title             String           The title text
3362 value             String           The string value to set into the active textbox element if displayed
3363 wait              Boolean          True to display a progress bar (defaults to false)
3364 width             Number           The width of the dialog in pixels
3365 </pre>
3366          *
3367          * Example usage:
3368          * <pre><code>
3369 Roo.Msg.show({
3370    title: 'Address',
3371    msg: 'Please enter your address:',
3372    width: 300,
3373    buttons: Roo.MessageBox.OKCANCEL,
3374    multiline: true,
3375    fn: saveAddress,
3376    animEl: 'addAddressBtn'
3377 });
3378 </code></pre>
3379          * @param {Object} config Configuration options
3380          * @return {Roo.MessageBox} This message box
3381          */
3382         show : function(options)
3383         {
3384             
3385             // this causes nightmares if you show one dialog after another
3386             // especially on callbacks..
3387              
3388             if(this.isVisible()){
3389                 
3390                 this.hide();
3391                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3392                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3393                 Roo.log("New Dialog Message:" +  options.msg )
3394                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3395                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3396                 
3397             }
3398             var d = this.getDialog();
3399             opt = options;
3400             d.setTitle(opt.title || "&#160;");
3401             d.closeEl.setDisplayed(opt.closable !== false);
3402             activeTextEl = textboxEl;
3403             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3404             if(opt.prompt){
3405                 if(opt.multiline){
3406                     textboxEl.hide();
3407                     textareaEl.show();
3408                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3409                         opt.multiline : this.defaultTextHeight);
3410                     activeTextEl = textareaEl;
3411                 }else{
3412                     textboxEl.show();
3413                     textareaEl.hide();
3414                 }
3415             }else{
3416                 textboxEl.hide();
3417                 textareaEl.hide();
3418             }
3419             progressEl.setDisplayed(opt.progress === true);
3420             this.updateProgress(0);
3421             activeTextEl.dom.value = opt.value || "";
3422             if(opt.prompt){
3423                 dlg.setDefaultButton(activeTextEl);
3424             }else{
3425                 var bs = opt.buttons;
3426                 var db = null;
3427                 if(bs && bs.ok){
3428                     db = buttons["ok"];
3429                 }else if(bs && bs.yes){
3430                     db = buttons["yes"];
3431                 }
3432                 dlg.setDefaultButton(db);
3433             }
3434             bwidth = updateButtons(opt.buttons);
3435             this.updateText(opt.msg);
3436             if(opt.cls){
3437                 d.el.addClass(opt.cls);
3438             }
3439             d.proxyDrag = opt.proxyDrag === true;
3440             d.modal = opt.modal !== false;
3441             d.mask = opt.modal !== false ? mask : false;
3442             if(!d.isVisible()){
3443                 // force it to the end of the z-index stack so it gets a cursor in FF
3444                 document.body.appendChild(dlg.el.dom);
3445                 d.animateTarget = null;
3446                 d.show(options.animEl);
3447             }
3448             return this;
3449         },
3450
3451         /**
3452          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3453          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3454          * and closing the message box when the process is complete.
3455          * @param {String} title The title bar text
3456          * @param {String} msg The message box body text
3457          * @return {Roo.MessageBox} This message box
3458          */
3459         progress : function(title, msg){
3460             this.show({
3461                 title : title,
3462                 msg : msg,
3463                 buttons: false,
3464                 progress:true,
3465                 closable:false,
3466                 minWidth: this.minProgressWidth,
3467                 modal : true
3468             });
3469             return this;
3470         },
3471
3472         /**
3473          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3474          * If a callback function is passed it will be called after the user clicks the button, and the
3475          * id of the button that was clicked will be passed as the only parameter to the callback
3476          * (could also be the top-right close button).
3477          * @param {String} title The title bar text
3478          * @param {String} msg The message box body text
3479          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3480          * @param {Object} scope (optional) The scope of the callback function
3481          * @return {Roo.MessageBox} This message box
3482          */
3483         alert : function(title, msg, fn, scope)
3484         {
3485             this.show({
3486                 title : title,
3487                 msg : msg,
3488                 buttons: this.OK,
3489                 fn: fn,
3490                 closable : false,
3491                 scope : scope,
3492                 modal : true
3493             });
3494             return this;
3495         },
3496
3497         /**
3498          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3499          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3500          * You are responsible for closing the message box when the process is complete.
3501          * @param {String} msg The message box body text
3502          * @param {String} title (optional) The title bar text
3503          * @return {Roo.MessageBox} This message box
3504          */
3505         wait : function(msg, title){
3506             this.show({
3507                 title : title,
3508                 msg : msg,
3509                 buttons: false,
3510                 closable:false,
3511                 progress:true,
3512                 modal:true,
3513                 width:300,
3514                 wait:true
3515             });
3516             waitTimer = Roo.TaskMgr.start({
3517                 run: function(i){
3518                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3519                 },
3520                 interval: 1000
3521             });
3522             return this;
3523         },
3524
3525         /**
3526          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3527          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3528          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3529          * @param {String} title The title bar text
3530          * @param {String} msg The message box body text
3531          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3532          * @param {Object} scope (optional) The scope of the callback function
3533          * @return {Roo.MessageBox} This message box
3534          */
3535         confirm : function(title, msg, fn, scope){
3536             this.show({
3537                 title : title,
3538                 msg : msg,
3539                 buttons: this.YESNO,
3540                 fn: fn,
3541                 scope : scope,
3542                 modal : true
3543             });
3544             return this;
3545         },
3546
3547         /**
3548          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3549          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3550          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3551          * (could also be the top-right close button) and the text that was entered will be passed as the two
3552          * parameters to the callback.
3553          * @param {String} title The title bar text
3554          * @param {String} msg The message box body text
3555          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3556          * @param {Object} scope (optional) The scope of the callback function
3557          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3558          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3559          * @return {Roo.MessageBox} This message box
3560          */
3561         prompt : function(title, msg, fn, scope, multiline){
3562             this.show({
3563                 title : title,
3564                 msg : msg,
3565                 buttons: this.OKCANCEL,
3566                 fn: fn,
3567                 minWidth:250,
3568                 scope : scope,
3569                 prompt:true,
3570                 multiline: multiline,
3571                 modal : true
3572             });
3573             return this;
3574         },
3575
3576         /**
3577          * Button config that displays a single OK button
3578          * @type Object
3579          */
3580         OK : {ok:true},
3581         /**
3582          * Button config that displays Yes and No buttons
3583          * @type Object
3584          */
3585         YESNO : {yes:true, no:true},
3586         /**
3587          * Button config that displays OK and Cancel buttons
3588          * @type Object
3589          */
3590         OKCANCEL : {ok:true, cancel:true},
3591         /**
3592          * Button config that displays Yes, No and Cancel buttons
3593          * @type Object
3594          */
3595         YESNOCANCEL : {yes:true, no:true, cancel:true},
3596
3597         /**
3598          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3599          * @type Number
3600          */
3601         defaultTextHeight : 75,
3602         /**
3603          * The maximum width in pixels of the message box (defaults to 600)
3604          * @type Number
3605          */
3606         maxWidth : 600,
3607         /**
3608          * The minimum width in pixels of the message box (defaults to 100)
3609          * @type Number
3610          */
3611         minWidth : 100,
3612         /**
3613          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3614          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3615          * @type Number
3616          */
3617         minProgressWidth : 250,
3618         /**
3619          * An object containing the default button text strings that can be overriden for localized language support.
3620          * Supported properties are: ok, cancel, yes and no.
3621          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3622          * @type Object
3623          */
3624         buttonText : {
3625             ok : "OK",
3626             cancel : "Cancel",
3627             yes : "Yes",
3628             no : "No"
3629         }
3630     };
3631 }();
3632
3633 /**
3634  * Shorthand for {@link Roo.MessageBox}
3635  */
3636 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3637 Roo.Msg = Roo.Msg || Roo.MessageBox;
3638 /*
3639  * - LGPL
3640  *
3641  * navbar
3642  * 
3643  */
3644
3645 /**
3646  * @class Roo.bootstrap.Navbar
3647  * @extends Roo.bootstrap.Component
3648  * Bootstrap Navbar class
3649
3650  * @constructor
3651  * Create a new Navbar
3652  * @param {Object} config The config object
3653  */
3654
3655
3656 Roo.bootstrap.Navbar = function(config){
3657     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3658     this.addEvents({
3659         // raw events
3660         /**
3661          * @event beforetoggle
3662          * Fire before toggle the menu
3663          * @param {Roo.EventObject} e
3664          */
3665         "beforetoggle" : true
3666     });
3667 };
3668
3669 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3670     
3671     
3672    
3673     // private
3674     navItems : false,
3675     loadMask : false,
3676     
3677     
3678     getAutoCreate : function(){
3679         
3680         
3681         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3682         
3683     },
3684     
3685     initEvents :function ()
3686     {
3687         //Roo.log(this.el.select('.navbar-toggle',true));
3688         this.el.select('.navbar-toggle',true).on('click', function() {
3689             if(this.fireEvent('beforetoggle', this) !== false){
3690                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3691             }
3692             
3693         }, this);
3694         
3695         var mark = {
3696             tag: "div",
3697             cls:"x-dlg-mask"
3698         };
3699         
3700         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3701         
3702         var size = this.el.getSize();
3703         this.maskEl.setSize(size.width, size.height);
3704         this.maskEl.enableDisplayMode("block");
3705         this.maskEl.hide();
3706         
3707         if(this.loadMask){
3708             this.maskEl.show();
3709         }
3710     },
3711     
3712     
3713     getChildContainer : function()
3714     {
3715         if (this.el.select('.collapse').getCount()) {
3716             return this.el.select('.collapse',true).first();
3717         }
3718         
3719         return this.el;
3720     },
3721     
3722     mask : function()
3723     {
3724         this.maskEl.show();
3725     },
3726     
3727     unmask : function()
3728     {
3729         this.maskEl.hide();
3730     } 
3731     
3732     
3733     
3734     
3735 });
3736
3737
3738
3739  
3740
3741  /*
3742  * - LGPL
3743  *
3744  * navbar
3745  * 
3746  */
3747
3748 /**
3749  * @class Roo.bootstrap.NavSimplebar
3750  * @extends Roo.bootstrap.Navbar
3751  * Bootstrap Sidebar class
3752  *
3753  * @cfg {Boolean} inverse is inverted color
3754  * 
3755  * @cfg {String} type (nav | pills | tabs)
3756  * @cfg {Boolean} arrangement stacked | justified
3757  * @cfg {String} align (left | right) alignment
3758  * 
3759  * @cfg {Boolean} main (true|false) main nav bar? default false
3760  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3761  * 
3762  * @cfg {String} tag (header|footer|nav|div) default is nav 
3763
3764  * 
3765  * 
3766  * 
3767  * @constructor
3768  * Create a new Sidebar
3769  * @param {Object} config The config object
3770  */
3771
3772
3773 Roo.bootstrap.NavSimplebar = function(config){
3774     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3775 };
3776
3777 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3778     
3779     inverse: false,
3780     
3781     type: false,
3782     arrangement: '',
3783     align : false,
3784     
3785     
3786     
3787     main : false,
3788     
3789     
3790     tag : false,
3791     
3792     
3793     getAutoCreate : function(){
3794         
3795         
3796         var cfg = {
3797             tag : this.tag || 'div',
3798             cls : 'navbar'
3799         };
3800           
3801         
3802         cfg.cn = [
3803             {
3804                 cls: 'nav',
3805                 tag : 'ul'
3806             }
3807         ];
3808         
3809          
3810         this.type = this.type || 'nav';
3811         if (['tabs','pills'].indexOf(this.type)!==-1) {
3812             cfg.cn[0].cls += ' nav-' + this.type
3813         
3814         
3815         } else {
3816             if (this.type!=='nav') {
3817                 Roo.log('nav type must be nav/tabs/pills')
3818             }
3819             cfg.cn[0].cls += ' navbar-nav'
3820         }
3821         
3822         
3823         
3824         
3825         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3826             cfg.cn[0].cls += ' nav-' + this.arrangement;
3827         }
3828         
3829         
3830         if (this.align === 'right') {
3831             cfg.cn[0].cls += ' navbar-right';
3832         }
3833         
3834         if (this.inverse) {
3835             cfg.cls += ' navbar-inverse';
3836             
3837         }
3838         
3839         
3840         return cfg;
3841     
3842         
3843     }
3844     
3845     
3846     
3847 });
3848
3849
3850
3851  
3852
3853  
3854        /*
3855  * - LGPL
3856  *
3857  * navbar
3858  * 
3859  */
3860
3861 /**
3862  * @class Roo.bootstrap.NavHeaderbar
3863  * @extends Roo.bootstrap.NavSimplebar
3864  * Bootstrap Sidebar class
3865  *
3866  * @cfg {String} brand what is brand
3867  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3868  * @cfg {String} brand_href href of the brand
3869  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3870  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3871  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3872  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3873  * 
3874  * @constructor
3875  * Create a new Sidebar
3876  * @param {Object} config The config object
3877  */
3878
3879
3880 Roo.bootstrap.NavHeaderbar = function(config){
3881     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3882       
3883 };
3884
3885 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3886     
3887     position: '',
3888     brand: '',
3889     brand_href: false,
3890     srButton : true,
3891     autohide : false,
3892     desktopCenter : false,
3893    
3894     
3895     getAutoCreate : function(){
3896         
3897         var   cfg = {
3898             tag: this.nav || 'nav',
3899             cls: 'navbar',
3900             role: 'navigation',
3901             cn: []
3902         };
3903         
3904         var cn = cfg.cn;
3905         if (this.desktopCenter) {
3906             cn.push({cls : 'container', cn : []});
3907             cn = cn[0].cn;
3908         }
3909         
3910         if(this.srButton){
3911             cn.push({
3912                 tag: 'div',
3913                 cls: 'navbar-header',
3914                 cn: [
3915                     {
3916                         tag: 'button',
3917                         type: 'button',
3918                         cls: 'navbar-toggle',
3919                         'data-toggle': 'collapse',
3920                         cn: [
3921                             {
3922                                 tag: 'span',
3923                                 cls: 'sr-only',
3924                                 html: 'Toggle navigation'
3925                             },
3926                             {
3927                                 tag: 'span',
3928                                 cls: 'icon-bar'
3929                             },
3930                             {
3931                                 tag: 'span',
3932                                 cls: 'icon-bar'
3933                             },
3934                             {
3935                                 tag: 'span',
3936                                 cls: 'icon-bar'
3937                             }
3938                         ]
3939                     }
3940                 ]
3941             });
3942         }
3943         
3944         cn.push({
3945             tag: 'div',
3946             cls: 'collapse navbar-collapse',
3947             cn : []
3948         });
3949         
3950         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3951         
3952         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3953             cfg.cls += ' navbar-' + this.position;
3954             
3955             // tag can override this..
3956             
3957             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3958         }
3959         
3960         if (this.brand !== '') {
3961             cn[0].cn.push({
3962                 tag: 'a',
3963                 href: this.brand_href ? this.brand_href : '#',
3964                 cls: 'navbar-brand',
3965                 cn: [
3966                 this.brand
3967                 ]
3968             });
3969         }
3970         
3971         if(this.main){
3972             cfg.cls += ' main-nav';
3973         }
3974         
3975         
3976         return cfg;
3977
3978         
3979     },
3980     getHeaderChildContainer : function()
3981     {
3982         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3983             return this.el.select('.navbar-header',true).first();
3984         }
3985         
3986         return this.getChildContainer();
3987     },
3988     
3989     
3990     initEvents : function()
3991     {
3992         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3993         
3994         if (this.autohide) {
3995             
3996             var prevScroll = 0;
3997             var ft = this.el;
3998             
3999             Roo.get(document).on('scroll',function(e) {
4000                 var ns = Roo.get(document).getScroll().top;
4001                 var os = prevScroll;
4002                 prevScroll = ns;
4003                 
4004                 if(ns > os){
4005                     ft.removeClass('slideDown');
4006                     ft.addClass('slideUp');
4007                     return;
4008                 }
4009                 ft.removeClass('slideUp');
4010                 ft.addClass('slideDown');
4011                  
4012               
4013           },this);
4014         }
4015     }    
4016     
4017 });
4018
4019
4020
4021  
4022
4023  /*
4024  * - LGPL
4025  *
4026  * navbar
4027  * 
4028  */
4029
4030 /**
4031  * @class Roo.bootstrap.NavSidebar
4032  * @extends Roo.bootstrap.Navbar
4033  * Bootstrap Sidebar class
4034  * 
4035  * @constructor
4036  * Create a new Sidebar
4037  * @param {Object} config The config object
4038  */
4039
4040
4041 Roo.bootstrap.NavSidebar = function(config){
4042     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4043 };
4044
4045 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4046     
4047     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4048     
4049     getAutoCreate : function(){
4050         
4051         
4052         return  {
4053             tag: 'div',
4054             cls: 'sidebar sidebar-nav'
4055         };
4056     
4057         
4058     }
4059     
4060     
4061     
4062 });
4063
4064
4065
4066  
4067
4068  /*
4069  * - LGPL
4070  *
4071  * nav group
4072  * 
4073  */
4074
4075 /**
4076  * @class Roo.bootstrap.NavGroup
4077  * @extends Roo.bootstrap.Component
4078  * Bootstrap NavGroup class
4079  * @cfg {String} align (left|right)
4080  * @cfg {Boolean} inverse
4081  * @cfg {String} type (nav|pills|tab) default nav
4082  * @cfg {String} navId - reference Id for navbar.
4083
4084  * 
4085  * @constructor
4086  * Create a new nav group
4087  * @param {Object} config The config object
4088  */
4089
4090 Roo.bootstrap.NavGroup = function(config){
4091     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4092     this.navItems = [];
4093    
4094     Roo.bootstrap.NavGroup.register(this);
4095      this.addEvents({
4096         /**
4097              * @event changed
4098              * Fires when the active item changes
4099              * @param {Roo.bootstrap.NavGroup} this
4100              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4101              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4102          */
4103         'changed': true
4104      });
4105     
4106 };
4107
4108 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4109     
4110     align: '',
4111     inverse: false,
4112     form: false,
4113     type: 'nav',
4114     navId : '',
4115     // private
4116     
4117     navItems : false, 
4118     
4119     getAutoCreate : function()
4120     {
4121         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4122         
4123         cfg = {
4124             tag : 'ul',
4125             cls: 'nav' 
4126         };
4127         
4128         if (['tabs','pills'].indexOf(this.type)!==-1) {
4129             cfg.cls += ' nav-' + this.type
4130         } else {
4131             if (this.type!=='nav') {
4132                 Roo.log('nav type must be nav/tabs/pills')
4133             }
4134             cfg.cls += ' navbar-nav'
4135         }
4136         
4137         if (this.parent() && this.parent().sidebar) {
4138             cfg = {
4139                 tag: 'ul',
4140                 cls: 'dashboard-menu sidebar-menu'
4141             };
4142             
4143             return cfg;
4144         }
4145         
4146         if (this.form === true) {
4147             cfg = {
4148                 tag: 'form',
4149                 cls: 'navbar-form'
4150             };
4151             
4152             if (this.align === 'right') {
4153                 cfg.cls += ' navbar-right';
4154             } else {
4155                 cfg.cls += ' navbar-left';
4156             }
4157         }
4158         
4159         if (this.align === 'right') {
4160             cfg.cls += ' navbar-right';
4161         }
4162         
4163         if (this.inverse) {
4164             cfg.cls += ' navbar-inverse';
4165             
4166         }
4167         
4168         
4169         return cfg;
4170     },
4171     /**
4172     * sets the active Navigation item
4173     * @param {Roo.bootstrap.NavItem} the new current navitem
4174     */
4175     setActiveItem : function(item)
4176     {
4177         var prev = false;
4178         Roo.each(this.navItems, function(v){
4179             if (v == item) {
4180                 return ;
4181             }
4182             if (v.isActive()) {
4183                 v.setActive(false, true);
4184                 prev = v;
4185                 
4186             }
4187             
4188         });
4189
4190         item.setActive(true, true);
4191         this.fireEvent('changed', this, item, prev);
4192         
4193         
4194     },
4195     /**
4196     * gets the active Navigation item
4197     * @return {Roo.bootstrap.NavItem} the current navitem
4198     */
4199     getActive : function()
4200     {
4201         
4202         var prev = false;
4203         Roo.each(this.navItems, function(v){
4204             
4205             if (v.isActive()) {
4206                 prev = v;
4207                 
4208             }
4209             
4210         });
4211         return prev;
4212     },
4213     
4214     indexOfNav : function()
4215     {
4216         
4217         var prev = false;
4218         Roo.each(this.navItems, function(v,i){
4219             
4220             if (v.isActive()) {
4221                 prev = i;
4222                 
4223             }
4224             
4225         });
4226         return prev;
4227     },
4228     /**
4229     * adds a Navigation item
4230     * @param {Roo.bootstrap.NavItem} the navitem to add
4231     */
4232     addItem : function(cfg)
4233     {
4234         var cn = new Roo.bootstrap.NavItem(cfg);
4235         this.register(cn);
4236         cn.parentId = this.id;
4237         cn.onRender(this.el, null);
4238         return cn;
4239     },
4240     /**
4241     * register a Navigation item
4242     * @param {Roo.bootstrap.NavItem} the navitem to add
4243     */
4244     register : function(item)
4245     {
4246         this.navItems.push( item);
4247         item.navId = this.navId;
4248     
4249     },
4250     
4251     /**
4252     * clear all the Navigation item
4253     */
4254    
4255     clearAll : function()
4256     {
4257         this.navItems = [];
4258         this.el.dom.innerHTML = '';
4259     },
4260     
4261     getNavItem: function(tabId)
4262     {
4263         var ret = false;
4264         Roo.each(this.navItems, function(e) {
4265             if (e.tabId == tabId) {
4266                ret =  e;
4267                return false;
4268             }
4269             return true;
4270             
4271         });
4272         return ret;
4273     },
4274     
4275     setActiveNext : function()
4276     {
4277         var i = this.indexOfNav(this.getActive());
4278         if (i > this.navItems.length) {
4279             return;
4280         }
4281         this.setActiveItem(this.navItems[i+1]);
4282     },
4283     setActivePrev : function()
4284     {
4285         var i = this.indexOfNav(this.getActive());
4286         if (i  < 1) {
4287             return;
4288         }
4289         this.setActiveItem(this.navItems[i-1]);
4290     },
4291     clearWasActive : function(except) {
4292         Roo.each(this.navItems, function(e) {
4293             if (e.tabId != except.tabId && e.was_active) {
4294                e.was_active = false;
4295                return false;
4296             }
4297             return true;
4298             
4299         });
4300     },
4301     getWasActive : function ()
4302     {
4303         var r = false;
4304         Roo.each(this.navItems, function(e) {
4305             if (e.was_active) {
4306                r = e;
4307                return false;
4308             }
4309             return true;
4310             
4311         });
4312         return r;
4313     }
4314     
4315     
4316 });
4317
4318  
4319 Roo.apply(Roo.bootstrap.NavGroup, {
4320     
4321     groups: {},
4322      /**
4323     * register a Navigation Group
4324     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4325     */
4326     register : function(navgrp)
4327     {
4328         this.groups[navgrp.navId] = navgrp;
4329         
4330     },
4331     /**
4332     * fetch a Navigation Group based on the navigation ID
4333     * @param {string} the navgroup to add
4334     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4335     */
4336     get: function(navId) {
4337         if (typeof(this.groups[navId]) == 'undefined') {
4338             return false;
4339             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4340         }
4341         return this.groups[navId] ;
4342     }
4343     
4344     
4345     
4346 });
4347
4348  /*
4349  * - LGPL
4350  *
4351  * row
4352  * 
4353  */
4354
4355 /**
4356  * @class Roo.bootstrap.NavItem
4357  * @extends Roo.bootstrap.Component
4358  * Bootstrap Navbar.NavItem class
4359  * @cfg {String} href  link to
4360  * @cfg {String} html content of button
4361  * @cfg {String} badge text inside badge
4362  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4363  * @cfg {String} glyphicon name of glyphicon
4364  * @cfg {String} icon name of font awesome icon
4365  * @cfg {Boolean} active Is item active
4366  * @cfg {Boolean} disabled Is item disabled
4367  
4368  * @cfg {Boolean} preventDefault (true | false) default false
4369  * @cfg {String} tabId the tab that this item activates.
4370  * @cfg {String} tagtype (a|span) render as a href or span?
4371  * @cfg {Boolean} animateRef (true|false) link to element default false  
4372   
4373  * @constructor
4374  * Create a new Navbar Item
4375  * @param {Object} config The config object
4376  */
4377 Roo.bootstrap.NavItem = function(config){
4378     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4379     this.addEvents({
4380         // raw events
4381         /**
4382          * @event click
4383          * The raw click event for the entire grid.
4384          * @param {Roo.EventObject} e
4385          */
4386         "click" : true,
4387          /**
4388             * @event changed
4389             * Fires when the active item active state changes
4390             * @param {Roo.bootstrap.NavItem} this
4391             * @param {boolean} state the new state
4392              
4393          */
4394         'changed': true,
4395         /**
4396             * @event scrollto
4397             * Fires when scroll to element
4398             * @param {Roo.bootstrap.NavItem} this
4399             * @param {Object} options
4400             * @param {Roo.EventObject} e
4401              
4402          */
4403         'scrollto': true
4404     });
4405    
4406 };
4407
4408 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4409     
4410     href: false,
4411     html: '',
4412     badge: '',
4413     icon: false,
4414     glyphicon: false,
4415     active: false,
4416     preventDefault : false,
4417     tabId : false,
4418     tagtype : 'a',
4419     disabled : false,
4420     animateRef : false,
4421     was_active : false,
4422     
4423     getAutoCreate : function(){
4424          
4425         var cfg = {
4426             tag: 'li',
4427             cls: 'nav-item'
4428             
4429         };
4430         
4431         if (this.active) {
4432             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4433         }
4434         if (this.disabled) {
4435             cfg.cls += ' disabled';
4436         }
4437         
4438         if (this.href || this.html || this.glyphicon || this.icon) {
4439             cfg.cn = [
4440                 {
4441                     tag: this.tagtype,
4442                     href : this.href || "#",
4443                     html: this.html || ''
4444                 }
4445             ];
4446             
4447             if (this.icon) {
4448                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4449             }
4450
4451             if(this.glyphicon) {
4452                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4453             }
4454             
4455             if (this.menu) {
4456                 
4457                 cfg.cn[0].html += " <span class='caret'></span>";
4458              
4459             }
4460             
4461             if (this.badge !== '') {
4462                  
4463                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4464             }
4465         }
4466         
4467         
4468         
4469         return cfg;
4470     },
4471     initEvents: function() 
4472     {
4473         if (typeof (this.menu) != 'undefined') {
4474             this.menu.parentType = this.xtype;
4475             this.menu.triggerEl = this.el;
4476             this.menu = this.addxtype(Roo.apply({}, this.menu));
4477         }
4478         
4479         this.el.select('a',true).on('click', this.onClick, this);
4480         
4481         if(this.tagtype == 'span'){
4482             this.el.select('span',true).on('click', this.onClick, this);
4483         }
4484        
4485         // at this point parent should be available..
4486         this.parent().register(this);
4487     },
4488     
4489     onClick : function(e)
4490     {
4491         if (e.getTarget('.dropdown-menu-item')) {
4492             // did you click on a menu itemm.... - then don't trigger onclick..
4493             return;
4494         }
4495         
4496         if(
4497                 this.preventDefault || 
4498                 this.href == '#' 
4499         ){
4500             Roo.log("NavItem - prevent Default?");
4501             e.preventDefault();
4502         }
4503         
4504         if (this.disabled) {
4505             return;
4506         }
4507         
4508         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4509         if (tg && tg.transition) {
4510             Roo.log("waiting for the transitionend");
4511             return;
4512         }
4513         
4514         
4515         
4516         //Roo.log("fire event clicked");
4517         if(this.fireEvent('click', this, e) === false){
4518             return;
4519         };
4520         
4521         if(this.tagtype == 'span'){
4522             return;
4523         }
4524         
4525         //Roo.log(this.href);
4526         var ael = this.el.select('a',true).first();
4527         //Roo.log(ael);
4528         
4529         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4530             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4531             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4532                 return; // ignore... - it's a 'hash' to another page.
4533             }
4534             Roo.log("NavItem - prevent Default?");
4535             e.preventDefault();
4536             this.scrollToElement(e);
4537         }
4538         
4539         
4540         var p =  this.parent();
4541    
4542         if (['tabs','pills'].indexOf(p.type)!==-1) {
4543             if (typeof(p.setActiveItem) !== 'undefined') {
4544                 p.setActiveItem(this);
4545             }
4546         }
4547         
4548         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4549         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4550             // remove the collapsed menu expand...
4551             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4552         }
4553     },
4554     
4555     isActive: function () {
4556         return this.active
4557     },
4558     setActive : function(state, fire, is_was_active)
4559     {
4560         if (this.active && !state && this.navId) {
4561             this.was_active = true;
4562             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4563             if (nv) {
4564                 nv.clearWasActive(this);
4565             }
4566             
4567         }
4568         this.active = state;
4569         
4570         if (!state ) {
4571             this.el.removeClass('active');
4572         } else if (!this.el.hasClass('active')) {
4573             this.el.addClass('active');
4574         }
4575         if (fire) {
4576             this.fireEvent('changed', this, state);
4577         }
4578         
4579         // show a panel if it's registered and related..
4580         
4581         if (!this.navId || !this.tabId || !state || is_was_active) {
4582             return;
4583         }
4584         
4585         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4586         if (!tg) {
4587             return;
4588         }
4589         var pan = tg.getPanelByName(this.tabId);
4590         if (!pan) {
4591             return;
4592         }
4593         // if we can not flip to new panel - go back to old nav highlight..
4594         if (false == tg.showPanel(pan)) {
4595             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4596             if (nv) {
4597                 var onav = nv.getWasActive();
4598                 if (onav) {
4599                     onav.setActive(true, false, true);
4600                 }
4601             }
4602             
4603         }
4604         
4605         
4606         
4607     },
4608      // this should not be here...
4609     setDisabled : function(state)
4610     {
4611         this.disabled = state;
4612         if (!state ) {
4613             this.el.removeClass('disabled');
4614         } else if (!this.el.hasClass('disabled')) {
4615             this.el.addClass('disabled');
4616         }
4617         
4618     },
4619     
4620     /**
4621      * Fetch the element to display the tooltip on.
4622      * @return {Roo.Element} defaults to this.el
4623      */
4624     tooltipEl : function()
4625     {
4626         return this.el.select('' + this.tagtype + '', true).first();
4627     },
4628     
4629     scrollToElement : function(e)
4630     {
4631         var c = document.body;
4632         
4633         /*
4634          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4635          */
4636         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4637             c = document.documentElement;
4638         }
4639         
4640         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4641         
4642         if(!target){
4643             return;
4644         }
4645
4646         var o = target.calcOffsetsTo(c);
4647         
4648         var options = {
4649             target : target,
4650             value : o[1]
4651         };
4652         
4653         this.fireEvent('scrollto', this, options, e);
4654         
4655         Roo.get(c).scrollTo('top', options.value, true);
4656         
4657         return;
4658     }
4659 });
4660  
4661
4662  /*
4663  * - LGPL
4664  *
4665  * sidebar item
4666  *
4667  *  li
4668  *    <span> icon </span>
4669  *    <span> text </span>
4670  *    <span>badge </span>
4671  */
4672
4673 /**
4674  * @class Roo.bootstrap.NavSidebarItem
4675  * @extends Roo.bootstrap.NavItem
4676  * Bootstrap Navbar.NavSidebarItem class
4677  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4678  * {Boolean} open is the menu open
4679  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4680  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4681  * {String} buttonSize (sm|md|lg)the extra classes for the button
4682  * {Boolean} showArrow show arrow next to the text (default true)
4683  * @constructor
4684  * Create a new Navbar Button
4685  * @param {Object} config The config object
4686  */
4687 Roo.bootstrap.NavSidebarItem = function(config){
4688     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4689     this.addEvents({
4690         // raw events
4691         /**
4692          * @event click
4693          * The raw click event for the entire grid.
4694          * @param {Roo.EventObject} e
4695          */
4696         "click" : true,
4697          /**
4698             * @event changed
4699             * Fires when the active item active state changes
4700             * @param {Roo.bootstrap.NavSidebarItem} this
4701             * @param {boolean} state the new state
4702              
4703          */
4704         'changed': true
4705     });
4706    
4707 };
4708
4709 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4710     
4711     badgeWeight : 'default',
4712     
4713     open: false,
4714     
4715     buttonView : false,
4716     
4717     buttonWeight : 'default',
4718     
4719     buttonSize : 'md',
4720     
4721     showArrow : true,
4722     
4723     getAutoCreate : function(){
4724         
4725         
4726         var a = {
4727                 tag: 'a',
4728                 href : this.href || '#',
4729                 cls: '',
4730                 html : '',
4731                 cn : []
4732         };
4733         
4734         if(this.buttonView){
4735             a = {
4736                 tag: 'button',
4737                 href : this.href || '#',
4738                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4739                 html : this.html,
4740                 cn : []
4741             };
4742         }
4743         
4744         var cfg = {
4745             tag: 'li',
4746             cls: '',
4747             cn: [ a ]
4748         };
4749         
4750         if (this.active) {
4751             cfg.cls += ' active';
4752         }
4753         
4754         if (this.disabled) {
4755             cfg.cls += ' disabled';
4756         }
4757         if (this.open) {
4758             cfg.cls += ' open x-open';
4759         }
4760         // left icon..
4761         if (this.glyphicon || this.icon) {
4762             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4763             a.cn.push({ tag : 'i', cls : c }) ;
4764         }
4765         
4766         if(!this.buttonView){
4767             var span = {
4768                 tag: 'span',
4769                 html : this.html || ''
4770             };
4771
4772             a.cn.push(span);
4773             
4774         }
4775         
4776         if (this.badge !== '') {
4777             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4778         }
4779         
4780         if (this.menu) {
4781             
4782             if(this.showArrow){
4783                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4784             }
4785             
4786             a.cls += ' dropdown-toggle treeview' ;
4787         }
4788         
4789         return cfg;
4790     },
4791     
4792     initEvents : function()
4793     { 
4794         if (typeof (this.menu) != 'undefined') {
4795             this.menu.parentType = this.xtype;
4796             this.menu.triggerEl = this.el;
4797             this.menu = this.addxtype(Roo.apply({}, this.menu));
4798         }
4799         
4800         this.el.on('click', this.onClick, this);
4801         
4802         if(this.badge !== ''){
4803             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4804         }
4805         
4806     },
4807     
4808     onClick : function(e)
4809     {
4810         if(this.disabled){
4811             e.preventDefault();
4812             return;
4813         }
4814         
4815         if(this.preventDefault){
4816             e.preventDefault();
4817         }
4818         
4819         this.fireEvent('click', this);
4820     },
4821     
4822     disable : function()
4823     {
4824         this.setDisabled(true);
4825     },
4826     
4827     enable : function()
4828     {
4829         this.setDisabled(false);
4830     },
4831     
4832     setDisabled : function(state)
4833     {
4834         if(this.disabled == state){
4835             return;
4836         }
4837         
4838         this.disabled = state;
4839         
4840         if (state) {
4841             this.el.addClass('disabled');
4842             return;
4843         }
4844         
4845         this.el.removeClass('disabled');
4846         
4847         return;
4848     },
4849     
4850     setActive : function(state)
4851     {
4852         if(this.active == state){
4853             return;
4854         }
4855         
4856         this.active = state;
4857         
4858         if (state) {
4859             this.el.addClass('active');
4860             return;
4861         }
4862         
4863         this.el.removeClass('active');
4864         
4865         return;
4866     },
4867     
4868     isActive: function () 
4869     {
4870         return this.active;
4871     },
4872     
4873     setBadge : function(str)
4874     {
4875         if(!this.badgeEl){
4876             return;
4877         }
4878         
4879         this.badgeEl.dom.innerHTML = str;
4880     }
4881     
4882    
4883      
4884  
4885 });
4886  
4887
4888  /*
4889  * - LGPL
4890  *
4891  * row
4892  * 
4893  */
4894
4895 /**
4896  * @class Roo.bootstrap.Row
4897  * @extends Roo.bootstrap.Component
4898  * Bootstrap Row class (contains columns...)
4899  * 
4900  * @constructor
4901  * Create a new Row
4902  * @param {Object} config The config object
4903  */
4904
4905 Roo.bootstrap.Row = function(config){
4906     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4907 };
4908
4909 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4910     
4911     getAutoCreate : function(){
4912        return {
4913             cls: 'row clearfix'
4914        };
4915     }
4916     
4917     
4918 });
4919
4920  
4921
4922  /*
4923  * - LGPL
4924  *
4925  * element
4926  * 
4927  */
4928
4929 /**
4930  * @class Roo.bootstrap.Element
4931  * @extends Roo.bootstrap.Component
4932  * Bootstrap Element class
4933  * @cfg {String} html contents of the element
4934  * @cfg {String} tag tag of the element
4935  * @cfg {String} cls class of the element
4936  * @cfg {Boolean} preventDefault (true|false) default false
4937  * @cfg {Boolean} clickable (true|false) default false
4938  * 
4939  * @constructor
4940  * Create a new Element
4941  * @param {Object} config The config object
4942  */
4943
4944 Roo.bootstrap.Element = function(config){
4945     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4946     
4947     this.addEvents({
4948         // raw events
4949         /**
4950          * @event click
4951          * When a element is chick
4952          * @param {Roo.bootstrap.Element} this
4953          * @param {Roo.EventObject} e
4954          */
4955         "click" : true
4956     });
4957 };
4958
4959 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4960     
4961     tag: 'div',
4962     cls: '',
4963     html: '',
4964     preventDefault: false, 
4965     clickable: false,
4966     
4967     getAutoCreate : function(){
4968         
4969         var cfg = {
4970             tag: this.tag,
4971             // cls: this.cls, double assign in parent class Component.js :: onRender
4972             html: this.html
4973         };
4974         
4975         return cfg;
4976     },
4977     
4978     initEvents: function() 
4979     {
4980         Roo.bootstrap.Element.superclass.initEvents.call(this);
4981         
4982         if(this.clickable){
4983             this.el.on('click', this.onClick, this);
4984         }
4985         
4986     },
4987     
4988     onClick : function(e)
4989     {
4990         if(this.preventDefault){
4991             e.preventDefault();
4992         }
4993         
4994         this.fireEvent('click', this, e);
4995     },
4996     
4997     getValue : function()
4998     {
4999         return this.el.dom.innerHTML;
5000     },
5001     
5002     setValue : function(value)
5003     {
5004         this.el.dom.innerHTML = value;
5005     }
5006    
5007 });
5008
5009  
5010
5011  /*
5012  * - LGPL
5013  *
5014  * pagination
5015  * 
5016  */
5017
5018 /**
5019  * @class Roo.bootstrap.Pagination
5020  * @extends Roo.bootstrap.Component
5021  * Bootstrap Pagination class
5022  * @cfg {String} size xs | sm | md | lg
5023  * @cfg {Boolean} inverse false | true
5024  * 
5025  * @constructor
5026  * Create a new Pagination
5027  * @param {Object} config The config object
5028  */
5029
5030 Roo.bootstrap.Pagination = function(config){
5031     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5032 };
5033
5034 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5035     
5036     cls: false,
5037     size: false,
5038     inverse: false,
5039     
5040     getAutoCreate : function(){
5041         var cfg = {
5042             tag: 'ul',
5043                 cls: 'pagination'
5044         };
5045         if (this.inverse) {
5046             cfg.cls += ' inverse';
5047         }
5048         if (this.html) {
5049             cfg.html=this.html;
5050         }
5051         if (this.cls) {
5052             cfg.cls += " " + this.cls;
5053         }
5054         return cfg;
5055     }
5056    
5057 });
5058
5059  
5060
5061  /*
5062  * - LGPL
5063  *
5064  * Pagination item
5065  * 
5066  */
5067
5068
5069 /**
5070  * @class Roo.bootstrap.PaginationItem
5071  * @extends Roo.bootstrap.Component
5072  * Bootstrap PaginationItem class
5073  * @cfg {String} html text
5074  * @cfg {String} href the link
5075  * @cfg {Boolean} preventDefault (true | false) default true
5076  * @cfg {Boolean} active (true | false) default false
5077  * @cfg {Boolean} disabled default false
5078  * 
5079  * 
5080  * @constructor
5081  * Create a new PaginationItem
5082  * @param {Object} config The config object
5083  */
5084
5085
5086 Roo.bootstrap.PaginationItem = function(config){
5087     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5088     this.addEvents({
5089         // raw events
5090         /**
5091          * @event click
5092          * The raw click event for the entire grid.
5093          * @param {Roo.EventObject} e
5094          */
5095         "click" : true
5096     });
5097 };
5098
5099 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5100     
5101     href : false,
5102     html : false,
5103     preventDefault: true,
5104     active : false,
5105     cls : false,
5106     disabled: false,
5107     
5108     getAutoCreate : function(){
5109         var cfg= {
5110             tag: 'li',
5111             cn: [
5112                 {
5113                     tag : 'a',
5114                     href : this.href ? this.href : '#',
5115                     html : this.html ? this.html : ''
5116                 }
5117             ]
5118         };
5119         
5120         if(this.cls){
5121             cfg.cls = this.cls;
5122         }
5123         
5124         if(this.disabled){
5125             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5126         }
5127         
5128         if(this.active){
5129             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5130         }
5131         
5132         return cfg;
5133     },
5134     
5135     initEvents: function() {
5136         
5137         this.el.on('click', this.onClick, this);
5138         
5139     },
5140     onClick : function(e)
5141     {
5142         Roo.log('PaginationItem on click ');
5143         if(this.preventDefault){
5144             e.preventDefault();
5145         }
5146         
5147         if(this.disabled){
5148             return;
5149         }
5150         
5151         this.fireEvent('click', this, e);
5152     }
5153    
5154 });
5155
5156  
5157
5158  /*
5159  * - LGPL
5160  *
5161  * slider
5162  * 
5163  */
5164
5165
5166 /**
5167  * @class Roo.bootstrap.Slider
5168  * @extends Roo.bootstrap.Component
5169  * Bootstrap Slider class
5170  *    
5171  * @constructor
5172  * Create a new Slider
5173  * @param {Object} config The config object
5174  */
5175
5176 Roo.bootstrap.Slider = function(config){
5177     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5178 };
5179
5180 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5181     
5182     getAutoCreate : function(){
5183         
5184         var cfg = {
5185             tag: 'div',
5186             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5187             cn: [
5188                 {
5189                     tag: 'a',
5190                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5191                 }
5192             ]
5193         };
5194         
5195         return cfg;
5196     }
5197    
5198 });
5199
5200  /*
5201  * Based on:
5202  * Ext JS Library 1.1.1
5203  * Copyright(c) 2006-2007, Ext JS, LLC.
5204  *
5205  * Originally Released Under LGPL - original licence link has changed is not relivant.
5206  *
5207  * Fork - LGPL
5208  * <script type="text/javascript">
5209  */
5210  
5211
5212 /**
5213  * @class Roo.grid.ColumnModel
5214  * @extends Roo.util.Observable
5215  * This is the default implementation of a ColumnModel used by the Grid. It defines
5216  * the columns in the grid.
5217  * <br>Usage:<br>
5218  <pre><code>
5219  var colModel = new Roo.grid.ColumnModel([
5220         {header: "Ticker", width: 60, sortable: true, locked: true},
5221         {header: "Company Name", width: 150, sortable: true},
5222         {header: "Market Cap.", width: 100, sortable: true},
5223         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5224         {header: "Employees", width: 100, sortable: true, resizable: false}
5225  ]);
5226  </code></pre>
5227  * <p>
5228  
5229  * The config options listed for this class are options which may appear in each
5230  * individual column definition.
5231  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5232  * @constructor
5233  * @param {Object} config An Array of column config objects. See this class's
5234  * config objects for details.
5235 */
5236 Roo.grid.ColumnModel = function(config){
5237         /**
5238      * The config passed into the constructor
5239      */
5240     this.config = config;
5241     this.lookup = {};
5242
5243     // if no id, create one
5244     // if the column does not have a dataIndex mapping,
5245     // map it to the order it is in the config
5246     for(var i = 0, len = config.length; i < len; i++){
5247         var c = config[i];
5248         if(typeof c.dataIndex == "undefined"){
5249             c.dataIndex = i;
5250         }
5251         if(typeof c.renderer == "string"){
5252             c.renderer = Roo.util.Format[c.renderer];
5253         }
5254         if(typeof c.id == "undefined"){
5255             c.id = Roo.id();
5256         }
5257         if(c.editor && c.editor.xtype){
5258             c.editor  = Roo.factory(c.editor, Roo.grid);
5259         }
5260         if(c.editor && c.editor.isFormField){
5261             c.editor = new Roo.grid.GridEditor(c.editor);
5262         }
5263         this.lookup[c.id] = c;
5264     }
5265
5266     /**
5267      * The width of columns which have no width specified (defaults to 100)
5268      * @type Number
5269      */
5270     this.defaultWidth = 100;
5271
5272     /**
5273      * Default sortable of columns which have no sortable specified (defaults to false)
5274      * @type Boolean
5275      */
5276     this.defaultSortable = false;
5277
5278     this.addEvents({
5279         /**
5280              * @event widthchange
5281              * Fires when the width of a column changes.
5282              * @param {ColumnModel} this
5283              * @param {Number} columnIndex The column index
5284              * @param {Number} newWidth The new width
5285              */
5286             "widthchange": true,
5287         /**
5288              * @event headerchange
5289              * Fires when the text of a header changes.
5290              * @param {ColumnModel} this
5291              * @param {Number} columnIndex The column index
5292              * @param {Number} newText The new header text
5293              */
5294             "headerchange": true,
5295         /**
5296              * @event hiddenchange
5297              * Fires when a column is hidden or "unhidden".
5298              * @param {ColumnModel} this
5299              * @param {Number} columnIndex The column index
5300              * @param {Boolean} hidden true if hidden, false otherwise
5301              */
5302             "hiddenchange": true,
5303             /**
5304          * @event columnmoved
5305          * Fires when a column is moved.
5306          * @param {ColumnModel} this
5307          * @param {Number} oldIndex
5308          * @param {Number} newIndex
5309          */
5310         "columnmoved" : true,
5311         /**
5312          * @event columlockchange
5313          * Fires when a column's locked state is changed
5314          * @param {ColumnModel} this
5315          * @param {Number} colIndex
5316          * @param {Boolean} locked true if locked
5317          */
5318         "columnlockchange" : true
5319     });
5320     Roo.grid.ColumnModel.superclass.constructor.call(this);
5321 };
5322 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5323     /**
5324      * @cfg {String} header The header text to display in the Grid view.
5325      */
5326     /**
5327      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5328      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5329      * specified, the column's index is used as an index into the Record's data Array.
5330      */
5331     /**
5332      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5333      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5334      */
5335     /**
5336      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5337      * Defaults to the value of the {@link #defaultSortable} property.
5338      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5339      */
5340     /**
5341      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5342      */
5343     /**
5344      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5345      */
5346     /**
5347      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5348      */
5349     /**
5350      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5351      */
5352     /**
5353      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5354      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5355      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5356      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5357      */
5358        /**
5359      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5360      */
5361     /**
5362      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5363      */
5364     /**
5365      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5366      */
5367     /**
5368      * @cfg {String} cursor (Optional)
5369      */
5370     /**
5371      * @cfg {String} tooltip (Optional)
5372      */
5373     /**
5374      * @cfg {Number} xs (Optional)
5375      */
5376     /**
5377      * @cfg {Number} sm (Optional)
5378      */
5379     /**
5380      * @cfg {Number} md (Optional)
5381      */
5382     /**
5383      * @cfg {Number} lg (Optional)
5384      */
5385     /**
5386      * Returns the id of the column at the specified index.
5387      * @param {Number} index The column index
5388      * @return {String} the id
5389      */
5390     getColumnId : function(index){
5391         return this.config[index].id;
5392     },
5393
5394     /**
5395      * Returns the column for a specified id.
5396      * @param {String} id The column id
5397      * @return {Object} the column
5398      */
5399     getColumnById : function(id){
5400         return this.lookup[id];
5401     },
5402
5403     
5404     /**
5405      * Returns the column for a specified dataIndex.
5406      * @param {String} dataIndex The column dataIndex
5407      * @return {Object|Boolean} the column or false if not found
5408      */
5409     getColumnByDataIndex: function(dataIndex){
5410         var index = this.findColumnIndex(dataIndex);
5411         return index > -1 ? this.config[index] : false;
5412     },
5413     
5414     /**
5415      * Returns the index for a specified column id.
5416      * @param {String} id The column id
5417      * @return {Number} the index, or -1 if not found
5418      */
5419     getIndexById : function(id){
5420         for(var i = 0, len = this.config.length; i < len; i++){
5421             if(this.config[i].id == id){
5422                 return i;
5423             }
5424         }
5425         return -1;
5426     },
5427     
5428     /**
5429      * Returns the index for a specified column dataIndex.
5430      * @param {String} dataIndex The column dataIndex
5431      * @return {Number} the index, or -1 if not found
5432      */
5433     
5434     findColumnIndex : function(dataIndex){
5435         for(var i = 0, len = this.config.length; i < len; i++){
5436             if(this.config[i].dataIndex == dataIndex){
5437                 return i;
5438             }
5439         }
5440         return -1;
5441     },
5442     
5443     
5444     moveColumn : function(oldIndex, newIndex){
5445         var c = this.config[oldIndex];
5446         this.config.splice(oldIndex, 1);
5447         this.config.splice(newIndex, 0, c);
5448         this.dataMap = null;
5449         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5450     },
5451
5452     isLocked : function(colIndex){
5453         return this.config[colIndex].locked === true;
5454     },
5455
5456     setLocked : function(colIndex, value, suppressEvent){
5457         if(this.isLocked(colIndex) == value){
5458             return;
5459         }
5460         this.config[colIndex].locked = value;
5461         if(!suppressEvent){
5462             this.fireEvent("columnlockchange", this, colIndex, value);
5463         }
5464     },
5465
5466     getTotalLockedWidth : function(){
5467         var totalWidth = 0;
5468         for(var i = 0; i < this.config.length; i++){
5469             if(this.isLocked(i) && !this.isHidden(i)){
5470                 this.totalWidth += this.getColumnWidth(i);
5471             }
5472         }
5473         return totalWidth;
5474     },
5475
5476     getLockedCount : function(){
5477         for(var i = 0, len = this.config.length; i < len; i++){
5478             if(!this.isLocked(i)){
5479                 return i;
5480             }
5481         }
5482         
5483         return this.config.length;
5484     },
5485
5486     /**
5487      * Returns the number of columns.
5488      * @return {Number}
5489      */
5490     getColumnCount : function(visibleOnly){
5491         if(visibleOnly === true){
5492             var c = 0;
5493             for(var i = 0, len = this.config.length; i < len; i++){
5494                 if(!this.isHidden(i)){
5495                     c++;
5496                 }
5497             }
5498             return c;
5499         }
5500         return this.config.length;
5501     },
5502
5503     /**
5504      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5505      * @param {Function} fn
5506      * @param {Object} scope (optional)
5507      * @return {Array} result
5508      */
5509     getColumnsBy : function(fn, scope){
5510         var r = [];
5511         for(var i = 0, len = this.config.length; i < len; i++){
5512             var c = this.config[i];
5513             if(fn.call(scope||this, c, i) === true){
5514                 r[r.length] = c;
5515             }
5516         }
5517         return r;
5518     },
5519
5520     /**
5521      * Returns true if the specified column is sortable.
5522      * @param {Number} col The column index
5523      * @return {Boolean}
5524      */
5525     isSortable : function(col){
5526         if(typeof this.config[col].sortable == "undefined"){
5527             return this.defaultSortable;
5528         }
5529         return this.config[col].sortable;
5530     },
5531
5532     /**
5533      * Returns the rendering (formatting) function defined for the column.
5534      * @param {Number} col The column index.
5535      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5536      */
5537     getRenderer : function(col){
5538         if(!this.config[col].renderer){
5539             return Roo.grid.ColumnModel.defaultRenderer;
5540         }
5541         return this.config[col].renderer;
5542     },
5543
5544     /**
5545      * Sets the rendering (formatting) function for a column.
5546      * @param {Number} col The column index
5547      * @param {Function} fn The function to use to process the cell's raw data
5548      * to return HTML markup for the grid view. The render function is called with
5549      * the following parameters:<ul>
5550      * <li>Data value.</li>
5551      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5552      * <li>css A CSS style string to apply to the table cell.</li>
5553      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5554      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5555      * <li>Row index</li>
5556      * <li>Column index</li>
5557      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5558      */
5559     setRenderer : function(col, fn){
5560         this.config[col].renderer = fn;
5561     },
5562
5563     /**
5564      * Returns the width for the specified column.
5565      * @param {Number} col The column index
5566      * @return {Number}
5567      */
5568     getColumnWidth : function(col){
5569         return this.config[col].width * 1 || this.defaultWidth;
5570     },
5571
5572     /**
5573      * Sets the width for a column.
5574      * @param {Number} col The column index
5575      * @param {Number} width The new width
5576      */
5577     setColumnWidth : function(col, width, suppressEvent){
5578         this.config[col].width = width;
5579         this.totalWidth = null;
5580         if(!suppressEvent){
5581              this.fireEvent("widthchange", this, col, width);
5582         }
5583     },
5584
5585     /**
5586      * Returns the total width of all columns.
5587      * @param {Boolean} includeHidden True to include hidden column widths
5588      * @return {Number}
5589      */
5590     getTotalWidth : function(includeHidden){
5591         if(!this.totalWidth){
5592             this.totalWidth = 0;
5593             for(var i = 0, len = this.config.length; i < len; i++){
5594                 if(includeHidden || !this.isHidden(i)){
5595                     this.totalWidth += this.getColumnWidth(i);
5596                 }
5597             }
5598         }
5599         return this.totalWidth;
5600     },
5601
5602     /**
5603      * Returns the header for the specified column.
5604      * @param {Number} col The column index
5605      * @return {String}
5606      */
5607     getColumnHeader : function(col){
5608         return this.config[col].header;
5609     },
5610
5611     /**
5612      * Sets the header for a column.
5613      * @param {Number} col The column index
5614      * @param {String} header The new header
5615      */
5616     setColumnHeader : function(col, header){
5617         this.config[col].header = header;
5618         this.fireEvent("headerchange", this, col, header);
5619     },
5620
5621     /**
5622      * Returns the tooltip for the specified column.
5623      * @param {Number} col The column index
5624      * @return {String}
5625      */
5626     getColumnTooltip : function(col){
5627             return this.config[col].tooltip;
5628     },
5629     /**
5630      * Sets the tooltip for a column.
5631      * @param {Number} col The column index
5632      * @param {String} tooltip The new tooltip
5633      */
5634     setColumnTooltip : function(col, tooltip){
5635             this.config[col].tooltip = tooltip;
5636     },
5637
5638     /**
5639      * Returns the dataIndex for the specified column.
5640      * @param {Number} col The column index
5641      * @return {Number}
5642      */
5643     getDataIndex : function(col){
5644         return this.config[col].dataIndex;
5645     },
5646
5647     /**
5648      * Sets the dataIndex for a column.
5649      * @param {Number} col The column index
5650      * @param {Number} dataIndex The new dataIndex
5651      */
5652     setDataIndex : function(col, dataIndex){
5653         this.config[col].dataIndex = dataIndex;
5654     },
5655
5656     
5657     
5658     /**
5659      * Returns true if the cell is editable.
5660      * @param {Number} colIndex The column index
5661      * @param {Number} rowIndex The row index - this is nto actually used..?
5662      * @return {Boolean}
5663      */
5664     isCellEditable : function(colIndex, rowIndex){
5665         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5666     },
5667
5668     /**
5669      * Returns the editor defined for the cell/column.
5670      * return false or null to disable editing.
5671      * @param {Number} colIndex The column index
5672      * @param {Number} rowIndex The row index
5673      * @return {Object}
5674      */
5675     getCellEditor : function(colIndex, rowIndex){
5676         return this.config[colIndex].editor;
5677     },
5678
5679     /**
5680      * Sets if a column is editable.
5681      * @param {Number} col The column index
5682      * @param {Boolean} editable True if the column is editable
5683      */
5684     setEditable : function(col, editable){
5685         this.config[col].editable = editable;
5686     },
5687
5688
5689     /**
5690      * Returns true if the column is hidden.
5691      * @param {Number} colIndex The column index
5692      * @return {Boolean}
5693      */
5694     isHidden : function(colIndex){
5695         return this.config[colIndex].hidden;
5696     },
5697
5698
5699     /**
5700      * Returns true if the column width cannot be changed
5701      */
5702     isFixed : function(colIndex){
5703         return this.config[colIndex].fixed;
5704     },
5705
5706     /**
5707      * Returns true if the column can be resized
5708      * @return {Boolean}
5709      */
5710     isResizable : function(colIndex){
5711         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5712     },
5713     /**
5714      * Sets if a column is hidden.
5715      * @param {Number} colIndex The column index
5716      * @param {Boolean} hidden True if the column is hidden
5717      */
5718     setHidden : function(colIndex, hidden){
5719         this.config[colIndex].hidden = hidden;
5720         this.totalWidth = null;
5721         this.fireEvent("hiddenchange", this, colIndex, hidden);
5722     },
5723
5724     /**
5725      * Sets the editor for a column.
5726      * @param {Number} col The column index
5727      * @param {Object} editor The editor object
5728      */
5729     setEditor : function(col, editor){
5730         this.config[col].editor = editor;
5731     }
5732 });
5733
5734 Roo.grid.ColumnModel.defaultRenderer = function(value)
5735 {
5736     if(typeof value == "object") {
5737         return value;
5738     }
5739         if(typeof value == "string" && value.length < 1){
5740             return "&#160;";
5741         }
5742     
5743         return String.format("{0}", value);
5744 };
5745
5746 // Alias for backwards compatibility
5747 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5748 /*
5749  * Based on:
5750  * Ext JS Library 1.1.1
5751  * Copyright(c) 2006-2007, Ext JS, LLC.
5752  *
5753  * Originally Released Under LGPL - original licence link has changed is not relivant.
5754  *
5755  * Fork - LGPL
5756  * <script type="text/javascript">
5757  */
5758  
5759 /**
5760  * @class Roo.LoadMask
5761  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5762  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5763  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5764  * element's UpdateManager load indicator and will be destroyed after the initial load.
5765  * @constructor
5766  * Create a new LoadMask
5767  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5768  * @param {Object} config The config object
5769  */
5770 Roo.LoadMask = function(el, config){
5771     this.el = Roo.get(el);
5772     Roo.apply(this, config);
5773     if(this.store){
5774         this.store.on('beforeload', this.onBeforeLoad, this);
5775         this.store.on('load', this.onLoad, this);
5776         this.store.on('loadexception', this.onLoadException, this);
5777         this.removeMask = false;
5778     }else{
5779         var um = this.el.getUpdateManager();
5780         um.showLoadIndicator = false; // disable the default indicator
5781         um.on('beforeupdate', this.onBeforeLoad, this);
5782         um.on('update', this.onLoad, this);
5783         um.on('failure', this.onLoad, this);
5784         this.removeMask = true;
5785     }
5786 };
5787
5788 Roo.LoadMask.prototype = {
5789     /**
5790      * @cfg {Boolean} removeMask
5791      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5792      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5793      */
5794     /**
5795      * @cfg {String} msg
5796      * The text to display in a centered loading message box (defaults to 'Loading...')
5797      */
5798     msg : 'Loading...',
5799     /**
5800      * @cfg {String} msgCls
5801      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5802      */
5803     msgCls : 'x-mask-loading',
5804
5805     /**
5806      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5807      * @type Boolean
5808      */
5809     disabled: false,
5810
5811     /**
5812      * Disables the mask to prevent it from being displayed
5813      */
5814     disable : function(){
5815        this.disabled = true;
5816     },
5817
5818     /**
5819      * Enables the mask so that it can be displayed
5820      */
5821     enable : function(){
5822         this.disabled = false;
5823     },
5824     
5825     onLoadException : function()
5826     {
5827         Roo.log(arguments);
5828         
5829         if (typeof(arguments[3]) != 'undefined') {
5830             Roo.MessageBox.alert("Error loading",arguments[3]);
5831         } 
5832         /*
5833         try {
5834             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5835                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5836             }   
5837         } catch(e) {
5838             
5839         }
5840         */
5841     
5842         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5843     },
5844     // private
5845     onLoad : function()
5846     {
5847         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5848     },
5849
5850     // private
5851     onBeforeLoad : function(){
5852         if(!this.disabled){
5853             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5854         }
5855     },
5856
5857     // private
5858     destroy : function(){
5859         if(this.store){
5860             this.store.un('beforeload', this.onBeforeLoad, this);
5861             this.store.un('load', this.onLoad, this);
5862             this.store.un('loadexception', this.onLoadException, this);
5863         }else{
5864             var um = this.el.getUpdateManager();
5865             um.un('beforeupdate', this.onBeforeLoad, this);
5866             um.un('update', this.onLoad, this);
5867             um.un('failure', this.onLoad, this);
5868         }
5869     }
5870 };/*
5871  * - LGPL
5872  *
5873  * table
5874  * 
5875  */
5876
5877 /**
5878  * @class Roo.bootstrap.Table
5879  * @extends Roo.bootstrap.Component
5880  * Bootstrap Table class
5881  * @cfg {String} cls table class
5882  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5883  * @cfg {String} bgcolor Specifies the background color for a table
5884  * @cfg {Number} border Specifies whether the table cells should have borders or not
5885  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5886  * @cfg {Number} cellspacing Specifies the space between cells
5887  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5888  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5889  * @cfg {String} sortable Specifies that the table should be sortable
5890  * @cfg {String} summary Specifies a summary of the content of a table
5891  * @cfg {Number} width Specifies the width of a table
5892  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5893  * 
5894  * @cfg {boolean} striped Should the rows be alternative striped
5895  * @cfg {boolean} bordered Add borders to the table
5896  * @cfg {boolean} hover Add hover highlighting
5897  * @cfg {boolean} condensed Format condensed
5898  * @cfg {boolean} responsive Format condensed
5899  * @cfg {Boolean} loadMask (true|false) default false
5900  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5901  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5902  * @cfg {Boolean} rowSelection (true|false) default false
5903  * @cfg {Boolean} cellSelection (true|false) default false
5904  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5905  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5906  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5907  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
5908  
5909  * 
5910  * @constructor
5911  * Create a new Table
5912  * @param {Object} config The config object
5913  */
5914
5915 Roo.bootstrap.Table = function(config){
5916     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5917     
5918   
5919     
5920     // BC...
5921     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5922     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5923     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5924     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5925     
5926     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5927     if (this.sm) {
5928         this.sm.grid = this;
5929         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5930         this.sm = this.selModel;
5931         this.sm.xmodule = this.xmodule || false;
5932     }
5933     
5934     if (this.cm && typeof(this.cm.config) == 'undefined') {
5935         this.colModel = new Roo.grid.ColumnModel(this.cm);
5936         this.cm = this.colModel;
5937         this.cm.xmodule = this.xmodule || false;
5938     }
5939     if (this.store) {
5940         this.store= Roo.factory(this.store, Roo.data);
5941         this.ds = this.store;
5942         this.ds.xmodule = this.xmodule || false;
5943          
5944     }
5945     if (this.footer && this.store) {
5946         this.footer.dataSource = this.ds;
5947         this.footer = Roo.factory(this.footer);
5948     }
5949     
5950     /** @private */
5951     this.addEvents({
5952         /**
5953          * @event cellclick
5954          * Fires when a cell is clicked
5955          * @param {Roo.bootstrap.Table} this
5956          * @param {Roo.Element} el
5957          * @param {Number} rowIndex
5958          * @param {Number} columnIndex
5959          * @param {Roo.EventObject} e
5960          */
5961         "cellclick" : true,
5962         /**
5963          * @event celldblclick
5964          * Fires when a cell is double clicked
5965          * @param {Roo.bootstrap.Table} this
5966          * @param {Roo.Element} el
5967          * @param {Number} rowIndex
5968          * @param {Number} columnIndex
5969          * @param {Roo.EventObject} e
5970          */
5971         "celldblclick" : true,
5972         /**
5973          * @event rowclick
5974          * Fires when a row is clicked
5975          * @param {Roo.bootstrap.Table} this
5976          * @param {Roo.Element} el
5977          * @param {Number} rowIndex
5978          * @param {Roo.EventObject} e
5979          */
5980         "rowclick" : true,
5981         /**
5982          * @event rowdblclick
5983          * Fires when a row is double clicked
5984          * @param {Roo.bootstrap.Table} this
5985          * @param {Roo.Element} el
5986          * @param {Number} rowIndex
5987          * @param {Roo.EventObject} e
5988          */
5989         "rowdblclick" : true,
5990         /**
5991          * @event mouseover
5992          * Fires when a mouseover occur
5993          * @param {Roo.bootstrap.Table} this
5994          * @param {Roo.Element} el
5995          * @param {Number} rowIndex
5996          * @param {Number} columnIndex
5997          * @param {Roo.EventObject} e
5998          */
5999         "mouseover" : true,
6000         /**
6001          * @event mouseout
6002          * Fires when a mouseout occur
6003          * @param {Roo.bootstrap.Table} this
6004          * @param {Roo.Element} el
6005          * @param {Number} rowIndex
6006          * @param {Number} columnIndex
6007          * @param {Roo.EventObject} e
6008          */
6009         "mouseout" : true,
6010         /**
6011          * @event rowclass
6012          * Fires when a row is rendered, so you can change add a style to it.
6013          * @param {Roo.bootstrap.Table} this
6014          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6015          */
6016         'rowclass' : true,
6017           /**
6018          * @event rowsrendered
6019          * Fires when all the  rows have been rendered
6020          * @param {Roo.bootstrap.Table} this
6021          */
6022         'rowsrendered' : true,
6023         /**
6024          * @event contextmenu
6025          * The raw contextmenu event for the entire grid.
6026          * @param {Roo.EventObject} e
6027          */
6028         "contextmenu" : true,
6029         /**
6030          * @event rowcontextmenu
6031          * Fires when a row is right clicked
6032          * @param {Roo.bootstrap.Table} this
6033          * @param {Number} rowIndex
6034          * @param {Roo.EventObject} e
6035          */
6036         "rowcontextmenu" : true,
6037         /**
6038          * @event cellcontextmenu
6039          * Fires when a cell is right clicked
6040          * @param {Roo.bootstrap.Table} this
6041          * @param {Number} rowIndex
6042          * @param {Number} cellIndex
6043          * @param {Roo.EventObject} e
6044          */
6045          "cellcontextmenu" : true,
6046          /**
6047          * @event headercontextmenu
6048          * Fires when a header is right clicked
6049          * @param {Roo.bootstrap.Table} this
6050          * @param {Number} columnIndex
6051          * @param {Roo.EventObject} e
6052          */
6053         "headercontextmenu" : true
6054     });
6055 };
6056
6057 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6058     
6059     cls: false,
6060     align: false,
6061     bgcolor: false,
6062     border: false,
6063     cellpadding: false,
6064     cellspacing: false,
6065     frame: false,
6066     rules: false,
6067     sortable: false,
6068     summary: false,
6069     width: false,
6070     striped : false,
6071     scrollBody : false,
6072     bordered: false,
6073     hover:  false,
6074     condensed : false,
6075     responsive : false,
6076     sm : false,
6077     cm : false,
6078     store : false,
6079     loadMask : false,
6080     footerShow : true,
6081     headerShow : true,
6082   
6083     rowSelection : false,
6084     cellSelection : false,
6085     layout : false,
6086     
6087     // Roo.Element - the tbody
6088     mainBody: false,
6089     // Roo.Element - thead element
6090     mainHead: false,
6091     
6092     container: false, // used by gridpanel...
6093     
6094     lazyLoad : false,
6095     
6096     CSS : Roo.util.CSS,
6097     
6098     auto_hide_footer : false,
6099     
6100     getAutoCreate : function()
6101     {
6102         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6103         
6104         cfg = {
6105             tag: 'table',
6106             cls : 'table',
6107             cn : []
6108         };
6109         if (this.scrollBody) {
6110             cfg.cls += ' table-body-fixed';
6111         }    
6112         if (this.striped) {
6113             cfg.cls += ' table-striped';
6114         }
6115         
6116         if (this.hover) {
6117             cfg.cls += ' table-hover';
6118         }
6119         if (this.bordered) {
6120             cfg.cls += ' table-bordered';
6121         }
6122         if (this.condensed) {
6123             cfg.cls += ' table-condensed';
6124         }
6125         if (this.responsive) {
6126             cfg.cls += ' table-responsive';
6127         }
6128         
6129         if (this.cls) {
6130             cfg.cls+=  ' ' +this.cls;
6131         }
6132         
6133         // this lot should be simplifed...
6134         var _t = this;
6135         var cp = [
6136             'align',
6137             'bgcolor',
6138             'border',
6139             'cellpadding',
6140             'cellspacing',
6141             'frame',
6142             'rules',
6143             'sortable',
6144             'summary',
6145             'width'
6146         ].forEach(function(k) {
6147             if (_t[k]) {
6148                 cfg[k] = _t[k];
6149             }
6150         });
6151         
6152         
6153         if (this.layout) {
6154             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6155         }
6156         
6157         if(this.store || this.cm){
6158             if(this.headerShow){
6159                 cfg.cn.push(this.renderHeader());
6160             }
6161             
6162             cfg.cn.push(this.renderBody());
6163             
6164             if(this.footerShow){
6165                 cfg.cn.push(this.renderFooter());
6166             }
6167             // where does this come from?
6168             //cfg.cls+=  ' TableGrid';
6169         }
6170         
6171         return { cn : [ cfg ] };
6172     },
6173     
6174     initEvents : function()
6175     {   
6176         if(!this.store || !this.cm){
6177             return;
6178         }
6179         if (this.selModel) {
6180             this.selModel.initEvents();
6181         }
6182         
6183         
6184         //Roo.log('initEvents with ds!!!!');
6185         
6186         this.mainBody = this.el.select('tbody', true).first();
6187         this.mainHead = this.el.select('thead', true).first();
6188         this.mainFoot = this.el.select('tfoot', true).first();
6189         
6190         
6191         
6192         var _this = this;
6193         
6194         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6195             e.on('click', _this.sort, _this);
6196         });
6197         
6198         this.mainBody.on("click", this.onClick, this);
6199         this.mainBody.on("dblclick", this.onDblClick, this);
6200         
6201         // why is this done????? = it breaks dialogs??
6202         //this.parent().el.setStyle('position', 'relative');
6203         
6204         
6205         if (this.footer) {
6206             this.footer.parentId = this.id;
6207             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6208             
6209             if(this.lazyLoad){
6210                 this.el.select('tfoot tr td').first().addClass('hide');
6211             }
6212         } 
6213         
6214         if(this.loadMask) {
6215             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6216         }
6217         
6218         this.store.on('load', this.onLoad, this);
6219         this.store.on('beforeload', this.onBeforeLoad, this);
6220         this.store.on('update', this.onUpdate, this);
6221         this.store.on('add', this.onAdd, this);
6222         this.store.on("clear", this.clear, this);
6223         
6224         this.el.on("contextmenu", this.onContextMenu, this);
6225         
6226         this.mainBody.on('scroll', this.onBodyScroll, this);
6227         
6228         this.cm.on("headerchange", this.onHeaderChange, this);
6229         
6230         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6231         
6232     },
6233     
6234     onContextMenu : function(e, t)
6235     {
6236         this.processEvent("contextmenu", e);
6237     },
6238     
6239     processEvent : function(name, e)
6240     {
6241         if (name != 'touchstart' ) {
6242             this.fireEvent(name, e);    
6243         }
6244         
6245         var t = e.getTarget();
6246         
6247         var cell = Roo.get(t);
6248         
6249         if(!cell){
6250             return;
6251         }
6252         
6253         if(cell.findParent('tfoot', false, true)){
6254             return;
6255         }
6256         
6257         if(cell.findParent('thead', false, true)){
6258             
6259             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6260                 cell = Roo.get(t).findParent('th', false, true);
6261                 if (!cell) {
6262                     Roo.log("failed to find th in thead?");
6263                     Roo.log(e.getTarget());
6264                     return;
6265                 }
6266             }
6267             
6268             var cellIndex = cell.dom.cellIndex;
6269             
6270             var ename = name == 'touchstart' ? 'click' : name;
6271             this.fireEvent("header" + ename, this, cellIndex, e);
6272             
6273             return;
6274         }
6275         
6276         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6277             cell = Roo.get(t).findParent('td', false, true);
6278             if (!cell) {
6279                 Roo.log("failed to find th in tbody?");
6280                 Roo.log(e.getTarget());
6281                 return;
6282             }
6283         }
6284         
6285         var row = cell.findParent('tr', false, true);
6286         var cellIndex = cell.dom.cellIndex;
6287         var rowIndex = row.dom.rowIndex - 1;
6288         
6289         if(row !== false){
6290             
6291             this.fireEvent("row" + name, this, rowIndex, e);
6292             
6293             if(cell !== false){
6294             
6295                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6296             }
6297         }
6298         
6299     },
6300     
6301     onMouseover : function(e, el)
6302     {
6303         var cell = Roo.get(el);
6304         
6305         if(!cell){
6306             return;
6307         }
6308         
6309         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6310             cell = cell.findParent('td', false, true);
6311         }
6312         
6313         var row = cell.findParent('tr', false, true);
6314         var cellIndex = cell.dom.cellIndex;
6315         var rowIndex = row.dom.rowIndex - 1; // start from 0
6316         
6317         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6318         
6319     },
6320     
6321     onMouseout : function(e, el)
6322     {
6323         var cell = Roo.get(el);
6324         
6325         if(!cell){
6326             return;
6327         }
6328         
6329         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6330             cell = cell.findParent('td', false, true);
6331         }
6332         
6333         var row = cell.findParent('tr', false, true);
6334         var cellIndex = cell.dom.cellIndex;
6335         var rowIndex = row.dom.rowIndex - 1; // start from 0
6336         
6337         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6338         
6339     },
6340     
6341     onClick : function(e, el)
6342     {
6343         var cell = Roo.get(el);
6344         
6345         if(!cell || (!this.cellSelection && !this.rowSelection)){
6346             return;
6347         }
6348         
6349         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6350             cell = cell.findParent('td', false, true);
6351         }
6352         
6353         if(!cell || typeof(cell) == 'undefined'){
6354             return;
6355         }
6356         
6357         var row = cell.findParent('tr', false, true);
6358         
6359         if(!row || typeof(row) == 'undefined'){
6360             return;
6361         }
6362         
6363         var cellIndex = cell.dom.cellIndex;
6364         var rowIndex = this.getRowIndex(row);
6365         
6366         // why??? - should these not be based on SelectionModel?
6367         if(this.cellSelection){
6368             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6369         }
6370         
6371         if(this.rowSelection){
6372             this.fireEvent('rowclick', this, row, rowIndex, e);
6373         }
6374         
6375         
6376     },
6377         
6378     onDblClick : function(e,el)
6379     {
6380         var cell = Roo.get(el);
6381         
6382         if(!cell || (!this.cellSelection && !this.rowSelection)){
6383             return;
6384         }
6385         
6386         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6387             cell = cell.findParent('td', false, true);
6388         }
6389         
6390         if(!cell || typeof(cell) == 'undefined'){
6391             return;
6392         }
6393         
6394         var row = cell.findParent('tr', false, true);
6395         
6396         if(!row || typeof(row) == 'undefined'){
6397             return;
6398         }
6399         
6400         var cellIndex = cell.dom.cellIndex;
6401         var rowIndex = this.getRowIndex(row);
6402         
6403         if(this.cellSelection){
6404             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6405         }
6406         
6407         if(this.rowSelection){
6408             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6409         }
6410     },
6411     
6412     sort : function(e,el)
6413     {
6414         var col = Roo.get(el);
6415         
6416         if(!col.hasClass('sortable')){
6417             return;
6418         }
6419         
6420         var sort = col.attr('sort');
6421         var dir = 'ASC';
6422         
6423         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6424             dir = 'DESC';
6425         }
6426         
6427         this.store.sortInfo = {field : sort, direction : dir};
6428         
6429         if (this.footer) {
6430             Roo.log("calling footer first");
6431             this.footer.onClick('first');
6432         } else {
6433         
6434             this.store.load({ params : { start : 0 } });
6435         }
6436     },
6437     
6438     renderHeader : function()
6439     {
6440         var header = {
6441             tag: 'thead',
6442             cn : []
6443         };
6444         
6445         var cm = this.cm;
6446         this.totalWidth = 0;
6447         
6448         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6449             
6450             var config = cm.config[i];
6451             
6452             var c = {
6453                 tag: 'th',
6454                 cls : 'x-hcol-' + i,
6455                 style : '',
6456                 html: cm.getColumnHeader(i)
6457             };
6458             
6459             var hh = '';
6460             
6461             if(typeof(config.sortable) != 'undefined' && config.sortable){
6462                 c.cls = 'sortable';
6463                 c.html = '<i class="glyphicon"></i>' + c.html;
6464             }
6465             
6466             if(typeof(config.lgHeader) != 'undefined'){
6467                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6468             }
6469             
6470             if(typeof(config.mdHeader) != 'undefined'){
6471                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6472             }
6473             
6474             if(typeof(config.smHeader) != 'undefined'){
6475                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6476             }
6477             
6478             if(typeof(config.xsHeader) != 'undefined'){
6479                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6480             }
6481             
6482             if(hh.length){
6483                 c.html = hh;
6484             }
6485             
6486             if(typeof(config.tooltip) != 'undefined'){
6487                 c.tooltip = config.tooltip;
6488             }
6489             
6490             if(typeof(config.colspan) != 'undefined'){
6491                 c.colspan = config.colspan;
6492             }
6493             
6494             if(typeof(config.hidden) != 'undefined' && config.hidden){
6495                 c.style += ' display:none;';
6496             }
6497             
6498             if(typeof(config.dataIndex) != 'undefined'){
6499                 c.sort = config.dataIndex;
6500             }
6501             
6502            
6503             
6504             if(typeof(config.align) != 'undefined' && config.align.length){
6505                 c.style += ' text-align:' + config.align + ';';
6506             }
6507             
6508             if(typeof(config.width) != 'undefined'){
6509                 c.style += ' width:' + config.width + 'px;';
6510                 this.totalWidth += config.width;
6511             } else {
6512                 this.totalWidth += 100; // assume minimum of 100 per column?
6513             }
6514             
6515             if(typeof(config.cls) != 'undefined'){
6516                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6517             }
6518             
6519             ['xs','sm','md','lg'].map(function(size){
6520                 
6521                 if(typeof(config[size]) == 'undefined'){
6522                     return;
6523                 }
6524                 
6525                 if (!config[size]) { // 0 = hidden
6526                     c.cls += ' hidden-' + size;
6527                     return;
6528                 }
6529                 
6530                 c.cls += ' col-' + size + '-' + config[size];
6531
6532             });
6533             
6534             header.cn.push(c)
6535         }
6536         
6537         return header;
6538     },
6539     
6540     renderBody : function()
6541     {
6542         var body = {
6543             tag: 'tbody',
6544             cn : [
6545                 {
6546                     tag: 'tr',
6547                     cn : [
6548                         {
6549                             tag : 'td',
6550                             colspan :  this.cm.getColumnCount()
6551                         }
6552                     ]
6553                 }
6554             ]
6555         };
6556         
6557         return body;
6558     },
6559     
6560     renderFooter : function()
6561     {
6562         var footer = {
6563             tag: 'tfoot',
6564             cn : [
6565                 {
6566                     tag: 'tr',
6567                     cn : [
6568                         {
6569                             tag : 'td',
6570                             colspan :  this.cm.getColumnCount()
6571                         }
6572                     ]
6573                 }
6574             ]
6575         };
6576         
6577         return footer;
6578     },
6579     
6580     
6581     
6582     onLoad : function()
6583     {
6584 //        Roo.log('ds onload');
6585         this.clear();
6586         
6587         var _this = this;
6588         var cm = this.cm;
6589         var ds = this.store;
6590         
6591         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6592             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6593             if (_this.store.sortInfo) {
6594                     
6595                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6596                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6597                 }
6598                 
6599                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6600                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6601                 }
6602             }
6603         });
6604         
6605         var tbody =  this.mainBody;
6606               
6607         if(ds.getCount() > 0){
6608             ds.data.each(function(d,rowIndex){
6609                 var row =  this.renderRow(cm, ds, rowIndex);
6610                 
6611                 tbody.createChild(row);
6612                 
6613                 var _this = this;
6614                 
6615                 if(row.cellObjects.length){
6616                     Roo.each(row.cellObjects, function(r){
6617                         _this.renderCellObject(r);
6618                     })
6619                 }
6620                 
6621             }, this);
6622         }
6623         
6624         var tfoot = this.el.select('tfoot', true).first();
6625         
6626         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6627             
6628             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6629             
6630             var total = this.ds.getTotalCount();
6631             
6632             if(this.footer.pageSize < total){
6633                 this.mainFoot.show();
6634             }
6635         }
6636         
6637         Roo.each(this.el.select('tbody td', true).elements, function(e){
6638             e.on('mouseover', _this.onMouseover, _this);
6639         });
6640         
6641         Roo.each(this.el.select('tbody td', true).elements, function(e){
6642             e.on('mouseout', _this.onMouseout, _this);
6643         });
6644         this.fireEvent('rowsrendered', this);
6645         
6646         this.autoSize();
6647     },
6648     
6649     
6650     onUpdate : function(ds,record)
6651     {
6652         this.refreshRow(record);
6653         this.autoSize();
6654     },
6655     
6656     onRemove : function(ds, record, index, isUpdate){
6657         if(isUpdate !== true){
6658             this.fireEvent("beforerowremoved", this, index, record);
6659         }
6660         var bt = this.mainBody.dom;
6661         
6662         var rows = this.el.select('tbody > tr', true).elements;
6663         
6664         if(typeof(rows[index]) != 'undefined'){
6665             bt.removeChild(rows[index].dom);
6666         }
6667         
6668 //        if(bt.rows[index]){
6669 //            bt.removeChild(bt.rows[index]);
6670 //        }
6671         
6672         if(isUpdate !== true){
6673             //this.stripeRows(index);
6674             //this.syncRowHeights(index, index);
6675             //this.layout();
6676             this.fireEvent("rowremoved", this, index, record);
6677         }
6678     },
6679     
6680     onAdd : function(ds, records, rowIndex)
6681     {
6682         //Roo.log('on Add called');
6683         // - note this does not handle multiple adding very well..
6684         var bt = this.mainBody.dom;
6685         for (var i =0 ; i < records.length;i++) {
6686             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6687             //Roo.log(records[i]);
6688             //Roo.log(this.store.getAt(rowIndex+i));
6689             this.insertRow(this.store, rowIndex + i, false);
6690             return;
6691         }
6692         
6693     },
6694     
6695     
6696     refreshRow : function(record){
6697         var ds = this.store, index;
6698         if(typeof record == 'number'){
6699             index = record;
6700             record = ds.getAt(index);
6701         }else{
6702             index = ds.indexOf(record);
6703         }
6704         this.insertRow(ds, index, true);
6705         this.autoSize();
6706         this.onRemove(ds, record, index+1, true);
6707         this.autoSize();
6708         //this.syncRowHeights(index, index);
6709         //this.layout();
6710         this.fireEvent("rowupdated", this, index, record);
6711     },
6712     
6713     insertRow : function(dm, rowIndex, isUpdate){
6714         
6715         if(!isUpdate){
6716             this.fireEvent("beforerowsinserted", this, rowIndex);
6717         }
6718             //var s = this.getScrollState();
6719         var row = this.renderRow(this.cm, this.store, rowIndex);
6720         // insert before rowIndex..
6721         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6722         
6723         var _this = this;
6724                 
6725         if(row.cellObjects.length){
6726             Roo.each(row.cellObjects, function(r){
6727                 _this.renderCellObject(r);
6728             })
6729         }
6730             
6731         if(!isUpdate){
6732             this.fireEvent("rowsinserted", this, rowIndex);
6733             //this.syncRowHeights(firstRow, lastRow);
6734             //this.stripeRows(firstRow);
6735             //this.layout();
6736         }
6737         
6738     },
6739     
6740     
6741     getRowDom : function(rowIndex)
6742     {
6743         var rows = this.el.select('tbody > tr', true).elements;
6744         
6745         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6746         
6747     },
6748     // returns the object tree for a tr..
6749   
6750     
6751     renderRow : function(cm, ds, rowIndex) 
6752     {
6753         var d = ds.getAt(rowIndex);
6754         
6755         var row = {
6756             tag : 'tr',
6757             cls : 'x-row-' + rowIndex,
6758             cn : []
6759         };
6760             
6761         var cellObjects = [];
6762         
6763         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6764             var config = cm.config[i];
6765             
6766             var renderer = cm.getRenderer(i);
6767             var value = '';
6768             var id = false;
6769             
6770             if(typeof(renderer) !== 'undefined'){
6771                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6772             }
6773             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6774             // and are rendered into the cells after the row is rendered - using the id for the element.
6775             
6776             if(typeof(value) === 'object'){
6777                 id = Roo.id();
6778                 cellObjects.push({
6779                     container : id,
6780                     cfg : value 
6781                 })
6782             }
6783             
6784             var rowcfg = {
6785                 record: d,
6786                 rowIndex : rowIndex,
6787                 colIndex : i,
6788                 rowClass : ''
6789             };
6790
6791             this.fireEvent('rowclass', this, rowcfg);
6792             
6793             var td = {
6794                 tag: 'td',
6795                 cls : rowcfg.rowClass + ' x-col-' + i,
6796                 style: '',
6797                 html: (typeof(value) === 'object') ? '' : value
6798             };
6799             
6800             if (id) {
6801                 td.id = id;
6802             }
6803             
6804             if(typeof(config.colspan) != 'undefined'){
6805                 td.colspan = config.colspan;
6806             }
6807             
6808             if(typeof(config.hidden) != 'undefined' && config.hidden){
6809                 td.style += ' display:none;';
6810             }
6811             
6812             if(typeof(config.align) != 'undefined' && config.align.length){
6813                 td.style += ' text-align:' + config.align + ';';
6814             }
6815             if(typeof(config.valign) != 'undefined' && config.valign.length){
6816                 td.style += ' vertical-align:' + config.valign + ';';
6817             }
6818             
6819             if(typeof(config.width) != 'undefined'){
6820                 td.style += ' width:' +  config.width + 'px;';
6821             }
6822             
6823             if(typeof(config.cursor) != 'undefined'){
6824                 td.style += ' cursor:' +  config.cursor + ';';
6825             }
6826             
6827             if(typeof(config.cls) != 'undefined'){
6828                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6829             }
6830             
6831             ['xs','sm','md','lg'].map(function(size){
6832                 
6833                 if(typeof(config[size]) == 'undefined'){
6834                     return;
6835                 }
6836                 
6837                 if (!config[size]) { // 0 = hidden
6838                     td.cls += ' hidden-' + size;
6839                     return;
6840                 }
6841                 
6842                 td.cls += ' col-' + size + '-' + config[size];
6843
6844             });
6845             
6846             row.cn.push(td);
6847            
6848         }
6849         
6850         row.cellObjects = cellObjects;
6851         
6852         return row;
6853           
6854     },
6855     
6856     
6857     
6858     onBeforeLoad : function()
6859     {
6860         
6861     },
6862      /**
6863      * Remove all rows
6864      */
6865     clear : function()
6866     {
6867         this.el.select('tbody', true).first().dom.innerHTML = '';
6868     },
6869     /**
6870      * Show or hide a row.
6871      * @param {Number} rowIndex to show or hide
6872      * @param {Boolean} state hide
6873      */
6874     setRowVisibility : function(rowIndex, state)
6875     {
6876         var bt = this.mainBody.dom;
6877         
6878         var rows = this.el.select('tbody > tr', true).elements;
6879         
6880         if(typeof(rows[rowIndex]) == 'undefined'){
6881             return;
6882         }
6883         rows[rowIndex].dom.style.display = state ? '' : 'none';
6884     },
6885     
6886     
6887     getSelectionModel : function(){
6888         if(!this.selModel){
6889             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6890         }
6891         return this.selModel;
6892     },
6893     /*
6894      * Render the Roo.bootstrap object from renderder
6895      */
6896     renderCellObject : function(r)
6897     {
6898         var _this = this;
6899         
6900         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6901         
6902         var t = r.cfg.render(r.container);
6903         
6904         if(r.cfg.cn){
6905             Roo.each(r.cfg.cn, function(c){
6906                 var child = {
6907                     container: t.getChildContainer(),
6908                     cfg: c
6909                 };
6910                 _this.renderCellObject(child);
6911             })
6912         }
6913     },
6914     
6915     getRowIndex : function(row)
6916     {
6917         var rowIndex = -1;
6918         
6919         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6920             if(el != row){
6921                 return;
6922             }
6923             
6924             rowIndex = index;
6925         });
6926         
6927         return rowIndex;
6928     },
6929      /**
6930      * Returns the grid's underlying element = used by panel.Grid
6931      * @return {Element} The element
6932      */
6933     getGridEl : function(){
6934         return this.el;
6935     },
6936      /**
6937      * Forces a resize - used by panel.Grid
6938      * @return {Element} The element
6939      */
6940     autoSize : function()
6941     {
6942         //var ctr = Roo.get(this.container.dom.parentElement);
6943         var ctr = Roo.get(this.el.dom);
6944         
6945         var thd = this.getGridEl().select('thead',true).first();
6946         var tbd = this.getGridEl().select('tbody', true).first();
6947         var tfd = this.getGridEl().select('tfoot', true).first();
6948         
6949         var cw = ctr.getWidth();
6950         
6951         if (tbd) {
6952             
6953             tbd.setSize(ctr.getWidth(),
6954                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6955             );
6956             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6957             cw -= barsize;
6958         }
6959         cw = Math.max(cw, this.totalWidth);
6960         this.getGridEl().select('tr',true).setWidth(cw);
6961         // resize 'expandable coloumn?
6962         
6963         return; // we doe not have a view in this design..
6964         
6965     },
6966     onBodyScroll: function()
6967     {
6968         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6969         if(this.mainHead){
6970             this.mainHead.setStyle({
6971                 'position' : 'relative',
6972                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6973             });
6974         }
6975         
6976         if(this.lazyLoad){
6977             
6978             var scrollHeight = this.mainBody.dom.scrollHeight;
6979             
6980             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6981             
6982             var height = this.mainBody.getHeight();
6983             
6984             if(scrollHeight - height == scrollTop) {
6985                 
6986                 var total = this.ds.getTotalCount();
6987                 
6988                 if(this.footer.cursor + this.footer.pageSize < total){
6989                     
6990                     this.footer.ds.load({
6991                         params : {
6992                             start : this.footer.cursor + this.footer.pageSize,
6993                             limit : this.footer.pageSize
6994                         },
6995                         add : true
6996                     });
6997                 }
6998             }
6999             
7000         }
7001     },
7002     
7003     onHeaderChange : function()
7004     {
7005         var header = this.renderHeader();
7006         var table = this.el.select('table', true).first();
7007         
7008         this.mainHead.remove();
7009         this.mainHead = table.createChild(header, this.mainBody, false);
7010     },
7011     
7012     onHiddenChange : function(colModel, colIndex, hidden)
7013     {
7014         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7015         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7016         
7017         this.CSS.updateRule(thSelector, "display", "");
7018         this.CSS.updateRule(tdSelector, "display", "");
7019         
7020         if(hidden){
7021             this.CSS.updateRule(thSelector, "display", "none");
7022             this.CSS.updateRule(tdSelector, "display", "none");
7023         }
7024         
7025         this.onHeaderChange();
7026         this.onLoad();
7027         
7028     }
7029     
7030 });
7031
7032  
7033
7034  /*
7035  * - LGPL
7036  *
7037  * table cell
7038  * 
7039  */
7040
7041 /**
7042  * @class Roo.bootstrap.TableCell
7043  * @extends Roo.bootstrap.Component
7044  * Bootstrap TableCell class
7045  * @cfg {String} html cell contain text
7046  * @cfg {String} cls cell class
7047  * @cfg {String} tag cell tag (td|th) default td
7048  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7049  * @cfg {String} align Aligns the content in a cell
7050  * @cfg {String} axis Categorizes cells
7051  * @cfg {String} bgcolor Specifies the background color of a cell
7052  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7053  * @cfg {Number} colspan Specifies the number of columns a cell should span
7054  * @cfg {String} headers Specifies one or more header cells a cell is related to
7055  * @cfg {Number} height Sets the height of a cell
7056  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7057  * @cfg {Number} rowspan Sets the number of rows a cell should span
7058  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7059  * @cfg {String} valign Vertical aligns the content in a cell
7060  * @cfg {Number} width Specifies the width of a cell
7061  * 
7062  * @constructor
7063  * Create a new TableCell
7064  * @param {Object} config The config object
7065  */
7066
7067 Roo.bootstrap.TableCell = function(config){
7068     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7069 };
7070
7071 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7072     
7073     html: false,
7074     cls: false,
7075     tag: false,
7076     abbr: false,
7077     align: false,
7078     axis: false,
7079     bgcolor: false,
7080     charoff: false,
7081     colspan: false,
7082     headers: false,
7083     height: false,
7084     nowrap: false,
7085     rowspan: false,
7086     scope: false,
7087     valign: false,
7088     width: false,
7089     
7090     
7091     getAutoCreate : function(){
7092         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7093         
7094         cfg = {
7095             tag: 'td'
7096         };
7097         
7098         if(this.tag){
7099             cfg.tag = this.tag;
7100         }
7101         
7102         if (this.html) {
7103             cfg.html=this.html
7104         }
7105         if (this.cls) {
7106             cfg.cls=this.cls
7107         }
7108         if (this.abbr) {
7109             cfg.abbr=this.abbr
7110         }
7111         if (this.align) {
7112             cfg.align=this.align
7113         }
7114         if (this.axis) {
7115             cfg.axis=this.axis
7116         }
7117         if (this.bgcolor) {
7118             cfg.bgcolor=this.bgcolor
7119         }
7120         if (this.charoff) {
7121             cfg.charoff=this.charoff
7122         }
7123         if (this.colspan) {
7124             cfg.colspan=this.colspan
7125         }
7126         if (this.headers) {
7127             cfg.headers=this.headers
7128         }
7129         if (this.height) {
7130             cfg.height=this.height
7131         }
7132         if (this.nowrap) {
7133             cfg.nowrap=this.nowrap
7134         }
7135         if (this.rowspan) {
7136             cfg.rowspan=this.rowspan
7137         }
7138         if (this.scope) {
7139             cfg.scope=this.scope
7140         }
7141         if (this.valign) {
7142             cfg.valign=this.valign
7143         }
7144         if (this.width) {
7145             cfg.width=this.width
7146         }
7147         
7148         
7149         return cfg;
7150     }
7151    
7152 });
7153
7154  
7155
7156  /*
7157  * - LGPL
7158  *
7159  * table row
7160  * 
7161  */
7162
7163 /**
7164  * @class Roo.bootstrap.TableRow
7165  * @extends Roo.bootstrap.Component
7166  * Bootstrap TableRow class
7167  * @cfg {String} cls row class
7168  * @cfg {String} align Aligns the content in a table row
7169  * @cfg {String} bgcolor Specifies a background color for a table row
7170  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7171  * @cfg {String} valign Vertical aligns the content in a table row
7172  * 
7173  * @constructor
7174  * Create a new TableRow
7175  * @param {Object} config The config object
7176  */
7177
7178 Roo.bootstrap.TableRow = function(config){
7179     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7180 };
7181
7182 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7183     
7184     cls: false,
7185     align: false,
7186     bgcolor: false,
7187     charoff: false,
7188     valign: false,
7189     
7190     getAutoCreate : function(){
7191         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7192         
7193         cfg = {
7194             tag: 'tr'
7195         };
7196             
7197         if(this.cls){
7198             cfg.cls = this.cls;
7199         }
7200         if(this.align){
7201             cfg.align = this.align;
7202         }
7203         if(this.bgcolor){
7204             cfg.bgcolor = this.bgcolor;
7205         }
7206         if(this.charoff){
7207             cfg.charoff = this.charoff;
7208         }
7209         if(this.valign){
7210             cfg.valign = this.valign;
7211         }
7212         
7213         return cfg;
7214     }
7215    
7216 });
7217
7218  
7219
7220  /*
7221  * - LGPL
7222  *
7223  * table body
7224  * 
7225  */
7226
7227 /**
7228  * @class Roo.bootstrap.TableBody
7229  * @extends Roo.bootstrap.Component
7230  * Bootstrap TableBody class
7231  * @cfg {String} cls element class
7232  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7233  * @cfg {String} align Aligns the content inside the element
7234  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7235  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7236  * 
7237  * @constructor
7238  * Create a new TableBody
7239  * @param {Object} config The config object
7240  */
7241
7242 Roo.bootstrap.TableBody = function(config){
7243     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7244 };
7245
7246 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7247     
7248     cls: false,
7249     tag: false,
7250     align: false,
7251     charoff: false,
7252     valign: false,
7253     
7254     getAutoCreate : function(){
7255         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7256         
7257         cfg = {
7258             tag: 'tbody'
7259         };
7260             
7261         if (this.cls) {
7262             cfg.cls=this.cls
7263         }
7264         if(this.tag){
7265             cfg.tag = this.tag;
7266         }
7267         
7268         if(this.align){
7269             cfg.align = this.align;
7270         }
7271         if(this.charoff){
7272             cfg.charoff = this.charoff;
7273         }
7274         if(this.valign){
7275             cfg.valign = this.valign;
7276         }
7277         
7278         return cfg;
7279     }
7280     
7281     
7282 //    initEvents : function()
7283 //    {
7284 //        
7285 //        if(!this.store){
7286 //            return;
7287 //        }
7288 //        
7289 //        this.store = Roo.factory(this.store, Roo.data);
7290 //        this.store.on('load', this.onLoad, this);
7291 //        
7292 //        this.store.load();
7293 //        
7294 //    },
7295 //    
7296 //    onLoad: function () 
7297 //    {   
7298 //        this.fireEvent('load', this);
7299 //    }
7300 //    
7301 //   
7302 });
7303
7304  
7305
7306  /*
7307  * Based on:
7308  * Ext JS Library 1.1.1
7309  * Copyright(c) 2006-2007, Ext JS, LLC.
7310  *
7311  * Originally Released Under LGPL - original licence link has changed is not relivant.
7312  *
7313  * Fork - LGPL
7314  * <script type="text/javascript">
7315  */
7316
7317 // as we use this in bootstrap.
7318 Roo.namespace('Roo.form');
7319  /**
7320  * @class Roo.form.Action
7321  * Internal Class used to handle form actions
7322  * @constructor
7323  * @param {Roo.form.BasicForm} el The form element or its id
7324  * @param {Object} config Configuration options
7325  */
7326
7327  
7328  
7329 // define the action interface
7330 Roo.form.Action = function(form, options){
7331     this.form = form;
7332     this.options = options || {};
7333 };
7334 /**
7335  * Client Validation Failed
7336  * @const 
7337  */
7338 Roo.form.Action.CLIENT_INVALID = 'client';
7339 /**
7340  * Server Validation Failed
7341  * @const 
7342  */
7343 Roo.form.Action.SERVER_INVALID = 'server';
7344  /**
7345  * Connect to Server Failed
7346  * @const 
7347  */
7348 Roo.form.Action.CONNECT_FAILURE = 'connect';
7349 /**
7350  * Reading Data from Server Failed
7351  * @const 
7352  */
7353 Roo.form.Action.LOAD_FAILURE = 'load';
7354
7355 Roo.form.Action.prototype = {
7356     type : 'default',
7357     failureType : undefined,
7358     response : undefined,
7359     result : undefined,
7360
7361     // interface method
7362     run : function(options){
7363
7364     },
7365
7366     // interface method
7367     success : function(response){
7368
7369     },
7370
7371     // interface method
7372     handleResponse : function(response){
7373
7374     },
7375
7376     // default connection failure
7377     failure : function(response){
7378         
7379         this.response = response;
7380         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7381         this.form.afterAction(this, false);
7382     },
7383
7384     processResponse : function(response){
7385         this.response = response;
7386         if(!response.responseText){
7387             return true;
7388         }
7389         this.result = this.handleResponse(response);
7390         return this.result;
7391     },
7392
7393     // utility functions used internally
7394     getUrl : function(appendParams){
7395         var url = this.options.url || this.form.url || this.form.el.dom.action;
7396         if(appendParams){
7397             var p = this.getParams();
7398             if(p){
7399                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7400             }
7401         }
7402         return url;
7403     },
7404
7405     getMethod : function(){
7406         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7407     },
7408
7409     getParams : function(){
7410         var bp = this.form.baseParams;
7411         var p = this.options.params;
7412         if(p){
7413             if(typeof p == "object"){
7414                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7415             }else if(typeof p == 'string' && bp){
7416                 p += '&' + Roo.urlEncode(bp);
7417             }
7418         }else if(bp){
7419             p = Roo.urlEncode(bp);
7420         }
7421         return p;
7422     },
7423
7424     createCallback : function(){
7425         return {
7426             success: this.success,
7427             failure: this.failure,
7428             scope: this,
7429             timeout: (this.form.timeout*1000),
7430             upload: this.form.fileUpload ? this.success : undefined
7431         };
7432     }
7433 };
7434
7435 Roo.form.Action.Submit = function(form, options){
7436     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7437 };
7438
7439 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7440     type : 'submit',
7441
7442     haveProgress : false,
7443     uploadComplete : false,
7444     
7445     // uploadProgress indicator.
7446     uploadProgress : function()
7447     {
7448         if (!this.form.progressUrl) {
7449             return;
7450         }
7451         
7452         if (!this.haveProgress) {
7453             Roo.MessageBox.progress("Uploading", "Uploading");
7454         }
7455         if (this.uploadComplete) {
7456            Roo.MessageBox.hide();
7457            return;
7458         }
7459         
7460         this.haveProgress = true;
7461    
7462         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7463         
7464         var c = new Roo.data.Connection();
7465         c.request({
7466             url : this.form.progressUrl,
7467             params: {
7468                 id : uid
7469             },
7470             method: 'GET',
7471             success : function(req){
7472                //console.log(data);
7473                 var rdata = false;
7474                 var edata;
7475                 try  {
7476                    rdata = Roo.decode(req.responseText)
7477                 } catch (e) {
7478                     Roo.log("Invalid data from server..");
7479                     Roo.log(edata);
7480                     return;
7481                 }
7482                 if (!rdata || !rdata.success) {
7483                     Roo.log(rdata);
7484                     Roo.MessageBox.alert(Roo.encode(rdata));
7485                     return;
7486                 }
7487                 var data = rdata.data;
7488                 
7489                 if (this.uploadComplete) {
7490                    Roo.MessageBox.hide();
7491                    return;
7492                 }
7493                    
7494                 if (data){
7495                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7496                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7497                     );
7498                 }
7499                 this.uploadProgress.defer(2000,this);
7500             },
7501        
7502             failure: function(data) {
7503                 Roo.log('progress url failed ');
7504                 Roo.log(data);
7505             },
7506             scope : this
7507         });
7508            
7509     },
7510     
7511     
7512     run : function()
7513     {
7514         // run get Values on the form, so it syncs any secondary forms.
7515         this.form.getValues();
7516         
7517         var o = this.options;
7518         var method = this.getMethod();
7519         var isPost = method == 'POST';
7520         if(o.clientValidation === false || this.form.isValid()){
7521             
7522             if (this.form.progressUrl) {
7523                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7524                     (new Date() * 1) + '' + Math.random());
7525                     
7526             } 
7527             
7528             
7529             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7530                 form:this.form.el.dom,
7531                 url:this.getUrl(!isPost),
7532                 method: method,
7533                 params:isPost ? this.getParams() : null,
7534                 isUpload: this.form.fileUpload
7535             }));
7536             
7537             this.uploadProgress();
7538
7539         }else if (o.clientValidation !== false){ // client validation failed
7540             this.failureType = Roo.form.Action.CLIENT_INVALID;
7541             this.form.afterAction(this, false);
7542         }
7543     },
7544
7545     success : function(response)
7546     {
7547         this.uploadComplete= true;
7548         if (this.haveProgress) {
7549             Roo.MessageBox.hide();
7550         }
7551         
7552         
7553         var result = this.processResponse(response);
7554         if(result === true || result.success){
7555             this.form.afterAction(this, true);
7556             return;
7557         }
7558         if(result.errors){
7559             this.form.markInvalid(result.errors);
7560             this.failureType = Roo.form.Action.SERVER_INVALID;
7561         }
7562         this.form.afterAction(this, false);
7563     },
7564     failure : function(response)
7565     {
7566         this.uploadComplete= true;
7567         if (this.haveProgress) {
7568             Roo.MessageBox.hide();
7569         }
7570         
7571         this.response = response;
7572         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7573         this.form.afterAction(this, false);
7574     },
7575     
7576     handleResponse : function(response){
7577         if(this.form.errorReader){
7578             var rs = this.form.errorReader.read(response);
7579             var errors = [];
7580             if(rs.records){
7581                 for(var i = 0, len = rs.records.length; i < len; i++) {
7582                     var r = rs.records[i];
7583                     errors[i] = r.data;
7584                 }
7585             }
7586             if(errors.length < 1){
7587                 errors = null;
7588             }
7589             return {
7590                 success : rs.success,
7591                 errors : errors
7592             };
7593         }
7594         var ret = false;
7595         try {
7596             ret = Roo.decode(response.responseText);
7597         } catch (e) {
7598             ret = {
7599                 success: false,
7600                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7601                 errors : []
7602             };
7603         }
7604         return ret;
7605         
7606     }
7607 });
7608
7609
7610 Roo.form.Action.Load = function(form, options){
7611     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7612     this.reader = this.form.reader;
7613 };
7614
7615 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7616     type : 'load',
7617
7618     run : function(){
7619         
7620         Roo.Ajax.request(Roo.apply(
7621                 this.createCallback(), {
7622                     method:this.getMethod(),
7623                     url:this.getUrl(false),
7624                     params:this.getParams()
7625         }));
7626     },
7627
7628     success : function(response){
7629         
7630         var result = this.processResponse(response);
7631         if(result === true || !result.success || !result.data){
7632             this.failureType = Roo.form.Action.LOAD_FAILURE;
7633             this.form.afterAction(this, false);
7634             return;
7635         }
7636         this.form.clearInvalid();
7637         this.form.setValues(result.data);
7638         this.form.afterAction(this, true);
7639     },
7640
7641     handleResponse : function(response){
7642         if(this.form.reader){
7643             var rs = this.form.reader.read(response);
7644             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7645             return {
7646                 success : rs.success,
7647                 data : data
7648             };
7649         }
7650         return Roo.decode(response.responseText);
7651     }
7652 });
7653
7654 Roo.form.Action.ACTION_TYPES = {
7655     'load' : Roo.form.Action.Load,
7656     'submit' : Roo.form.Action.Submit
7657 };/*
7658  * - LGPL
7659  *
7660  * form
7661  *
7662  */
7663
7664 /**
7665  * @class Roo.bootstrap.Form
7666  * @extends Roo.bootstrap.Component
7667  * Bootstrap Form class
7668  * @cfg {String} method  GET | POST (default POST)
7669  * @cfg {String} labelAlign top | left (default top)
7670  * @cfg {String} align left  | right - for navbars
7671  * @cfg {Boolean} loadMask load mask when submit (default true)
7672
7673  *
7674  * @constructor
7675  * Create a new Form
7676  * @param {Object} config The config object
7677  */
7678
7679
7680 Roo.bootstrap.Form = function(config){
7681     
7682     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7683     
7684     Roo.bootstrap.Form.popover.apply();
7685     
7686     this.addEvents({
7687         /**
7688          * @event clientvalidation
7689          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7690          * @param {Form} this
7691          * @param {Boolean} valid true if the form has passed client-side validation
7692          */
7693         clientvalidation: true,
7694         /**
7695          * @event beforeaction
7696          * Fires before any action is performed. Return false to cancel the action.
7697          * @param {Form} this
7698          * @param {Action} action The action to be performed
7699          */
7700         beforeaction: true,
7701         /**
7702          * @event actionfailed
7703          * Fires when an action fails.
7704          * @param {Form} this
7705          * @param {Action} action The action that failed
7706          */
7707         actionfailed : true,
7708         /**
7709          * @event actioncomplete
7710          * Fires when an action is completed.
7711          * @param {Form} this
7712          * @param {Action} action The action that completed
7713          */
7714         actioncomplete : true
7715     });
7716 };
7717
7718 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7719
7720      /**
7721      * @cfg {String} method
7722      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7723      */
7724     method : 'POST',
7725     /**
7726      * @cfg {String} url
7727      * The URL to use for form actions if one isn't supplied in the action options.
7728      */
7729     /**
7730      * @cfg {Boolean} fileUpload
7731      * Set to true if this form is a file upload.
7732      */
7733
7734     /**
7735      * @cfg {Object} baseParams
7736      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7737      */
7738
7739     /**
7740      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7741      */
7742     timeout: 30,
7743     /**
7744      * @cfg {Sting} align (left|right) for navbar forms
7745      */
7746     align : 'left',
7747
7748     // private
7749     activeAction : null,
7750
7751     /**
7752      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7753      * element by passing it or its id or mask the form itself by passing in true.
7754      * @type Mixed
7755      */
7756     waitMsgTarget : false,
7757
7758     loadMask : true,
7759     
7760     /**
7761      * @cfg {Boolean} errorMask (true|false) default false
7762      */
7763     errorMask : false,
7764     
7765     /**
7766      * @cfg {Number} maskOffset Default 100
7767      */
7768     maskOffset : 100,
7769     
7770     /**
7771      * @cfg {Boolean} maskBody
7772      */
7773     maskBody : false,
7774
7775     getAutoCreate : function(){
7776
7777         var cfg = {
7778             tag: 'form',
7779             method : this.method || 'POST',
7780             id : this.id || Roo.id(),
7781             cls : ''
7782         };
7783         if (this.parent().xtype.match(/^Nav/)) {
7784             cfg.cls = 'navbar-form navbar-' + this.align;
7785
7786         }
7787
7788         if (this.labelAlign == 'left' ) {
7789             cfg.cls += ' form-horizontal';
7790         }
7791
7792
7793         return cfg;
7794     },
7795     initEvents : function()
7796     {
7797         this.el.on('submit', this.onSubmit, this);
7798         // this was added as random key presses on the form where triggering form submit.
7799         this.el.on('keypress', function(e) {
7800             if (e.getCharCode() != 13) {
7801                 return true;
7802             }
7803             // we might need to allow it for textareas.. and some other items.
7804             // check e.getTarget().
7805
7806             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7807                 return true;
7808             }
7809
7810             Roo.log("keypress blocked");
7811
7812             e.preventDefault();
7813             return false;
7814         });
7815         
7816     },
7817     // private
7818     onSubmit : function(e){
7819         e.stopEvent();
7820     },
7821
7822      /**
7823      * Returns true if client-side validation on the form is successful.
7824      * @return Boolean
7825      */
7826     isValid : function(){
7827         var items = this.getItems();
7828         var valid = true;
7829         var target = false;
7830         
7831         items.each(function(f){
7832             
7833             if(f.validate()){
7834                 return;
7835             }
7836             
7837             valid = false;
7838
7839             if(!target && f.el.isVisible(true)){
7840                 target = f;
7841             }
7842            
7843         });
7844         
7845         if(this.errorMask && !valid){
7846             Roo.bootstrap.Form.popover.mask(this, target);
7847         }
7848         
7849         return valid;
7850     },
7851     
7852     /**
7853      * Returns true if any fields in this form have changed since their original load.
7854      * @return Boolean
7855      */
7856     isDirty : function(){
7857         var dirty = false;
7858         var items = this.getItems();
7859         items.each(function(f){
7860            if(f.isDirty()){
7861                dirty = true;
7862                return false;
7863            }
7864            return true;
7865         });
7866         return dirty;
7867     },
7868      /**
7869      * Performs a predefined action (submit or load) or custom actions you define on this form.
7870      * @param {String} actionName The name of the action type
7871      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7872      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7873      * accept other config options):
7874      * <pre>
7875 Property          Type             Description
7876 ----------------  ---------------  ----------------------------------------------------------------------------------
7877 url               String           The url for the action (defaults to the form's url)
7878 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7879 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7880 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7881                                    validate the form on the client (defaults to false)
7882      * </pre>
7883      * @return {BasicForm} this
7884      */
7885     doAction : function(action, options){
7886         if(typeof action == 'string'){
7887             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7888         }
7889         if(this.fireEvent('beforeaction', this, action) !== false){
7890             this.beforeAction(action);
7891             action.run.defer(100, action);
7892         }
7893         return this;
7894     },
7895
7896     // private
7897     beforeAction : function(action){
7898         var o = action.options;
7899         
7900         if(this.loadMask){
7901             
7902             if(this.maskBody){
7903                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7904             } else {
7905                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7906             }
7907         }
7908         // not really supported yet.. ??
7909
7910         //if(this.waitMsgTarget === true){
7911         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7912         //}else if(this.waitMsgTarget){
7913         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7914         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7915         //}else {
7916         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7917        // }
7918
7919     },
7920
7921     // private
7922     afterAction : function(action, success){
7923         this.activeAction = null;
7924         var o = action.options;
7925
7926         if(this.loadMask){
7927             
7928             if(this.maskBody){
7929                 Roo.get(document.body).unmask();
7930             } else {
7931                 this.el.unmask();
7932             }
7933         }
7934         
7935         //if(this.waitMsgTarget === true){
7936 //            this.el.unmask();
7937         //}else if(this.waitMsgTarget){
7938         //    this.waitMsgTarget.unmask();
7939         //}else{
7940         //    Roo.MessageBox.updateProgress(1);
7941         //    Roo.MessageBox.hide();
7942        // }
7943         //
7944         if(success){
7945             if(o.reset){
7946                 this.reset();
7947             }
7948             Roo.callback(o.success, o.scope, [this, action]);
7949             this.fireEvent('actioncomplete', this, action);
7950
7951         }else{
7952
7953             // failure condition..
7954             // we have a scenario where updates need confirming.
7955             // eg. if a locking scenario exists..
7956             // we look for { errors : { needs_confirm : true }} in the response.
7957             if (
7958                 (typeof(action.result) != 'undefined')  &&
7959                 (typeof(action.result.errors) != 'undefined')  &&
7960                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7961            ){
7962                 var _t = this;
7963                 Roo.log("not supported yet");
7964                  /*
7965
7966                 Roo.MessageBox.confirm(
7967                     "Change requires confirmation",
7968                     action.result.errorMsg,
7969                     function(r) {
7970                         if (r != 'yes') {
7971                             return;
7972                         }
7973                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7974                     }
7975
7976                 );
7977                 */
7978
7979
7980                 return;
7981             }
7982
7983             Roo.callback(o.failure, o.scope, [this, action]);
7984             // show an error message if no failed handler is set..
7985             if (!this.hasListener('actionfailed')) {
7986                 Roo.log("need to add dialog support");
7987                 /*
7988                 Roo.MessageBox.alert("Error",
7989                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7990                         action.result.errorMsg :
7991                         "Saving Failed, please check your entries or try again"
7992                 );
7993                 */
7994             }
7995
7996             this.fireEvent('actionfailed', this, action);
7997         }
7998
7999     },
8000     /**
8001      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8002      * @param {String} id The value to search for
8003      * @return Field
8004      */
8005     findField : function(id){
8006         var items = this.getItems();
8007         var field = items.get(id);
8008         if(!field){
8009              items.each(function(f){
8010                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8011                     field = f;
8012                     return false;
8013                 }
8014                 return true;
8015             });
8016         }
8017         return field || null;
8018     },
8019      /**
8020      * Mark fields in this form invalid in bulk.
8021      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8022      * @return {BasicForm} this
8023      */
8024     markInvalid : function(errors){
8025         if(errors instanceof Array){
8026             for(var i = 0, len = errors.length; i < len; i++){
8027                 var fieldError = errors[i];
8028                 var f = this.findField(fieldError.id);
8029                 if(f){
8030                     f.markInvalid(fieldError.msg);
8031                 }
8032             }
8033         }else{
8034             var field, id;
8035             for(id in errors){
8036                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8037                     field.markInvalid(errors[id]);
8038                 }
8039             }
8040         }
8041         //Roo.each(this.childForms || [], function (f) {
8042         //    f.markInvalid(errors);
8043         //});
8044
8045         return this;
8046     },
8047
8048     /**
8049      * Set values for fields in this form in bulk.
8050      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8051      * @return {BasicForm} this
8052      */
8053     setValues : function(values){
8054         if(values instanceof Array){ // array of objects
8055             for(var i = 0, len = values.length; i < len; i++){
8056                 var v = values[i];
8057                 var f = this.findField(v.id);
8058                 if(f){
8059                     f.setValue(v.value);
8060                     if(this.trackResetOnLoad){
8061                         f.originalValue = f.getValue();
8062                     }
8063                 }
8064             }
8065         }else{ // object hash
8066             var field, id;
8067             for(id in values){
8068                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8069
8070                     if (field.setFromData &&
8071                         field.valueField &&
8072                         field.displayField &&
8073                         // combos' with local stores can
8074                         // be queried via setValue()
8075                         // to set their value..
8076                         (field.store && !field.store.isLocal)
8077                         ) {
8078                         // it's a combo
8079                         var sd = { };
8080                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8081                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8082                         field.setFromData(sd);
8083
8084                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8085                         
8086                         field.setFromData(values);
8087                         
8088                     } else {
8089                         field.setValue(values[id]);
8090                     }
8091
8092
8093                     if(this.trackResetOnLoad){
8094                         field.originalValue = field.getValue();
8095                     }
8096                 }
8097             }
8098         }
8099
8100         //Roo.each(this.childForms || [], function (f) {
8101         //    f.setValues(values);
8102         //});
8103
8104         return this;
8105     },
8106
8107     /**
8108      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8109      * they are returned as an array.
8110      * @param {Boolean} asString
8111      * @return {Object}
8112      */
8113     getValues : function(asString){
8114         //if (this.childForms) {
8115             // copy values from the child forms
8116         //    Roo.each(this.childForms, function (f) {
8117         //        this.setValues(f.getValues());
8118         //    }, this);
8119         //}
8120
8121
8122
8123         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8124         if(asString === true){
8125             return fs;
8126         }
8127         return Roo.urlDecode(fs);
8128     },
8129
8130     /**
8131      * Returns the fields in this form as an object with key/value pairs.
8132      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8133      * @return {Object}
8134      */
8135     getFieldValues : function(with_hidden)
8136     {
8137         var items = this.getItems();
8138         var ret = {};
8139         items.each(function(f){
8140             
8141             if (!f.getName()) {
8142                 return;
8143             }
8144             
8145             var v = f.getValue();
8146             
8147             if (f.inputType =='radio') {
8148                 if (typeof(ret[f.getName()]) == 'undefined') {
8149                     ret[f.getName()] = ''; // empty..
8150                 }
8151
8152                 if (!f.el.dom.checked) {
8153                     return;
8154
8155                 }
8156                 v = f.el.dom.value;
8157
8158             }
8159             
8160             if(f.xtype == 'MoneyField'){
8161                 ret[f.currencyName] = f.getCurrency();
8162             }
8163
8164             // not sure if this supported any more..
8165             if ((typeof(v) == 'object') && f.getRawValue) {
8166                 v = f.getRawValue() ; // dates..
8167             }
8168             // combo boxes where name != hiddenName...
8169             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8170                 ret[f.name] = f.getRawValue();
8171             }
8172             ret[f.getName()] = v;
8173         });
8174
8175         return ret;
8176     },
8177
8178     /**
8179      * Clears all invalid messages in this form.
8180      * @return {BasicForm} this
8181      */
8182     clearInvalid : function(){
8183         var items = this.getItems();
8184
8185         items.each(function(f){
8186            f.clearInvalid();
8187         });
8188
8189         return this;
8190     },
8191
8192     /**
8193      * Resets this form.
8194      * @return {BasicForm} this
8195      */
8196     reset : function(){
8197         var items = this.getItems();
8198         items.each(function(f){
8199             f.reset();
8200         });
8201
8202         Roo.each(this.childForms || [], function (f) {
8203             f.reset();
8204         });
8205
8206
8207         return this;
8208     },
8209     
8210     getItems : function()
8211     {
8212         var r=new Roo.util.MixedCollection(false, function(o){
8213             return o.id || (o.id = Roo.id());
8214         });
8215         var iter = function(el) {
8216             if (el.inputEl) {
8217                 r.add(el);
8218             }
8219             if (!el.items) {
8220                 return;
8221             }
8222             Roo.each(el.items,function(e) {
8223                 iter(e);
8224             });
8225         };
8226
8227         iter(this);
8228         return r;
8229     },
8230     
8231     hideFields : function(items)
8232     {
8233         Roo.each(items, function(i){
8234             
8235             var f = this.findField(i);
8236             
8237             if(!f){
8238                 return;
8239             }
8240             
8241             if(f.xtype == 'DateField'){
8242                 f.setVisible(false);
8243                 return;
8244             }
8245             
8246             f.hide();
8247             
8248         }, this);
8249     },
8250     
8251     showFields : function(items)
8252     {
8253         Roo.each(items, function(i){
8254             
8255             var f = this.findField(i);
8256             
8257             if(!f){
8258                 return;
8259             }
8260             
8261             if(f.xtype == 'DateField'){
8262                 f.setVisible(true);
8263                 return;
8264             }
8265             
8266             f.show();
8267             
8268         }, this);
8269     }
8270
8271 });
8272
8273 Roo.apply(Roo.bootstrap.Form, {
8274     
8275     popover : {
8276         
8277         padding : 5,
8278         
8279         isApplied : false,
8280         
8281         isMasked : false,
8282         
8283         form : false,
8284         
8285         target : false,
8286         
8287         toolTip : false,
8288         
8289         intervalID : false,
8290         
8291         maskEl : false,
8292         
8293         apply : function()
8294         {
8295             if(this.isApplied){
8296                 return;
8297             }
8298             
8299             this.maskEl = {
8300                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8301                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8302                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8303                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8304             };
8305             
8306             this.maskEl.top.enableDisplayMode("block");
8307             this.maskEl.left.enableDisplayMode("block");
8308             this.maskEl.bottom.enableDisplayMode("block");
8309             this.maskEl.right.enableDisplayMode("block");
8310             
8311             this.toolTip = new Roo.bootstrap.Tooltip({
8312                 cls : 'roo-form-error-popover',
8313                 alignment : {
8314                     'left' : ['r-l', [-2,0], 'right'],
8315                     'right' : ['l-r', [2,0], 'left'],
8316                     'bottom' : ['tl-bl', [0,2], 'top'],
8317                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8318                 }
8319             });
8320             
8321             this.toolTip.render(Roo.get(document.body));
8322
8323             this.toolTip.el.enableDisplayMode("block");
8324             
8325             Roo.get(document.body).on('click', function(){
8326                 this.unmask();
8327             }, this);
8328             
8329             Roo.get(document.body).on('touchstart', function(){
8330                 this.unmask();
8331             }, this);
8332             
8333             this.isApplied = true
8334         },
8335         
8336         mask : function(form, target)
8337         {
8338             this.form = form;
8339             
8340             this.target = target;
8341             
8342             if(!this.form.errorMask || !target.el){
8343                 return;
8344             }
8345             
8346             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8347             
8348             Roo.log(scrollable);
8349             
8350             var ot = this.target.el.calcOffsetsTo(scrollable);
8351             
8352             var scrollTo = ot[1] - this.form.maskOffset;
8353             
8354             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8355             
8356             scrollable.scrollTo('top', scrollTo);
8357             
8358             var box = this.target.el.getBox();
8359             Roo.log(box);
8360             var zIndex = Roo.bootstrap.Modal.zIndex++;
8361
8362             
8363             this.maskEl.top.setStyle('position', 'absolute');
8364             this.maskEl.top.setStyle('z-index', zIndex);
8365             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8366             this.maskEl.top.setLeft(0);
8367             this.maskEl.top.setTop(0);
8368             this.maskEl.top.show();
8369             
8370             this.maskEl.left.setStyle('position', 'absolute');
8371             this.maskEl.left.setStyle('z-index', zIndex);
8372             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8373             this.maskEl.left.setLeft(0);
8374             this.maskEl.left.setTop(box.y - this.padding);
8375             this.maskEl.left.show();
8376
8377             this.maskEl.bottom.setStyle('position', 'absolute');
8378             this.maskEl.bottom.setStyle('z-index', zIndex);
8379             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8380             this.maskEl.bottom.setLeft(0);
8381             this.maskEl.bottom.setTop(box.bottom + this.padding);
8382             this.maskEl.bottom.show();
8383
8384             this.maskEl.right.setStyle('position', 'absolute');
8385             this.maskEl.right.setStyle('z-index', zIndex);
8386             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8387             this.maskEl.right.setLeft(box.right + this.padding);
8388             this.maskEl.right.setTop(box.y - this.padding);
8389             this.maskEl.right.show();
8390
8391             this.toolTip.bindEl = this.target.el;
8392
8393             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8394
8395             var tip = this.target.blankText;
8396
8397             if(this.target.getValue() !== '' ) {
8398                 
8399                 if (this.target.invalidText.length) {
8400                     tip = this.target.invalidText;
8401                 } else if (this.target.regexText.length){
8402                     tip = this.target.regexText;
8403                 }
8404             }
8405
8406             this.toolTip.show(tip);
8407
8408             this.intervalID = window.setInterval(function() {
8409                 Roo.bootstrap.Form.popover.unmask();
8410             }, 10000);
8411
8412             window.onwheel = function(){ return false;};
8413             
8414             (function(){ this.isMasked = true; }).defer(500, this);
8415             
8416         },
8417         
8418         unmask : function()
8419         {
8420             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8421                 return;
8422             }
8423             
8424             this.maskEl.top.setStyle('position', 'absolute');
8425             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8426             this.maskEl.top.hide();
8427
8428             this.maskEl.left.setStyle('position', 'absolute');
8429             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8430             this.maskEl.left.hide();
8431
8432             this.maskEl.bottom.setStyle('position', 'absolute');
8433             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8434             this.maskEl.bottom.hide();
8435
8436             this.maskEl.right.setStyle('position', 'absolute');
8437             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8438             this.maskEl.right.hide();
8439             
8440             this.toolTip.hide();
8441             
8442             this.toolTip.el.hide();
8443             
8444             window.onwheel = function(){ return true;};
8445             
8446             if(this.intervalID){
8447                 window.clearInterval(this.intervalID);
8448                 this.intervalID = false;
8449             }
8450             
8451             this.isMasked = false;
8452             
8453         }
8454         
8455     }
8456     
8457 });
8458
8459 /*
8460  * Based on:
8461  * Ext JS Library 1.1.1
8462  * Copyright(c) 2006-2007, Ext JS, LLC.
8463  *
8464  * Originally Released Under LGPL - original licence link has changed is not relivant.
8465  *
8466  * Fork - LGPL
8467  * <script type="text/javascript">
8468  */
8469 /**
8470  * @class Roo.form.VTypes
8471  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8472  * @singleton
8473  */
8474 Roo.form.VTypes = function(){
8475     // closure these in so they are only created once.
8476     var alpha = /^[a-zA-Z_]+$/;
8477     var alphanum = /^[a-zA-Z0-9_]+$/;
8478     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8479     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8480
8481     // All these messages and functions are configurable
8482     return {
8483         /**
8484          * The function used to validate email addresses
8485          * @param {String} value The email address
8486          */
8487         'email' : function(v){
8488             return email.test(v);
8489         },
8490         /**
8491          * The error text to display when the email validation function returns false
8492          * @type String
8493          */
8494         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8495         /**
8496          * The keystroke filter mask to be applied on email input
8497          * @type RegExp
8498          */
8499         'emailMask' : /[a-z0-9_\.\-@]/i,
8500
8501         /**
8502          * The function used to validate URLs
8503          * @param {String} value The URL
8504          */
8505         'url' : function(v){
8506             return url.test(v);
8507         },
8508         /**
8509          * The error text to display when the url validation function returns false
8510          * @type String
8511          */
8512         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8513         
8514         /**
8515          * The function used to validate alpha values
8516          * @param {String} value The value
8517          */
8518         'alpha' : function(v){
8519             return alpha.test(v);
8520         },
8521         /**
8522          * The error text to display when the alpha validation function returns false
8523          * @type String
8524          */
8525         'alphaText' : 'This field should only contain letters and _',
8526         /**
8527          * The keystroke filter mask to be applied on alpha input
8528          * @type RegExp
8529          */
8530         'alphaMask' : /[a-z_]/i,
8531
8532         /**
8533          * The function used to validate alphanumeric values
8534          * @param {String} value The value
8535          */
8536         'alphanum' : function(v){
8537             return alphanum.test(v);
8538         },
8539         /**
8540          * The error text to display when the alphanumeric validation function returns false
8541          * @type String
8542          */
8543         'alphanumText' : 'This field should only contain letters, numbers and _',
8544         /**
8545          * The keystroke filter mask to be applied on alphanumeric input
8546          * @type RegExp
8547          */
8548         'alphanumMask' : /[a-z0-9_]/i
8549     };
8550 }();/*
8551  * - LGPL
8552  *
8553  * Input
8554  * 
8555  */
8556
8557 /**
8558  * @class Roo.bootstrap.Input
8559  * @extends Roo.bootstrap.Component
8560  * Bootstrap Input class
8561  * @cfg {Boolean} disabled is it disabled
8562  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8563  * @cfg {String} name name of the input
8564  * @cfg {string} fieldLabel - the label associated
8565  * @cfg {string} placeholder - placeholder to put in text.
8566  * @cfg {string}  before - input group add on before
8567  * @cfg {string} after - input group add on after
8568  * @cfg {string} size - (lg|sm) or leave empty..
8569  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8570  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8571  * @cfg {Number} md colspan out of 12 for computer-sized screens
8572  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8573  * @cfg {string} value default value of the input
8574  * @cfg {Number} labelWidth set the width of label 
8575  * @cfg {Number} labellg set the width of label (1-12)
8576  * @cfg {Number} labelmd set the width of label (1-12)
8577  * @cfg {Number} labelsm set the width of label (1-12)
8578  * @cfg {Number} labelxs set the width of label (1-12)
8579  * @cfg {String} labelAlign (top|left)
8580  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8581  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8582  * @cfg {String} indicatorpos (left|right) default left
8583  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8584  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8585
8586  * @cfg {String} align (left|center|right) Default left
8587  * @cfg {Boolean} forceFeedback (true|false) Default false
8588  * 
8589  * @constructor
8590  * Create a new Input
8591  * @param {Object} config The config object
8592  */
8593
8594 Roo.bootstrap.Input = function(config){
8595     
8596     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8597     
8598     this.addEvents({
8599         /**
8600          * @event focus
8601          * Fires when this field receives input focus.
8602          * @param {Roo.form.Field} this
8603          */
8604         focus : true,
8605         /**
8606          * @event blur
8607          * Fires when this field loses input focus.
8608          * @param {Roo.form.Field} this
8609          */
8610         blur : true,
8611         /**
8612          * @event specialkey
8613          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8614          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8615          * @param {Roo.form.Field} this
8616          * @param {Roo.EventObject} e The event object
8617          */
8618         specialkey : true,
8619         /**
8620          * @event change
8621          * Fires just before the field blurs if the field value has changed.
8622          * @param {Roo.form.Field} this
8623          * @param {Mixed} newValue The new value
8624          * @param {Mixed} oldValue The original value
8625          */
8626         change : true,
8627         /**
8628          * @event invalid
8629          * Fires after the field has been marked as invalid.
8630          * @param {Roo.form.Field} this
8631          * @param {String} msg The validation message
8632          */
8633         invalid : true,
8634         /**
8635          * @event valid
8636          * Fires after the field has been validated with no errors.
8637          * @param {Roo.form.Field} this
8638          */
8639         valid : true,
8640          /**
8641          * @event keyup
8642          * Fires after the key up
8643          * @param {Roo.form.Field} this
8644          * @param {Roo.EventObject}  e The event Object
8645          */
8646         keyup : true
8647     });
8648 };
8649
8650 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8651      /**
8652      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8653       automatic validation (defaults to "keyup").
8654      */
8655     validationEvent : "keyup",
8656      /**
8657      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8658      */
8659     validateOnBlur : true,
8660     /**
8661      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8662      */
8663     validationDelay : 250,
8664      /**
8665      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8666      */
8667     focusClass : "x-form-focus",  // not needed???
8668     
8669        
8670     /**
8671      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8672      */
8673     invalidClass : "has-warning",
8674     
8675     /**
8676      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8677      */
8678     validClass : "has-success",
8679     
8680     /**
8681      * @cfg {Boolean} hasFeedback (true|false) default true
8682      */
8683     hasFeedback : true,
8684     
8685     /**
8686      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8687      */
8688     invalidFeedbackClass : "glyphicon-warning-sign",
8689     
8690     /**
8691      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8692      */
8693     validFeedbackClass : "glyphicon-ok",
8694     
8695     /**
8696      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8697      */
8698     selectOnFocus : false,
8699     
8700      /**
8701      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8702      */
8703     maskRe : null,
8704        /**
8705      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8706      */
8707     vtype : null,
8708     
8709       /**
8710      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8711      */
8712     disableKeyFilter : false,
8713     
8714        /**
8715      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8716      */
8717     disabled : false,
8718      /**
8719      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8720      */
8721     allowBlank : true,
8722     /**
8723      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8724      */
8725     blankText : "Please complete this mandatory field",
8726     
8727      /**
8728      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8729      */
8730     minLength : 0,
8731     /**
8732      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8733      */
8734     maxLength : Number.MAX_VALUE,
8735     /**
8736      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8737      */
8738     minLengthText : "The minimum length for this field is {0}",
8739     /**
8740      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8741      */
8742     maxLengthText : "The maximum length for this field is {0}",
8743   
8744     
8745     /**
8746      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8747      * If available, this function will be called only after the basic validators all return true, and will be passed the
8748      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8749      */
8750     validator : null,
8751     /**
8752      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8753      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8754      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8755      */
8756     regex : null,
8757     /**
8758      * @cfg {String} regexText -- Depricated - use Invalid Text
8759      */
8760     regexText : "",
8761     
8762     /**
8763      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8764      */
8765     invalidText : "",
8766     
8767     
8768     
8769     autocomplete: false,
8770     
8771     
8772     fieldLabel : '',
8773     inputType : 'text',
8774     
8775     name : false,
8776     placeholder: false,
8777     before : false,
8778     after : false,
8779     size : false,
8780     hasFocus : false,
8781     preventMark: false,
8782     isFormField : true,
8783     value : '',
8784     labelWidth : 2,
8785     labelAlign : false,
8786     readOnly : false,
8787     align : false,
8788     formatedValue : false,
8789     forceFeedback : false,
8790     
8791     indicatorpos : 'left',
8792     
8793     labellg : 0,
8794     labelmd : 0,
8795     labelsm : 0,
8796     labelxs : 0,
8797     
8798     capture : '',
8799     accept : '',
8800     
8801     parentLabelAlign : function()
8802     {
8803         var parent = this;
8804         while (parent.parent()) {
8805             parent = parent.parent();
8806             if (typeof(parent.labelAlign) !='undefined') {
8807                 return parent.labelAlign;
8808             }
8809         }
8810         return 'left';
8811         
8812     },
8813     
8814     getAutoCreate : function()
8815     {
8816         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8817         
8818         var id = Roo.id();
8819         
8820         var cfg = {};
8821         
8822         if(this.inputType != 'hidden'){
8823             cfg.cls = 'form-group' //input-group
8824         }
8825         
8826         var input =  {
8827             tag: 'input',
8828             id : id,
8829             type : this.inputType,
8830             value : this.value,
8831             cls : 'form-control',
8832             placeholder : this.placeholder || '',
8833             autocomplete : this.autocomplete || 'new-password'
8834         };
8835         
8836         if(this.capture.length){
8837             input.capture = this.capture;
8838         }
8839         
8840         if(this.accept.length){
8841             input.accept = this.accept + "/*";
8842         }
8843         
8844         if(this.align){
8845             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8846         }
8847         
8848         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8849             input.maxLength = this.maxLength;
8850         }
8851         
8852         if (this.disabled) {
8853             input.disabled=true;
8854         }
8855         
8856         if (this.readOnly) {
8857             input.readonly=true;
8858         }
8859         
8860         if (this.name) {
8861             input.name = this.name;
8862         }
8863         
8864         if (this.size) {
8865             input.cls += ' input-' + this.size;
8866         }
8867         
8868         var settings=this;
8869         ['xs','sm','md','lg'].map(function(size){
8870             if (settings[size]) {
8871                 cfg.cls += ' col-' + size + '-' + settings[size];
8872             }
8873         });
8874         
8875         var inputblock = input;
8876         
8877         var feedback = {
8878             tag: 'span',
8879             cls: 'glyphicon form-control-feedback'
8880         };
8881             
8882         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8883             
8884             inputblock = {
8885                 cls : 'has-feedback',
8886                 cn :  [
8887                     input,
8888                     feedback
8889                 ] 
8890             };  
8891         }
8892         
8893         if (this.before || this.after) {
8894             
8895             inputblock = {
8896                 cls : 'input-group',
8897                 cn :  [] 
8898             };
8899             
8900             if (this.before && typeof(this.before) == 'string') {
8901                 
8902                 inputblock.cn.push({
8903                     tag :'span',
8904                     cls : 'roo-input-before input-group-addon',
8905                     html : this.before
8906                 });
8907             }
8908             if (this.before && typeof(this.before) == 'object') {
8909                 this.before = Roo.factory(this.before);
8910                 
8911                 inputblock.cn.push({
8912                     tag :'span',
8913                     cls : 'roo-input-before input-group-' +
8914                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8915                 });
8916             }
8917             
8918             inputblock.cn.push(input);
8919             
8920             if (this.after && typeof(this.after) == 'string') {
8921                 inputblock.cn.push({
8922                     tag :'span',
8923                     cls : 'roo-input-after input-group-addon',
8924                     html : this.after
8925                 });
8926             }
8927             if (this.after && typeof(this.after) == 'object') {
8928                 this.after = Roo.factory(this.after);
8929                 
8930                 inputblock.cn.push({
8931                     tag :'span',
8932                     cls : 'roo-input-after input-group-' +
8933                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8934                 });
8935             }
8936             
8937             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8938                 inputblock.cls += ' has-feedback';
8939                 inputblock.cn.push(feedback);
8940             }
8941         };
8942         
8943         if (align ==='left' && this.fieldLabel.length) {
8944             
8945             cfg.cls += ' roo-form-group-label-left';
8946             
8947             cfg.cn = [
8948                 {
8949                     tag : 'i',
8950                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8951                     tooltip : 'This field is required'
8952                 },
8953                 {
8954                     tag: 'label',
8955                     'for' :  id,
8956                     cls : 'control-label',
8957                     html : this.fieldLabel
8958
8959                 },
8960                 {
8961                     cls : "", 
8962                     cn: [
8963                         inputblock
8964                     ]
8965                 }
8966             ];
8967             
8968             var labelCfg = cfg.cn[1];
8969             var contentCfg = cfg.cn[2];
8970             
8971             if(this.indicatorpos == 'right'){
8972                 cfg.cn = [
8973                     {
8974                         tag: 'label',
8975                         'for' :  id,
8976                         cls : 'control-label',
8977                         cn : [
8978                             {
8979                                 tag : 'span',
8980                                 html : this.fieldLabel
8981                             },
8982                             {
8983                                 tag : 'i',
8984                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8985                                 tooltip : 'This field is required'
8986                             }
8987                         ]
8988                     },
8989                     {
8990                         cls : "",
8991                         cn: [
8992                             inputblock
8993                         ]
8994                     }
8995
8996                 ];
8997                 
8998                 labelCfg = cfg.cn[0];
8999                 contentCfg = cfg.cn[1];
9000             
9001             }
9002             
9003             if(this.labelWidth > 12){
9004                 labelCfg.style = "width: " + this.labelWidth + 'px';
9005             }
9006             
9007             if(this.labelWidth < 13 && this.labelmd == 0){
9008                 this.labelmd = this.labelWidth;
9009             }
9010             
9011             if(this.labellg > 0){
9012                 labelCfg.cls += ' col-lg-' + this.labellg;
9013                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9014             }
9015             
9016             if(this.labelmd > 0){
9017                 labelCfg.cls += ' col-md-' + this.labelmd;
9018                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9019             }
9020             
9021             if(this.labelsm > 0){
9022                 labelCfg.cls += ' col-sm-' + this.labelsm;
9023                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9024             }
9025             
9026             if(this.labelxs > 0){
9027                 labelCfg.cls += ' col-xs-' + this.labelxs;
9028                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9029             }
9030             
9031             
9032         } else if ( this.fieldLabel.length) {
9033                 
9034             cfg.cn = [
9035                 {
9036                     tag : 'i',
9037                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9038                     tooltip : 'This field is required'
9039                 },
9040                 {
9041                     tag: 'label',
9042                    //cls : 'input-group-addon',
9043                     html : this.fieldLabel
9044
9045                 },
9046
9047                inputblock
9048
9049            ];
9050            
9051            if(this.indicatorpos == 'right'){
9052                 
9053                 cfg.cn = [
9054                     {
9055                         tag: 'label',
9056                        //cls : 'input-group-addon',
9057                         html : this.fieldLabel
9058
9059                     },
9060                     {
9061                         tag : 'i',
9062                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9063                         tooltip : 'This field is required'
9064                     },
9065
9066                    inputblock
9067
9068                ];
9069
9070             }
9071
9072         } else {
9073             
9074             cfg.cn = [
9075
9076                     inputblock
9077
9078             ];
9079                 
9080                 
9081         };
9082         
9083         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9084            cfg.cls += ' navbar-form';
9085         }
9086         
9087         if (this.parentType === 'NavGroup') {
9088            cfg.cls += ' navbar-form';
9089            cfg.tag = 'li';
9090         }
9091         
9092         return cfg;
9093         
9094     },
9095     /**
9096      * return the real input element.
9097      */
9098     inputEl: function ()
9099     {
9100         return this.el.select('input.form-control',true).first();
9101     },
9102     
9103     tooltipEl : function()
9104     {
9105         return this.inputEl();
9106     },
9107     
9108     indicatorEl : function()
9109     {
9110         var indicator = this.el.select('i.roo-required-indicator',true).first();
9111         
9112         if(!indicator){
9113             return false;
9114         }
9115         
9116         return indicator;
9117         
9118     },
9119     
9120     setDisabled : function(v)
9121     {
9122         var i  = this.inputEl().dom;
9123         if (!v) {
9124             i.removeAttribute('disabled');
9125             return;
9126             
9127         }
9128         i.setAttribute('disabled','true');
9129     },
9130     initEvents : function()
9131     {
9132           
9133         this.inputEl().on("keydown" , this.fireKey,  this);
9134         this.inputEl().on("focus", this.onFocus,  this);
9135         this.inputEl().on("blur", this.onBlur,  this);
9136         
9137         this.inputEl().relayEvent('keyup', this);
9138         
9139         this.indicator = this.indicatorEl();
9140         
9141         if(this.indicator){
9142             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9143         }
9144  
9145         // reference to original value for reset
9146         this.originalValue = this.getValue();
9147         //Roo.form.TextField.superclass.initEvents.call(this);
9148         if(this.validationEvent == 'keyup'){
9149             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9150             this.inputEl().on('keyup', this.filterValidation, this);
9151         }
9152         else if(this.validationEvent !== false){
9153             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9154         }
9155         
9156         if(this.selectOnFocus){
9157             this.on("focus", this.preFocus, this);
9158             
9159         }
9160         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9161             this.inputEl().on("keypress", this.filterKeys, this);
9162         } else {
9163             this.inputEl().relayEvent('keypress', this);
9164         }
9165        /* if(this.grow){
9166             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9167             this.el.on("click", this.autoSize,  this);
9168         }
9169         */
9170         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9171             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9172         }
9173         
9174         if (typeof(this.before) == 'object') {
9175             this.before.render(this.el.select('.roo-input-before',true).first());
9176         }
9177         if (typeof(this.after) == 'object') {
9178             this.after.render(this.el.select('.roo-input-after',true).first());
9179         }
9180         
9181         this.inputEl().on('change', this.onChange, this);
9182         
9183     },
9184     filterValidation : function(e){
9185         if(!e.isNavKeyPress()){
9186             this.validationTask.delay(this.validationDelay);
9187         }
9188     },
9189      /**
9190      * Validates the field value
9191      * @return {Boolean} True if the value is valid, else false
9192      */
9193     validate : function(){
9194         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9195         if(this.disabled || this.validateValue(this.getRawValue())){
9196             this.markValid();
9197             return true;
9198         }
9199         
9200         this.markInvalid();
9201         return false;
9202     },
9203     
9204     
9205     /**
9206      * Validates a value according to the field's validation rules and marks the field as invalid
9207      * if the validation fails
9208      * @param {Mixed} value The value to validate
9209      * @return {Boolean} True if the value is valid, else false
9210      */
9211     validateValue : function(value)
9212     {
9213         if(this.getVisibilityEl().hasClass('hidden')){
9214             return true;
9215         }
9216         
9217         if(value.length < 1)  { // if it's blank
9218             if(this.allowBlank){
9219                 return true;
9220             }
9221             return false;
9222         }
9223         
9224         if(value.length < this.minLength){
9225             return false;
9226         }
9227         if(value.length > this.maxLength){
9228             return false;
9229         }
9230         if(this.vtype){
9231             var vt = Roo.form.VTypes;
9232             if(!vt[this.vtype](value, this)){
9233                 return false;
9234             }
9235         }
9236         if(typeof this.validator == "function"){
9237             var msg = this.validator(value);
9238             if(msg !== true){
9239                 return false;
9240             }
9241             if (typeof(msg) == 'string') {
9242                 this.invalidText = msg;
9243             }
9244         }
9245         
9246         if(this.regex && !this.regex.test(value)){
9247             return false;
9248         }
9249         
9250         return true;
9251     },
9252     
9253      // private
9254     fireKey : function(e){
9255         //Roo.log('field ' + e.getKey());
9256         if(e.isNavKeyPress()){
9257             this.fireEvent("specialkey", this, e);
9258         }
9259     },
9260     focus : function (selectText){
9261         if(this.rendered){
9262             this.inputEl().focus();
9263             if(selectText === true){
9264                 this.inputEl().dom.select();
9265             }
9266         }
9267         return this;
9268     } ,
9269     
9270     onFocus : function(){
9271         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9272            // this.el.addClass(this.focusClass);
9273         }
9274         if(!this.hasFocus){
9275             this.hasFocus = true;
9276             this.startValue = this.getValue();
9277             this.fireEvent("focus", this);
9278         }
9279     },
9280     
9281     beforeBlur : Roo.emptyFn,
9282
9283     
9284     // private
9285     onBlur : function(){
9286         this.beforeBlur();
9287         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9288             //this.el.removeClass(this.focusClass);
9289         }
9290         this.hasFocus = false;
9291         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9292             this.validate();
9293         }
9294         var v = this.getValue();
9295         if(String(v) !== String(this.startValue)){
9296             this.fireEvent('change', this, v, this.startValue);
9297         }
9298         this.fireEvent("blur", this);
9299     },
9300     
9301     onChange : function(e)
9302     {
9303         var v = this.getValue();
9304         if(String(v) !== String(this.startValue)){
9305             this.fireEvent('change', this, v, this.startValue);
9306         }
9307         
9308     },
9309     
9310     /**
9311      * Resets the current field value to the originally loaded value and clears any validation messages
9312      */
9313     reset : function(){
9314         this.setValue(this.originalValue);
9315         this.validate();
9316     },
9317      /**
9318      * Returns the name of the field
9319      * @return {Mixed} name The name field
9320      */
9321     getName: function(){
9322         return this.name;
9323     },
9324      /**
9325      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9326      * @return {Mixed} value The field value
9327      */
9328     getValue : function(){
9329         
9330         var v = this.inputEl().getValue();
9331         
9332         return v;
9333     },
9334     /**
9335      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9336      * @return {Mixed} value The field value
9337      */
9338     getRawValue : function(){
9339         var v = this.inputEl().getValue();
9340         
9341         return v;
9342     },
9343     
9344     /**
9345      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9346      * @param {Mixed} value The value to set
9347      */
9348     setRawValue : function(v){
9349         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9350     },
9351     
9352     selectText : function(start, end){
9353         var v = this.getRawValue();
9354         if(v.length > 0){
9355             start = start === undefined ? 0 : start;
9356             end = end === undefined ? v.length : end;
9357             var d = this.inputEl().dom;
9358             if(d.setSelectionRange){
9359                 d.setSelectionRange(start, end);
9360             }else if(d.createTextRange){
9361                 var range = d.createTextRange();
9362                 range.moveStart("character", start);
9363                 range.moveEnd("character", v.length-end);
9364                 range.select();
9365             }
9366         }
9367     },
9368     
9369     /**
9370      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9371      * @param {Mixed} value The value to set
9372      */
9373     setValue : function(v){
9374         this.value = v;
9375         if(this.rendered){
9376             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9377             this.validate();
9378         }
9379     },
9380     
9381     /*
9382     processValue : function(value){
9383         if(this.stripCharsRe){
9384             var newValue = value.replace(this.stripCharsRe, '');
9385             if(newValue !== value){
9386                 this.setRawValue(newValue);
9387                 return newValue;
9388             }
9389         }
9390         return value;
9391     },
9392   */
9393     preFocus : function(){
9394         
9395         if(this.selectOnFocus){
9396             this.inputEl().dom.select();
9397         }
9398     },
9399     filterKeys : function(e){
9400         var k = e.getKey();
9401         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9402             return;
9403         }
9404         var c = e.getCharCode(), cc = String.fromCharCode(c);
9405         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9406             return;
9407         }
9408         if(!this.maskRe.test(cc)){
9409             e.stopEvent();
9410         }
9411     },
9412      /**
9413      * Clear any invalid styles/messages for this field
9414      */
9415     clearInvalid : function(){
9416         
9417         if(!this.el || this.preventMark){ // not rendered
9418             return;
9419         }
9420         
9421      
9422         this.el.removeClass(this.invalidClass);
9423         
9424         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9425             
9426             var feedback = this.el.select('.form-control-feedback', true).first();
9427             
9428             if(feedback){
9429                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9430             }
9431             
9432         }
9433         
9434         if(this.indicator){
9435             this.indicator.removeClass('visible');
9436             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9437         }
9438         
9439         this.fireEvent('valid', this);
9440     },
9441     
9442      /**
9443      * Mark this field as valid
9444      */
9445     markValid : function()
9446     {
9447         if(!this.el  || this.preventMark){ // not rendered...
9448             return;
9449         }
9450         
9451         this.el.removeClass([this.invalidClass, this.validClass]);
9452         
9453         var feedback = this.el.select('.form-control-feedback', true).first();
9454             
9455         if(feedback){
9456             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9457         }
9458         
9459         if(this.indicator){
9460             this.indicator.removeClass('visible');
9461             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9462         }
9463         
9464         if(this.disabled){
9465             return;
9466         }
9467         
9468         if(this.allowBlank && !this.getRawValue().length){
9469             return;
9470         }
9471         
9472         this.el.addClass(this.validClass);
9473         
9474         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9475             
9476             var feedback = this.el.select('.form-control-feedback', true).first();
9477             
9478             if(feedback){
9479                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9480                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9481             }
9482             
9483         }
9484         
9485         this.fireEvent('valid', this);
9486     },
9487     
9488      /**
9489      * Mark this field as invalid
9490      * @param {String} msg The validation message
9491      */
9492     markInvalid : function(msg)
9493     {
9494         if(!this.el  || this.preventMark){ // not rendered
9495             return;
9496         }
9497         
9498         this.el.removeClass([this.invalidClass, this.validClass]);
9499         
9500         var feedback = this.el.select('.form-control-feedback', true).first();
9501             
9502         if(feedback){
9503             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9504         }
9505
9506         if(this.disabled){
9507             return;
9508         }
9509         
9510         if(this.allowBlank && !this.getRawValue().length){
9511             return;
9512         }
9513         
9514         if(this.indicator){
9515             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9516             this.indicator.addClass('visible');
9517         }
9518         
9519         this.el.addClass(this.invalidClass);
9520         
9521         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9522             
9523             var feedback = this.el.select('.form-control-feedback', true).first();
9524             
9525             if(feedback){
9526                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9527                 
9528                 if(this.getValue().length || this.forceFeedback){
9529                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9530                 }
9531                 
9532             }
9533             
9534         }
9535         
9536         this.fireEvent('invalid', this, msg);
9537     },
9538     // private
9539     SafariOnKeyDown : function(event)
9540     {
9541         // this is a workaround for a password hang bug on chrome/ webkit.
9542         if (this.inputEl().dom.type != 'password') {
9543             return;
9544         }
9545         
9546         var isSelectAll = false;
9547         
9548         if(this.inputEl().dom.selectionEnd > 0){
9549             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9550         }
9551         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9552             event.preventDefault();
9553             this.setValue('');
9554             return;
9555         }
9556         
9557         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9558             
9559             event.preventDefault();
9560             // this is very hacky as keydown always get's upper case.
9561             //
9562             var cc = String.fromCharCode(event.getCharCode());
9563             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9564             
9565         }
9566     },
9567     adjustWidth : function(tag, w){
9568         tag = tag.toLowerCase();
9569         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9570             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9571                 if(tag == 'input'){
9572                     return w + 2;
9573                 }
9574                 if(tag == 'textarea'){
9575                     return w-2;
9576                 }
9577             }else if(Roo.isOpera){
9578                 if(tag == 'input'){
9579                     return w + 2;
9580                 }
9581                 if(tag == 'textarea'){
9582                     return w-2;
9583                 }
9584             }
9585         }
9586         return w;
9587     },
9588     
9589     setFieldLabel : function(v)
9590     {
9591         if(!this.rendered){
9592             return;
9593         }
9594         
9595         if(this.indicator){
9596             var ar = this.el.select('label > span',true);
9597             
9598             if (ar.elements.length) {
9599                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9600                 this.fieldLabel = v;
9601                 return;
9602             }
9603             
9604             var br = this.el.select('label',true);
9605             
9606             if(br.elements.length) {
9607                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9608                 this.fieldLabel = v;
9609                 return;
9610             }
9611             
9612             Roo.log('Cannot Found any of label > span || label in input');
9613             return;
9614         }
9615         
9616         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9617         this.fieldLabel = v;
9618         
9619         
9620     }
9621 });
9622
9623  
9624 /*
9625  * - LGPL
9626  *
9627  * Input
9628  * 
9629  */
9630
9631 /**
9632  * @class Roo.bootstrap.TextArea
9633  * @extends Roo.bootstrap.Input
9634  * Bootstrap TextArea class
9635  * @cfg {Number} cols Specifies the visible width of a text area
9636  * @cfg {Number} rows Specifies the visible number of lines in a text area
9637  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9638  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9639  * @cfg {string} html text
9640  * 
9641  * @constructor
9642  * Create a new TextArea
9643  * @param {Object} config The config object
9644  */
9645
9646 Roo.bootstrap.TextArea = function(config){
9647     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9648    
9649 };
9650
9651 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9652      
9653     cols : false,
9654     rows : 5,
9655     readOnly : false,
9656     warp : 'soft',
9657     resize : false,
9658     value: false,
9659     html: false,
9660     
9661     getAutoCreate : function(){
9662         
9663         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9664         
9665         var id = Roo.id();
9666         
9667         var cfg = {};
9668         
9669         if(this.inputType != 'hidden'){
9670             cfg.cls = 'form-group' //input-group
9671         }
9672         
9673         var input =  {
9674             tag: 'textarea',
9675             id : id,
9676             warp : this.warp,
9677             rows : this.rows,
9678             value : this.value || '',
9679             html: this.html || '',
9680             cls : 'form-control',
9681             placeholder : this.placeholder || '' 
9682             
9683         };
9684         
9685         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9686             input.maxLength = this.maxLength;
9687         }
9688         
9689         if(this.resize){
9690             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9691         }
9692         
9693         if(this.cols){
9694             input.cols = this.cols;
9695         }
9696         
9697         if (this.readOnly) {
9698             input.readonly = true;
9699         }
9700         
9701         if (this.name) {
9702             input.name = this.name;
9703         }
9704         
9705         if (this.size) {
9706             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9707         }
9708         
9709         var settings=this;
9710         ['xs','sm','md','lg'].map(function(size){
9711             if (settings[size]) {
9712                 cfg.cls += ' col-' + size + '-' + settings[size];
9713             }
9714         });
9715         
9716         var inputblock = input;
9717         
9718         if(this.hasFeedback && !this.allowBlank){
9719             
9720             var feedback = {
9721                 tag: 'span',
9722                 cls: 'glyphicon form-control-feedback'
9723             };
9724
9725             inputblock = {
9726                 cls : 'has-feedback',
9727                 cn :  [
9728                     input,
9729                     feedback
9730                 ] 
9731             };  
9732         }
9733         
9734         
9735         if (this.before || this.after) {
9736             
9737             inputblock = {
9738                 cls : 'input-group',
9739                 cn :  [] 
9740             };
9741             if (this.before) {
9742                 inputblock.cn.push({
9743                     tag :'span',
9744                     cls : 'input-group-addon',
9745                     html : this.before
9746                 });
9747             }
9748             
9749             inputblock.cn.push(input);
9750             
9751             if(this.hasFeedback && !this.allowBlank){
9752                 inputblock.cls += ' has-feedback';
9753                 inputblock.cn.push(feedback);
9754             }
9755             
9756             if (this.after) {
9757                 inputblock.cn.push({
9758                     tag :'span',
9759                     cls : 'input-group-addon',
9760                     html : this.after
9761                 });
9762             }
9763             
9764         }
9765         
9766         if (align ==='left' && this.fieldLabel.length) {
9767             cfg.cn = [
9768                 {
9769                     tag: 'label',
9770                     'for' :  id,
9771                     cls : 'control-label',
9772                     html : this.fieldLabel
9773                 },
9774                 {
9775                     cls : "",
9776                     cn: [
9777                         inputblock
9778                     ]
9779                 }
9780
9781             ];
9782             
9783             if(this.labelWidth > 12){
9784                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9785             }
9786
9787             if(this.labelWidth < 13 && this.labelmd == 0){
9788                 this.labelmd = this.labelWidth;
9789             }
9790
9791             if(this.labellg > 0){
9792                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9793                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9794             }
9795
9796             if(this.labelmd > 0){
9797                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9798                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9799             }
9800
9801             if(this.labelsm > 0){
9802                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9803                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9804             }
9805
9806             if(this.labelxs > 0){
9807                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9808                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9809             }
9810             
9811         } else if ( this.fieldLabel.length) {
9812             cfg.cn = [
9813
9814                {
9815                    tag: 'label',
9816                    //cls : 'input-group-addon',
9817                    html : this.fieldLabel
9818
9819                },
9820
9821                inputblock
9822
9823            ];
9824
9825         } else {
9826
9827             cfg.cn = [
9828
9829                 inputblock
9830
9831             ];
9832                 
9833         }
9834         
9835         if (this.disabled) {
9836             input.disabled=true;
9837         }
9838         
9839         return cfg;
9840         
9841     },
9842     /**
9843      * return the real textarea element.
9844      */
9845     inputEl: function ()
9846     {
9847         return this.el.select('textarea.form-control',true).first();
9848     },
9849     
9850     /**
9851      * Clear any invalid styles/messages for this field
9852      */
9853     clearInvalid : function()
9854     {
9855         
9856         if(!this.el || this.preventMark){ // not rendered
9857             return;
9858         }
9859         
9860         var label = this.el.select('label', true).first();
9861         var icon = this.el.select('i.fa-star', true).first();
9862         
9863         if(label && icon){
9864             icon.remove();
9865         }
9866         
9867         this.el.removeClass(this.invalidClass);
9868         
9869         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9870             
9871             var feedback = this.el.select('.form-control-feedback', true).first();
9872             
9873             if(feedback){
9874                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9875             }
9876             
9877         }
9878         
9879         this.fireEvent('valid', this);
9880     },
9881     
9882      /**
9883      * Mark this field as valid
9884      */
9885     markValid : function()
9886     {
9887         if(!this.el  || this.preventMark){ // not rendered
9888             return;
9889         }
9890         
9891         this.el.removeClass([this.invalidClass, this.validClass]);
9892         
9893         var feedback = this.el.select('.form-control-feedback', true).first();
9894             
9895         if(feedback){
9896             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9897         }
9898
9899         if(this.disabled || this.allowBlank){
9900             return;
9901         }
9902         
9903         var label = this.el.select('label', true).first();
9904         var icon = this.el.select('i.fa-star', true).first();
9905         
9906         if(label && icon){
9907             icon.remove();
9908         }
9909         
9910         this.el.addClass(this.validClass);
9911         
9912         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9913             
9914             var feedback = this.el.select('.form-control-feedback', true).first();
9915             
9916             if(feedback){
9917                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9918                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9919             }
9920             
9921         }
9922         
9923         this.fireEvent('valid', this);
9924     },
9925     
9926      /**
9927      * Mark this field as invalid
9928      * @param {String} msg The validation message
9929      */
9930     markInvalid : function(msg)
9931     {
9932         if(!this.el  || this.preventMark){ // not rendered
9933             return;
9934         }
9935         
9936         this.el.removeClass([this.invalidClass, this.validClass]);
9937         
9938         var feedback = this.el.select('.form-control-feedback', true).first();
9939             
9940         if(feedback){
9941             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9942         }
9943
9944         if(this.disabled || this.allowBlank){
9945             return;
9946         }
9947         
9948         var label = this.el.select('label', true).first();
9949         var icon = this.el.select('i.fa-star', true).first();
9950         
9951         if(!this.getValue().length && label && !icon){
9952             this.el.createChild({
9953                 tag : 'i',
9954                 cls : 'text-danger fa fa-lg fa-star',
9955                 tooltip : 'This field is required',
9956                 style : 'margin-right:5px;'
9957             }, label, true);
9958         }
9959
9960         this.el.addClass(this.invalidClass);
9961         
9962         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9963             
9964             var feedback = this.el.select('.form-control-feedback', true).first();
9965             
9966             if(feedback){
9967                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9968                 
9969                 if(this.getValue().length || this.forceFeedback){
9970                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9971                 }
9972                 
9973             }
9974             
9975         }
9976         
9977         this.fireEvent('invalid', this, msg);
9978     }
9979 });
9980
9981  
9982 /*
9983  * - LGPL
9984  *
9985  * trigger field - base class for combo..
9986  * 
9987  */
9988  
9989 /**
9990  * @class Roo.bootstrap.TriggerField
9991  * @extends Roo.bootstrap.Input
9992  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9993  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9994  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9995  * for which you can provide a custom implementation.  For example:
9996  * <pre><code>
9997 var trigger = new Roo.bootstrap.TriggerField();
9998 trigger.onTriggerClick = myTriggerFn;
9999 trigger.applyTo('my-field');
10000 </code></pre>
10001  *
10002  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10003  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10004  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10005  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10006  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10007
10008  * @constructor
10009  * Create a new TriggerField.
10010  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10011  * to the base TextField)
10012  */
10013 Roo.bootstrap.TriggerField = function(config){
10014     this.mimicing = false;
10015     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10016 };
10017
10018 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10019     /**
10020      * @cfg {String} triggerClass A CSS class to apply to the trigger
10021      */
10022      /**
10023      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10024      */
10025     hideTrigger:false,
10026
10027     /**
10028      * @cfg {Boolean} removable (true|false) special filter default false
10029      */
10030     removable : false,
10031     
10032     /** @cfg {Boolean} grow @hide */
10033     /** @cfg {Number} growMin @hide */
10034     /** @cfg {Number} growMax @hide */
10035
10036     /**
10037      * @hide 
10038      * @method
10039      */
10040     autoSize: Roo.emptyFn,
10041     // private
10042     monitorTab : true,
10043     // private
10044     deferHeight : true,
10045
10046     
10047     actionMode : 'wrap',
10048     
10049     caret : false,
10050     
10051     
10052     getAutoCreate : function(){
10053        
10054         var align = this.labelAlign || this.parentLabelAlign();
10055         
10056         var id = Roo.id();
10057         
10058         var cfg = {
10059             cls: 'form-group' //input-group
10060         };
10061         
10062         
10063         var input =  {
10064             tag: 'input',
10065             id : id,
10066             type : this.inputType,
10067             cls : 'form-control',
10068             autocomplete: 'new-password',
10069             placeholder : this.placeholder || '' 
10070             
10071         };
10072         if (this.name) {
10073             input.name = this.name;
10074         }
10075         if (this.size) {
10076             input.cls += ' input-' + this.size;
10077         }
10078         
10079         if (this.disabled) {
10080             input.disabled=true;
10081         }
10082         
10083         var inputblock = input;
10084         
10085         if(this.hasFeedback && !this.allowBlank){
10086             
10087             var feedback = {
10088                 tag: 'span',
10089                 cls: 'glyphicon form-control-feedback'
10090             };
10091             
10092             if(this.removable && !this.editable && !this.tickable){
10093                 inputblock = {
10094                     cls : 'has-feedback',
10095                     cn :  [
10096                         inputblock,
10097                         {
10098                             tag: 'button',
10099                             html : 'x',
10100                             cls : 'roo-combo-removable-btn close'
10101                         },
10102                         feedback
10103                     ] 
10104                 };
10105             } else {
10106                 inputblock = {
10107                     cls : 'has-feedback',
10108                     cn :  [
10109                         inputblock,
10110                         feedback
10111                     ] 
10112                 };
10113             }
10114
10115         } else {
10116             if(this.removable && !this.editable && !this.tickable){
10117                 inputblock = {
10118                     cls : 'roo-removable',
10119                     cn :  [
10120                         inputblock,
10121                         {
10122                             tag: 'button',
10123                             html : 'x',
10124                             cls : 'roo-combo-removable-btn close'
10125                         }
10126                     ] 
10127                 };
10128             }
10129         }
10130         
10131         if (this.before || this.after) {
10132             
10133             inputblock = {
10134                 cls : 'input-group',
10135                 cn :  [] 
10136             };
10137             if (this.before) {
10138                 inputblock.cn.push({
10139                     tag :'span',
10140                     cls : 'input-group-addon',
10141                     html : this.before
10142                 });
10143             }
10144             
10145             inputblock.cn.push(input);
10146             
10147             if(this.hasFeedback && !this.allowBlank){
10148                 inputblock.cls += ' has-feedback';
10149                 inputblock.cn.push(feedback);
10150             }
10151             
10152             if (this.after) {
10153                 inputblock.cn.push({
10154                     tag :'span',
10155                     cls : 'input-group-addon',
10156                     html : this.after
10157                 });
10158             }
10159             
10160         };
10161         
10162         var box = {
10163             tag: 'div',
10164             cn: [
10165                 {
10166                     tag: 'input',
10167                     type : 'hidden',
10168                     cls: 'form-hidden-field'
10169                 },
10170                 inputblock
10171             ]
10172             
10173         };
10174         
10175         if(this.multiple){
10176             box = {
10177                 tag: 'div',
10178                 cn: [
10179                     {
10180                         tag: 'input',
10181                         type : 'hidden',
10182                         cls: 'form-hidden-field'
10183                     },
10184                     {
10185                         tag: 'ul',
10186                         cls: 'roo-select2-choices',
10187                         cn:[
10188                             {
10189                                 tag: 'li',
10190                                 cls: 'roo-select2-search-field',
10191                                 cn: [
10192
10193                                     inputblock
10194                                 ]
10195                             }
10196                         ]
10197                     }
10198                 ]
10199             }
10200         };
10201         
10202         var combobox = {
10203             cls: 'roo-select2-container input-group',
10204             cn: [
10205                 box
10206 //                {
10207 //                    tag: 'ul',
10208 //                    cls: 'typeahead typeahead-long dropdown-menu',
10209 //                    style: 'display:none'
10210 //                }
10211             ]
10212         };
10213         
10214         if(!this.multiple && this.showToggleBtn){
10215             
10216             var caret = {
10217                         tag: 'span',
10218                         cls: 'caret'
10219              };
10220             if (this.caret != false) {
10221                 caret = {
10222                      tag: 'i',
10223                      cls: 'fa fa-' + this.caret
10224                 };
10225                 
10226             }
10227             
10228             combobox.cn.push({
10229                 tag :'span',
10230                 cls : 'input-group-addon btn dropdown-toggle',
10231                 cn : [
10232                     caret,
10233                     {
10234                         tag: 'span',
10235                         cls: 'combobox-clear',
10236                         cn  : [
10237                             {
10238                                 tag : 'i',
10239                                 cls: 'icon-remove'
10240                             }
10241                         ]
10242                     }
10243                 ]
10244
10245             })
10246         }
10247         
10248         if(this.multiple){
10249             combobox.cls += ' roo-select2-container-multi';
10250         }
10251         
10252         if (align ==='left' && this.fieldLabel.length) {
10253             
10254             cfg.cls += ' roo-form-group-label-left';
10255
10256             cfg.cn = [
10257                 {
10258                     tag : 'i',
10259                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10260                     tooltip : 'This field is required'
10261                 },
10262                 {
10263                     tag: 'label',
10264                     'for' :  id,
10265                     cls : 'control-label',
10266                     html : this.fieldLabel
10267
10268                 },
10269                 {
10270                     cls : "", 
10271                     cn: [
10272                         combobox
10273                     ]
10274                 }
10275
10276             ];
10277             
10278             var labelCfg = cfg.cn[1];
10279             var contentCfg = cfg.cn[2];
10280             
10281             if(this.indicatorpos == 'right'){
10282                 cfg.cn = [
10283                     {
10284                         tag: 'label',
10285                         'for' :  id,
10286                         cls : 'control-label',
10287                         cn : [
10288                             {
10289                                 tag : 'span',
10290                                 html : this.fieldLabel
10291                             },
10292                             {
10293                                 tag : 'i',
10294                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10295                                 tooltip : 'This field is required'
10296                             }
10297                         ]
10298                     },
10299                     {
10300                         cls : "", 
10301                         cn: [
10302                             combobox
10303                         ]
10304                     }
10305
10306                 ];
10307                 
10308                 labelCfg = cfg.cn[0];
10309                 contentCfg = cfg.cn[1];
10310             }
10311             
10312             if(this.labelWidth > 12){
10313                 labelCfg.style = "width: " + this.labelWidth + 'px';
10314             }
10315             
10316             if(this.labelWidth < 13 && this.labelmd == 0){
10317                 this.labelmd = this.labelWidth;
10318             }
10319             
10320             if(this.labellg > 0){
10321                 labelCfg.cls += ' col-lg-' + this.labellg;
10322                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10323             }
10324             
10325             if(this.labelmd > 0){
10326                 labelCfg.cls += ' col-md-' + this.labelmd;
10327                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10328             }
10329             
10330             if(this.labelsm > 0){
10331                 labelCfg.cls += ' col-sm-' + this.labelsm;
10332                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10333             }
10334             
10335             if(this.labelxs > 0){
10336                 labelCfg.cls += ' col-xs-' + this.labelxs;
10337                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10338             }
10339             
10340         } else if ( this.fieldLabel.length) {
10341 //                Roo.log(" label");
10342             cfg.cn = [
10343                 {
10344                    tag : 'i',
10345                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10346                    tooltip : 'This field is required'
10347                },
10348                {
10349                    tag: 'label',
10350                    //cls : 'input-group-addon',
10351                    html : this.fieldLabel
10352
10353                },
10354
10355                combobox
10356
10357             ];
10358             
10359             if(this.indicatorpos == 'right'){
10360                 
10361                 cfg.cn = [
10362                     {
10363                        tag: 'label',
10364                        cn : [
10365                            {
10366                                tag : 'span',
10367                                html : this.fieldLabel
10368                            },
10369                            {
10370                               tag : 'i',
10371                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10372                               tooltip : 'This field is required'
10373                            }
10374                        ]
10375
10376                     },
10377                     combobox
10378
10379                 ];
10380
10381             }
10382
10383         } else {
10384             
10385 //                Roo.log(" no label && no align");
10386                 cfg = combobox
10387                      
10388                 
10389         }
10390         
10391         var settings=this;
10392         ['xs','sm','md','lg'].map(function(size){
10393             if (settings[size]) {
10394                 cfg.cls += ' col-' + size + '-' + settings[size];
10395             }
10396         });
10397         
10398         return cfg;
10399         
10400     },
10401     
10402     
10403     
10404     // private
10405     onResize : function(w, h){
10406 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10407 //        if(typeof w == 'number'){
10408 //            var x = w - this.trigger.getWidth();
10409 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10410 //            this.trigger.setStyle('left', x+'px');
10411 //        }
10412     },
10413
10414     // private
10415     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10416
10417     // private
10418     getResizeEl : function(){
10419         return this.inputEl();
10420     },
10421
10422     // private
10423     getPositionEl : function(){
10424         return this.inputEl();
10425     },
10426
10427     // private
10428     alignErrorIcon : function(){
10429         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10430     },
10431
10432     // private
10433     initEvents : function(){
10434         
10435         this.createList();
10436         
10437         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10438         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10439         if(!this.multiple && this.showToggleBtn){
10440             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10441             if(this.hideTrigger){
10442                 this.trigger.setDisplayed(false);
10443             }
10444             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10445         }
10446         
10447         if(this.multiple){
10448             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10449         }
10450         
10451         if(this.removable && !this.editable && !this.tickable){
10452             var close = this.closeTriggerEl();
10453             
10454             if(close){
10455                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10456                 close.on('click', this.removeBtnClick, this, close);
10457             }
10458         }
10459         
10460         //this.trigger.addClassOnOver('x-form-trigger-over');
10461         //this.trigger.addClassOnClick('x-form-trigger-click');
10462         
10463         //if(!this.width){
10464         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10465         //}
10466     },
10467     
10468     closeTriggerEl : function()
10469     {
10470         var close = this.el.select('.roo-combo-removable-btn', true).first();
10471         return close ? close : false;
10472     },
10473     
10474     removeBtnClick : function(e, h, el)
10475     {
10476         e.preventDefault();
10477         
10478         if(this.fireEvent("remove", this) !== false){
10479             this.reset();
10480             this.fireEvent("afterremove", this)
10481         }
10482     },
10483     
10484     createList : function()
10485     {
10486         this.list = Roo.get(document.body).createChild({
10487             tag: 'ul',
10488             cls: 'typeahead typeahead-long dropdown-menu',
10489             style: 'display:none'
10490         });
10491         
10492         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10493         
10494     },
10495
10496     // private
10497     initTrigger : function(){
10498        
10499     },
10500
10501     // private
10502     onDestroy : function(){
10503         if(this.trigger){
10504             this.trigger.removeAllListeners();
10505           //  this.trigger.remove();
10506         }
10507         //if(this.wrap){
10508         //    this.wrap.remove();
10509         //}
10510         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10511     },
10512
10513     // private
10514     onFocus : function(){
10515         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10516         /*
10517         if(!this.mimicing){
10518             this.wrap.addClass('x-trigger-wrap-focus');
10519             this.mimicing = true;
10520             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10521             if(this.monitorTab){
10522                 this.el.on("keydown", this.checkTab, this);
10523             }
10524         }
10525         */
10526     },
10527
10528     // private
10529     checkTab : function(e){
10530         if(e.getKey() == e.TAB){
10531             this.triggerBlur();
10532         }
10533     },
10534
10535     // private
10536     onBlur : function(){
10537         // do nothing
10538     },
10539
10540     // private
10541     mimicBlur : function(e, t){
10542         /*
10543         if(!this.wrap.contains(t) && this.validateBlur()){
10544             this.triggerBlur();
10545         }
10546         */
10547     },
10548
10549     // private
10550     triggerBlur : function(){
10551         this.mimicing = false;
10552         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10553         if(this.monitorTab){
10554             this.el.un("keydown", this.checkTab, this);
10555         }
10556         //this.wrap.removeClass('x-trigger-wrap-focus');
10557         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10558     },
10559
10560     // private
10561     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10562     validateBlur : function(e, t){
10563         return true;
10564     },
10565
10566     // private
10567     onDisable : function(){
10568         this.inputEl().dom.disabled = true;
10569         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10570         //if(this.wrap){
10571         //    this.wrap.addClass('x-item-disabled');
10572         //}
10573     },
10574
10575     // private
10576     onEnable : function(){
10577         this.inputEl().dom.disabled = false;
10578         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10579         //if(this.wrap){
10580         //    this.el.removeClass('x-item-disabled');
10581         //}
10582     },
10583
10584     // private
10585     onShow : function(){
10586         var ae = this.getActionEl();
10587         
10588         if(ae){
10589             ae.dom.style.display = '';
10590             ae.dom.style.visibility = 'visible';
10591         }
10592     },
10593
10594     // private
10595     
10596     onHide : function(){
10597         var ae = this.getActionEl();
10598         ae.dom.style.display = 'none';
10599     },
10600
10601     /**
10602      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10603      * by an implementing function.
10604      * @method
10605      * @param {EventObject} e
10606      */
10607     onTriggerClick : Roo.emptyFn
10608 });
10609  /*
10610  * Based on:
10611  * Ext JS Library 1.1.1
10612  * Copyright(c) 2006-2007, Ext JS, LLC.
10613  *
10614  * Originally Released Under LGPL - original licence link has changed is not relivant.
10615  *
10616  * Fork - LGPL
10617  * <script type="text/javascript">
10618  */
10619
10620
10621 /**
10622  * @class Roo.data.SortTypes
10623  * @singleton
10624  * Defines the default sorting (casting?) comparison functions used when sorting data.
10625  */
10626 Roo.data.SortTypes = {
10627     /**
10628      * Default sort that does nothing
10629      * @param {Mixed} s The value being converted
10630      * @return {Mixed} The comparison value
10631      */
10632     none : function(s){
10633         return s;
10634     },
10635     
10636     /**
10637      * The regular expression used to strip tags
10638      * @type {RegExp}
10639      * @property
10640      */
10641     stripTagsRE : /<\/?[^>]+>/gi,
10642     
10643     /**
10644      * Strips all HTML tags to sort on text only
10645      * @param {Mixed} s The value being converted
10646      * @return {String} The comparison value
10647      */
10648     asText : function(s){
10649         return String(s).replace(this.stripTagsRE, "");
10650     },
10651     
10652     /**
10653      * Strips all HTML tags to sort on text only - Case insensitive
10654      * @param {Mixed} s The value being converted
10655      * @return {String} The comparison value
10656      */
10657     asUCText : function(s){
10658         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10659     },
10660     
10661     /**
10662      * Case insensitive string
10663      * @param {Mixed} s The value being converted
10664      * @return {String} The comparison value
10665      */
10666     asUCString : function(s) {
10667         return String(s).toUpperCase();
10668     },
10669     
10670     /**
10671      * Date sorting
10672      * @param {Mixed} s The value being converted
10673      * @return {Number} The comparison value
10674      */
10675     asDate : function(s) {
10676         if(!s){
10677             return 0;
10678         }
10679         if(s instanceof Date){
10680             return s.getTime();
10681         }
10682         return Date.parse(String(s));
10683     },
10684     
10685     /**
10686      * Float sorting
10687      * @param {Mixed} s The value being converted
10688      * @return {Float} The comparison value
10689      */
10690     asFloat : function(s) {
10691         var val = parseFloat(String(s).replace(/,/g, ""));
10692         if(isNaN(val)) {
10693             val = 0;
10694         }
10695         return val;
10696     },
10697     
10698     /**
10699      * Integer sorting
10700      * @param {Mixed} s The value being converted
10701      * @return {Number} The comparison value
10702      */
10703     asInt : function(s) {
10704         var val = parseInt(String(s).replace(/,/g, ""));
10705         if(isNaN(val)) {
10706             val = 0;
10707         }
10708         return val;
10709     }
10710 };/*
10711  * Based on:
10712  * Ext JS Library 1.1.1
10713  * Copyright(c) 2006-2007, Ext JS, LLC.
10714  *
10715  * Originally Released Under LGPL - original licence link has changed is not relivant.
10716  *
10717  * Fork - LGPL
10718  * <script type="text/javascript">
10719  */
10720
10721 /**
10722 * @class Roo.data.Record
10723  * Instances of this class encapsulate both record <em>definition</em> information, and record
10724  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10725  * to access Records cached in an {@link Roo.data.Store} object.<br>
10726  * <p>
10727  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10728  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10729  * objects.<br>
10730  * <p>
10731  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10732  * @constructor
10733  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10734  * {@link #create}. The parameters are the same.
10735  * @param {Array} data An associative Array of data values keyed by the field name.
10736  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10737  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10738  * not specified an integer id is generated.
10739  */
10740 Roo.data.Record = function(data, id){
10741     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10742     this.data = data;
10743 };
10744
10745 /**
10746  * Generate a constructor for a specific record layout.
10747  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10748  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10749  * Each field definition object may contain the following properties: <ul>
10750  * <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,
10751  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10752  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10753  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10754  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10755  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10756  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10757  * this may be omitted.</p></li>
10758  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10759  * <ul><li>auto (Default, implies no conversion)</li>
10760  * <li>string</li>
10761  * <li>int</li>
10762  * <li>float</li>
10763  * <li>boolean</li>
10764  * <li>date</li></ul></p></li>
10765  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10766  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10767  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10768  * by the Reader into an object that will be stored in the Record. It is passed the
10769  * following parameters:<ul>
10770  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10771  * </ul></p></li>
10772  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10773  * </ul>
10774  * <br>usage:<br><pre><code>
10775 var TopicRecord = Roo.data.Record.create(
10776     {name: 'title', mapping: 'topic_title'},
10777     {name: 'author', mapping: 'username'},
10778     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10779     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10780     {name: 'lastPoster', mapping: 'user2'},
10781     {name: 'excerpt', mapping: 'post_text'}
10782 );
10783
10784 var myNewRecord = new TopicRecord({
10785     title: 'Do my job please',
10786     author: 'noobie',
10787     totalPosts: 1,
10788     lastPost: new Date(),
10789     lastPoster: 'Animal',
10790     excerpt: 'No way dude!'
10791 });
10792 myStore.add(myNewRecord);
10793 </code></pre>
10794  * @method create
10795  * @static
10796  */
10797 Roo.data.Record.create = function(o){
10798     var f = function(){
10799         f.superclass.constructor.apply(this, arguments);
10800     };
10801     Roo.extend(f, Roo.data.Record);
10802     var p = f.prototype;
10803     p.fields = new Roo.util.MixedCollection(false, function(field){
10804         return field.name;
10805     });
10806     for(var i = 0, len = o.length; i < len; i++){
10807         p.fields.add(new Roo.data.Field(o[i]));
10808     }
10809     f.getField = function(name){
10810         return p.fields.get(name);  
10811     };
10812     return f;
10813 };
10814
10815 Roo.data.Record.AUTO_ID = 1000;
10816 Roo.data.Record.EDIT = 'edit';
10817 Roo.data.Record.REJECT = 'reject';
10818 Roo.data.Record.COMMIT = 'commit';
10819
10820 Roo.data.Record.prototype = {
10821     /**
10822      * Readonly flag - true if this record has been modified.
10823      * @type Boolean
10824      */
10825     dirty : false,
10826     editing : false,
10827     error: null,
10828     modified: null,
10829
10830     // private
10831     join : function(store){
10832         this.store = store;
10833     },
10834
10835     /**
10836      * Set the named field to the specified value.
10837      * @param {String} name The name of the field to set.
10838      * @param {Object} value The value to set the field to.
10839      */
10840     set : function(name, value){
10841         if(this.data[name] == value){
10842             return;
10843         }
10844         this.dirty = true;
10845         if(!this.modified){
10846             this.modified = {};
10847         }
10848         if(typeof this.modified[name] == 'undefined'){
10849             this.modified[name] = this.data[name];
10850         }
10851         this.data[name] = value;
10852         if(!this.editing && this.store){
10853             this.store.afterEdit(this);
10854         }       
10855     },
10856
10857     /**
10858      * Get the value of the named field.
10859      * @param {String} name The name of the field to get the value of.
10860      * @return {Object} The value of the field.
10861      */
10862     get : function(name){
10863         return this.data[name]; 
10864     },
10865
10866     // private
10867     beginEdit : function(){
10868         this.editing = true;
10869         this.modified = {}; 
10870     },
10871
10872     // private
10873     cancelEdit : function(){
10874         this.editing = false;
10875         delete this.modified;
10876     },
10877
10878     // private
10879     endEdit : function(){
10880         this.editing = false;
10881         if(this.dirty && this.store){
10882             this.store.afterEdit(this);
10883         }
10884     },
10885
10886     /**
10887      * Usually called by the {@link Roo.data.Store} which owns the Record.
10888      * Rejects all changes made to the Record since either creation, or the last commit operation.
10889      * Modified fields are reverted to their original values.
10890      * <p>
10891      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10892      * of reject operations.
10893      */
10894     reject : function(){
10895         var m = this.modified;
10896         for(var n in m){
10897             if(typeof m[n] != "function"){
10898                 this.data[n] = m[n];
10899             }
10900         }
10901         this.dirty = false;
10902         delete this.modified;
10903         this.editing = false;
10904         if(this.store){
10905             this.store.afterReject(this);
10906         }
10907     },
10908
10909     /**
10910      * Usually called by the {@link Roo.data.Store} which owns the Record.
10911      * Commits all changes made to the Record since either creation, or the last commit operation.
10912      * <p>
10913      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10914      * of commit operations.
10915      */
10916     commit : function(){
10917         this.dirty = false;
10918         delete this.modified;
10919         this.editing = false;
10920         if(this.store){
10921             this.store.afterCommit(this);
10922         }
10923     },
10924
10925     // private
10926     hasError : function(){
10927         return this.error != null;
10928     },
10929
10930     // private
10931     clearError : function(){
10932         this.error = null;
10933     },
10934
10935     /**
10936      * Creates a copy of this record.
10937      * @param {String} id (optional) A new record id if you don't want to use this record's id
10938      * @return {Record}
10939      */
10940     copy : function(newId) {
10941         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10942     }
10943 };/*
10944  * Based on:
10945  * Ext JS Library 1.1.1
10946  * Copyright(c) 2006-2007, Ext JS, LLC.
10947  *
10948  * Originally Released Under LGPL - original licence link has changed is not relivant.
10949  *
10950  * Fork - LGPL
10951  * <script type="text/javascript">
10952  */
10953
10954
10955
10956 /**
10957  * @class Roo.data.Store
10958  * @extends Roo.util.Observable
10959  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10960  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10961  * <p>
10962  * 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
10963  * has no knowledge of the format of the data returned by the Proxy.<br>
10964  * <p>
10965  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10966  * instances from the data object. These records are cached and made available through accessor functions.
10967  * @constructor
10968  * Creates a new Store.
10969  * @param {Object} config A config object containing the objects needed for the Store to access data,
10970  * and read the data into Records.
10971  */
10972 Roo.data.Store = function(config){
10973     this.data = new Roo.util.MixedCollection(false);
10974     this.data.getKey = function(o){
10975         return o.id;
10976     };
10977     this.baseParams = {};
10978     // private
10979     this.paramNames = {
10980         "start" : "start",
10981         "limit" : "limit",
10982         "sort" : "sort",
10983         "dir" : "dir",
10984         "multisort" : "_multisort"
10985     };
10986
10987     if(config && config.data){
10988         this.inlineData = config.data;
10989         delete config.data;
10990     }
10991
10992     Roo.apply(this, config);
10993     
10994     if(this.reader){ // reader passed
10995         this.reader = Roo.factory(this.reader, Roo.data);
10996         this.reader.xmodule = this.xmodule || false;
10997         if(!this.recordType){
10998             this.recordType = this.reader.recordType;
10999         }
11000         if(this.reader.onMetaChange){
11001             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11002         }
11003     }
11004
11005     if(this.recordType){
11006         this.fields = this.recordType.prototype.fields;
11007     }
11008     this.modified = [];
11009
11010     this.addEvents({
11011         /**
11012          * @event datachanged
11013          * Fires when the data cache has changed, and a widget which is using this Store
11014          * as a Record cache should refresh its view.
11015          * @param {Store} this
11016          */
11017         datachanged : true,
11018         /**
11019          * @event metachange
11020          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11021          * @param {Store} this
11022          * @param {Object} meta The JSON metadata
11023          */
11024         metachange : true,
11025         /**
11026          * @event add
11027          * Fires when Records have been added to the Store
11028          * @param {Store} this
11029          * @param {Roo.data.Record[]} records The array of Records added
11030          * @param {Number} index The index at which the record(s) were added
11031          */
11032         add : true,
11033         /**
11034          * @event remove
11035          * Fires when a Record has been removed from the Store
11036          * @param {Store} this
11037          * @param {Roo.data.Record} record The Record that was removed
11038          * @param {Number} index The index at which the record was removed
11039          */
11040         remove : true,
11041         /**
11042          * @event update
11043          * Fires when a Record has been updated
11044          * @param {Store} this
11045          * @param {Roo.data.Record} record The Record that was updated
11046          * @param {String} operation The update operation being performed.  Value may be one of:
11047          * <pre><code>
11048  Roo.data.Record.EDIT
11049  Roo.data.Record.REJECT
11050  Roo.data.Record.COMMIT
11051          * </code></pre>
11052          */
11053         update : true,
11054         /**
11055          * @event clear
11056          * Fires when the data cache has been cleared.
11057          * @param {Store} this
11058          */
11059         clear : true,
11060         /**
11061          * @event beforeload
11062          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11063          * the load action will be canceled.
11064          * @param {Store} this
11065          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11066          */
11067         beforeload : true,
11068         /**
11069          * @event beforeloadadd
11070          * Fires after a new set of Records has been loaded.
11071          * @param {Store} this
11072          * @param {Roo.data.Record[]} records The Records that were loaded
11073          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11074          */
11075         beforeloadadd : true,
11076         /**
11077          * @event load
11078          * Fires after a new set of Records has been loaded, before they are added to the store.
11079          * @param {Store} this
11080          * @param {Roo.data.Record[]} records The Records that were loaded
11081          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11082          * @params {Object} return from reader
11083          */
11084         load : true,
11085         /**
11086          * @event loadexception
11087          * Fires if an exception occurs in the Proxy during loading.
11088          * Called with the signature of the Proxy's "loadexception" event.
11089          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11090          * 
11091          * @param {Proxy} 
11092          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11093          * @param {Object} load options 
11094          * @param {Object} jsonData from your request (normally this contains the Exception)
11095          */
11096         loadexception : true
11097     });
11098     
11099     if(this.proxy){
11100         this.proxy = Roo.factory(this.proxy, Roo.data);
11101         this.proxy.xmodule = this.xmodule || false;
11102         this.relayEvents(this.proxy,  ["loadexception"]);
11103     }
11104     this.sortToggle = {};
11105     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11106
11107     Roo.data.Store.superclass.constructor.call(this);
11108
11109     if(this.inlineData){
11110         this.loadData(this.inlineData);
11111         delete this.inlineData;
11112     }
11113 };
11114
11115 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11116      /**
11117     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11118     * without a remote query - used by combo/forms at present.
11119     */
11120     
11121     /**
11122     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11123     */
11124     /**
11125     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11126     */
11127     /**
11128     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11129     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11130     */
11131     /**
11132     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11133     * on any HTTP request
11134     */
11135     /**
11136     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11137     */
11138     /**
11139     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11140     */
11141     multiSort: false,
11142     /**
11143     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11144     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11145     */
11146     remoteSort : false,
11147
11148     /**
11149     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11150      * loaded or when a record is removed. (defaults to false).
11151     */
11152     pruneModifiedRecords : false,
11153
11154     // private
11155     lastOptions : null,
11156
11157     /**
11158      * Add Records to the Store and fires the add event.
11159      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11160      */
11161     add : function(records){
11162         records = [].concat(records);
11163         for(var i = 0, len = records.length; i < len; i++){
11164             records[i].join(this);
11165         }
11166         var index = this.data.length;
11167         this.data.addAll(records);
11168         this.fireEvent("add", this, records, index);
11169     },
11170
11171     /**
11172      * Remove a Record from the Store and fires the remove event.
11173      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11174      */
11175     remove : function(record){
11176         var index = this.data.indexOf(record);
11177         this.data.removeAt(index);
11178  
11179         if(this.pruneModifiedRecords){
11180             this.modified.remove(record);
11181         }
11182         this.fireEvent("remove", this, record, index);
11183     },
11184
11185     /**
11186      * Remove all Records from the Store and fires the clear event.
11187      */
11188     removeAll : function(){
11189         this.data.clear();
11190         if(this.pruneModifiedRecords){
11191             this.modified = [];
11192         }
11193         this.fireEvent("clear", this);
11194     },
11195
11196     /**
11197      * Inserts Records to the Store at the given index and fires the add event.
11198      * @param {Number} index The start index at which to insert the passed Records.
11199      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11200      */
11201     insert : function(index, records){
11202         records = [].concat(records);
11203         for(var i = 0, len = records.length; i < len; i++){
11204             this.data.insert(index, records[i]);
11205             records[i].join(this);
11206         }
11207         this.fireEvent("add", this, records, index);
11208     },
11209
11210     /**
11211      * Get the index within the cache of the passed Record.
11212      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11213      * @return {Number} The index of the passed Record. Returns -1 if not found.
11214      */
11215     indexOf : function(record){
11216         return this.data.indexOf(record);
11217     },
11218
11219     /**
11220      * Get the index within the cache of the Record with the passed id.
11221      * @param {String} id The id of the Record to find.
11222      * @return {Number} The index of the Record. Returns -1 if not found.
11223      */
11224     indexOfId : function(id){
11225         return this.data.indexOfKey(id);
11226     },
11227
11228     /**
11229      * Get the Record with the specified id.
11230      * @param {String} id The id of the Record to find.
11231      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11232      */
11233     getById : function(id){
11234         return this.data.key(id);
11235     },
11236
11237     /**
11238      * Get the Record at the specified index.
11239      * @param {Number} index The index of the Record to find.
11240      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11241      */
11242     getAt : function(index){
11243         return this.data.itemAt(index);
11244     },
11245
11246     /**
11247      * Returns a range of Records between specified indices.
11248      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11249      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11250      * @return {Roo.data.Record[]} An array of Records
11251      */
11252     getRange : function(start, end){
11253         return this.data.getRange(start, end);
11254     },
11255
11256     // private
11257     storeOptions : function(o){
11258         o = Roo.apply({}, o);
11259         delete o.callback;
11260         delete o.scope;
11261         this.lastOptions = o;
11262     },
11263
11264     /**
11265      * Loads the Record cache from the configured Proxy using the configured Reader.
11266      * <p>
11267      * If using remote paging, then the first load call must specify the <em>start</em>
11268      * and <em>limit</em> properties in the options.params property to establish the initial
11269      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11270      * <p>
11271      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11272      * and this call will return before the new data has been loaded. Perform any post-processing
11273      * in a callback function, or in a "load" event handler.</strong>
11274      * <p>
11275      * @param {Object} options An object containing properties which control loading options:<ul>
11276      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11277      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11278      * passed the following arguments:<ul>
11279      * <li>r : Roo.data.Record[]</li>
11280      * <li>options: Options object from the load call</li>
11281      * <li>success: Boolean success indicator</li></ul></li>
11282      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11283      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11284      * </ul>
11285      */
11286     load : function(options){
11287         options = options || {};
11288         if(this.fireEvent("beforeload", this, options) !== false){
11289             this.storeOptions(options);
11290             var p = Roo.apply(options.params || {}, this.baseParams);
11291             // if meta was not loaded from remote source.. try requesting it.
11292             if (!this.reader.metaFromRemote) {
11293                 p._requestMeta = 1;
11294             }
11295             if(this.sortInfo && this.remoteSort){
11296                 var pn = this.paramNames;
11297                 p[pn["sort"]] = this.sortInfo.field;
11298                 p[pn["dir"]] = this.sortInfo.direction;
11299             }
11300             if (this.multiSort) {
11301                 var pn = this.paramNames;
11302                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11303             }
11304             
11305             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11306         }
11307     },
11308
11309     /**
11310      * Reloads the Record cache from the configured Proxy using the configured Reader and
11311      * the options from the last load operation performed.
11312      * @param {Object} options (optional) An object containing properties which may override the options
11313      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11314      * the most recently used options are reused).
11315      */
11316     reload : function(options){
11317         this.load(Roo.applyIf(options||{}, this.lastOptions));
11318     },
11319
11320     // private
11321     // Called as a callback by the Reader during a load operation.
11322     loadRecords : function(o, options, success){
11323         if(!o || success === false){
11324             if(success !== false){
11325                 this.fireEvent("load", this, [], options, o);
11326             }
11327             if(options.callback){
11328                 options.callback.call(options.scope || this, [], options, false);
11329             }
11330             return;
11331         }
11332         // if data returned failure - throw an exception.
11333         if (o.success === false) {
11334             // show a message if no listener is registered.
11335             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11336                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11337             }
11338             // loadmask wil be hooked into this..
11339             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11340             return;
11341         }
11342         var r = o.records, t = o.totalRecords || r.length;
11343         
11344         this.fireEvent("beforeloadadd", this, r, options, o);
11345         
11346         if(!options || options.add !== true){
11347             if(this.pruneModifiedRecords){
11348                 this.modified = [];
11349             }
11350             for(var i = 0, len = r.length; i < len; i++){
11351                 r[i].join(this);
11352             }
11353             if(this.snapshot){
11354                 this.data = this.snapshot;
11355                 delete this.snapshot;
11356             }
11357             this.data.clear();
11358             this.data.addAll(r);
11359             this.totalLength = t;
11360             this.applySort();
11361             this.fireEvent("datachanged", this);
11362         }else{
11363             this.totalLength = Math.max(t, this.data.length+r.length);
11364             this.add(r);
11365         }
11366         
11367         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11368                 
11369             var e = new Roo.data.Record({});
11370
11371             e.set(this.parent.displayField, this.parent.emptyTitle);
11372             e.set(this.parent.valueField, '');
11373
11374             this.insert(0, e);
11375         }
11376             
11377         this.fireEvent("load", this, r, options, o);
11378         if(options.callback){
11379             options.callback.call(options.scope || this, r, options, true);
11380         }
11381     },
11382
11383
11384     /**
11385      * Loads data from a passed data block. A Reader which understands the format of the data
11386      * must have been configured in the constructor.
11387      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11388      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11389      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11390      */
11391     loadData : function(o, append){
11392         var r = this.reader.readRecords(o);
11393         this.loadRecords(r, {add: append}, true);
11394     },
11395
11396     /**
11397      * Gets the number of cached records.
11398      * <p>
11399      * <em>If using paging, this may not be the total size of the dataset. If the data object
11400      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11401      * the data set size</em>
11402      */
11403     getCount : function(){
11404         return this.data.length || 0;
11405     },
11406
11407     /**
11408      * Gets the total number of records in the dataset as returned by the server.
11409      * <p>
11410      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11411      * the dataset size</em>
11412      */
11413     getTotalCount : function(){
11414         return this.totalLength || 0;
11415     },
11416
11417     /**
11418      * Returns the sort state of the Store as an object with two properties:
11419      * <pre><code>
11420  field {String} The name of the field by which the Records are sorted
11421  direction {String} The sort order, "ASC" or "DESC"
11422      * </code></pre>
11423      */
11424     getSortState : function(){
11425         return this.sortInfo;
11426     },
11427
11428     // private
11429     applySort : function(){
11430         if(this.sortInfo && !this.remoteSort){
11431             var s = this.sortInfo, f = s.field;
11432             var st = this.fields.get(f).sortType;
11433             var fn = function(r1, r2){
11434                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11435                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11436             };
11437             this.data.sort(s.direction, fn);
11438             if(this.snapshot && this.snapshot != this.data){
11439                 this.snapshot.sort(s.direction, fn);
11440             }
11441         }
11442     },
11443
11444     /**
11445      * Sets the default sort column and order to be used by the next load operation.
11446      * @param {String} fieldName The name of the field to sort by.
11447      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11448      */
11449     setDefaultSort : function(field, dir){
11450         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11451     },
11452
11453     /**
11454      * Sort the Records.
11455      * If remote sorting is used, the sort is performed on the server, and the cache is
11456      * reloaded. If local sorting is used, the cache is sorted internally.
11457      * @param {String} fieldName The name of the field to sort by.
11458      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11459      */
11460     sort : function(fieldName, dir){
11461         var f = this.fields.get(fieldName);
11462         if(!dir){
11463             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11464             
11465             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11466                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11467             }else{
11468                 dir = f.sortDir;
11469             }
11470         }
11471         this.sortToggle[f.name] = dir;
11472         this.sortInfo = {field: f.name, direction: dir};
11473         if(!this.remoteSort){
11474             this.applySort();
11475             this.fireEvent("datachanged", this);
11476         }else{
11477             this.load(this.lastOptions);
11478         }
11479     },
11480
11481     /**
11482      * Calls the specified function for each of the Records in the cache.
11483      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11484      * Returning <em>false</em> aborts and exits the iteration.
11485      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11486      */
11487     each : function(fn, scope){
11488         this.data.each(fn, scope);
11489     },
11490
11491     /**
11492      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11493      * (e.g., during paging).
11494      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11495      */
11496     getModifiedRecords : function(){
11497         return this.modified;
11498     },
11499
11500     // private
11501     createFilterFn : function(property, value, anyMatch){
11502         if(!value.exec){ // not a regex
11503             value = String(value);
11504             if(value.length == 0){
11505                 return false;
11506             }
11507             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11508         }
11509         return function(r){
11510             return value.test(r.data[property]);
11511         };
11512     },
11513
11514     /**
11515      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11516      * @param {String} property A field on your records
11517      * @param {Number} start The record index to start at (defaults to 0)
11518      * @param {Number} end The last record index to include (defaults to length - 1)
11519      * @return {Number} The sum
11520      */
11521     sum : function(property, start, end){
11522         var rs = this.data.items, v = 0;
11523         start = start || 0;
11524         end = (end || end === 0) ? end : rs.length-1;
11525
11526         for(var i = start; i <= end; i++){
11527             v += (rs[i].data[property] || 0);
11528         }
11529         return v;
11530     },
11531
11532     /**
11533      * Filter the records by a specified property.
11534      * @param {String} field A field on your records
11535      * @param {String/RegExp} value Either a string that the field
11536      * should start with or a RegExp to test against the field
11537      * @param {Boolean} anyMatch True to match any part not just the beginning
11538      */
11539     filter : function(property, value, anyMatch){
11540         var fn = this.createFilterFn(property, value, anyMatch);
11541         return fn ? this.filterBy(fn) : this.clearFilter();
11542     },
11543
11544     /**
11545      * Filter by a function. The specified function will be called with each
11546      * record in this data source. If the function returns true the record is included,
11547      * otherwise it is filtered.
11548      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11549      * @param {Object} scope (optional) The scope of the function (defaults to this)
11550      */
11551     filterBy : function(fn, scope){
11552         this.snapshot = this.snapshot || this.data;
11553         this.data = this.queryBy(fn, scope||this);
11554         this.fireEvent("datachanged", this);
11555     },
11556
11557     /**
11558      * Query 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      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11564      */
11565     query : function(property, value, anyMatch){
11566         var fn = this.createFilterFn(property, value, anyMatch);
11567         return fn ? this.queryBy(fn) : this.data.clone();
11568     },
11569
11570     /**
11571      * Query by a function. The specified function will be called with each
11572      * record in this data source. If the function returns true the record is included
11573      * in the results.
11574      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11575      * @param {Object} scope (optional) The scope of the function (defaults to this)
11576       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11577      **/
11578     queryBy : function(fn, scope){
11579         var data = this.snapshot || this.data;
11580         return data.filterBy(fn, scope||this);
11581     },
11582
11583     /**
11584      * Collects unique values for a particular dataIndex from this store.
11585      * @param {String} dataIndex The property to collect
11586      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11587      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11588      * @return {Array} An array of the unique values
11589      **/
11590     collect : function(dataIndex, allowNull, bypassFilter){
11591         var d = (bypassFilter === true && this.snapshot) ?
11592                 this.snapshot.items : this.data.items;
11593         var v, sv, r = [], l = {};
11594         for(var i = 0, len = d.length; i < len; i++){
11595             v = d[i].data[dataIndex];
11596             sv = String(v);
11597             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11598                 l[sv] = true;
11599                 r[r.length] = v;
11600             }
11601         }
11602         return r;
11603     },
11604
11605     /**
11606      * Revert to a view of the Record cache with no filtering applied.
11607      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11608      */
11609     clearFilter : function(suppressEvent){
11610         if(this.snapshot && this.snapshot != this.data){
11611             this.data = this.snapshot;
11612             delete this.snapshot;
11613             if(suppressEvent !== true){
11614                 this.fireEvent("datachanged", this);
11615             }
11616         }
11617     },
11618
11619     // private
11620     afterEdit : function(record){
11621         if(this.modified.indexOf(record) == -1){
11622             this.modified.push(record);
11623         }
11624         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11625     },
11626     
11627     // private
11628     afterReject : function(record){
11629         this.modified.remove(record);
11630         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11631     },
11632
11633     // private
11634     afterCommit : function(record){
11635         this.modified.remove(record);
11636         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11637     },
11638
11639     /**
11640      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11641      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11642      */
11643     commitChanges : function(){
11644         var m = this.modified.slice(0);
11645         this.modified = [];
11646         for(var i = 0, len = m.length; i < len; i++){
11647             m[i].commit();
11648         }
11649     },
11650
11651     /**
11652      * Cancel outstanding changes on all changed records.
11653      */
11654     rejectChanges : function(){
11655         var m = this.modified.slice(0);
11656         this.modified = [];
11657         for(var i = 0, len = m.length; i < len; i++){
11658             m[i].reject();
11659         }
11660     },
11661
11662     onMetaChange : function(meta, rtype, o){
11663         this.recordType = rtype;
11664         this.fields = rtype.prototype.fields;
11665         delete this.snapshot;
11666         this.sortInfo = meta.sortInfo || this.sortInfo;
11667         this.modified = [];
11668         this.fireEvent('metachange', this, this.reader.meta);
11669     },
11670     
11671     moveIndex : function(data, type)
11672     {
11673         var index = this.indexOf(data);
11674         
11675         var newIndex = index + type;
11676         
11677         this.remove(data);
11678         
11679         this.insert(newIndex, data);
11680         
11681     }
11682 });/*
11683  * Based on:
11684  * Ext JS Library 1.1.1
11685  * Copyright(c) 2006-2007, Ext JS, LLC.
11686  *
11687  * Originally Released Under LGPL - original licence link has changed is not relivant.
11688  *
11689  * Fork - LGPL
11690  * <script type="text/javascript">
11691  */
11692
11693 /**
11694  * @class Roo.data.SimpleStore
11695  * @extends Roo.data.Store
11696  * Small helper class to make creating Stores from Array data easier.
11697  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11698  * @cfg {Array} fields An array of field definition objects, or field name strings.
11699  * @cfg {Array} data The multi-dimensional array of data
11700  * @constructor
11701  * @param {Object} config
11702  */
11703 Roo.data.SimpleStore = function(config){
11704     Roo.data.SimpleStore.superclass.constructor.call(this, {
11705         isLocal : true,
11706         reader: new Roo.data.ArrayReader({
11707                 id: config.id
11708             },
11709             Roo.data.Record.create(config.fields)
11710         ),
11711         proxy : new Roo.data.MemoryProxy(config.data)
11712     });
11713     this.load();
11714 };
11715 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11716  * Based on:
11717  * Ext JS Library 1.1.1
11718  * Copyright(c) 2006-2007, Ext JS, LLC.
11719  *
11720  * Originally Released Under LGPL - original licence link has changed is not relivant.
11721  *
11722  * Fork - LGPL
11723  * <script type="text/javascript">
11724  */
11725
11726 /**
11727 /**
11728  * @extends Roo.data.Store
11729  * @class Roo.data.JsonStore
11730  * Small helper class to make creating Stores for JSON data easier. <br/>
11731 <pre><code>
11732 var store = new Roo.data.JsonStore({
11733     url: 'get-images.php',
11734     root: 'images',
11735     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11736 });
11737 </code></pre>
11738  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11739  * JsonReader and HttpProxy (unless inline data is provided).</b>
11740  * @cfg {Array} fields An array of field definition objects, or field name strings.
11741  * @constructor
11742  * @param {Object} config
11743  */
11744 Roo.data.JsonStore = function(c){
11745     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11746         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11747         reader: new Roo.data.JsonReader(c, c.fields)
11748     }));
11749 };
11750 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11751  * Based on:
11752  * Ext JS Library 1.1.1
11753  * Copyright(c) 2006-2007, Ext JS, LLC.
11754  *
11755  * Originally Released Under LGPL - original licence link has changed is not relivant.
11756  *
11757  * Fork - LGPL
11758  * <script type="text/javascript">
11759  */
11760
11761  
11762 Roo.data.Field = function(config){
11763     if(typeof config == "string"){
11764         config = {name: config};
11765     }
11766     Roo.apply(this, config);
11767     
11768     if(!this.type){
11769         this.type = "auto";
11770     }
11771     
11772     var st = Roo.data.SortTypes;
11773     // named sortTypes are supported, here we look them up
11774     if(typeof this.sortType == "string"){
11775         this.sortType = st[this.sortType];
11776     }
11777     
11778     // set default sortType for strings and dates
11779     if(!this.sortType){
11780         switch(this.type){
11781             case "string":
11782                 this.sortType = st.asUCString;
11783                 break;
11784             case "date":
11785                 this.sortType = st.asDate;
11786                 break;
11787             default:
11788                 this.sortType = st.none;
11789         }
11790     }
11791
11792     // define once
11793     var stripRe = /[\$,%]/g;
11794
11795     // prebuilt conversion function for this field, instead of
11796     // switching every time we're reading a value
11797     if(!this.convert){
11798         var cv, dateFormat = this.dateFormat;
11799         switch(this.type){
11800             case "":
11801             case "auto":
11802             case undefined:
11803                 cv = function(v){ return v; };
11804                 break;
11805             case "string":
11806                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11807                 break;
11808             case "int":
11809                 cv = function(v){
11810                     return v !== undefined && v !== null && v !== '' ?
11811                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11812                     };
11813                 break;
11814             case "float":
11815                 cv = function(v){
11816                     return v !== undefined && v !== null && v !== '' ?
11817                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11818                     };
11819                 break;
11820             case "bool":
11821             case "boolean":
11822                 cv = function(v){ return v === true || v === "true" || v == 1; };
11823                 break;
11824             case "date":
11825                 cv = function(v){
11826                     if(!v){
11827                         return '';
11828                     }
11829                     if(v instanceof Date){
11830                         return v;
11831                     }
11832                     if(dateFormat){
11833                         if(dateFormat == "timestamp"){
11834                             return new Date(v*1000);
11835                         }
11836                         return Date.parseDate(v, dateFormat);
11837                     }
11838                     var parsed = Date.parse(v);
11839                     return parsed ? new Date(parsed) : null;
11840                 };
11841              break;
11842             
11843         }
11844         this.convert = cv;
11845     }
11846 };
11847
11848 Roo.data.Field.prototype = {
11849     dateFormat: null,
11850     defaultValue: "",
11851     mapping: null,
11852     sortType : null,
11853     sortDir : "ASC"
11854 };/*
11855  * Based on:
11856  * Ext JS Library 1.1.1
11857  * Copyright(c) 2006-2007, Ext JS, LLC.
11858  *
11859  * Originally Released Under LGPL - original licence link has changed is not relivant.
11860  *
11861  * Fork - LGPL
11862  * <script type="text/javascript">
11863  */
11864  
11865 // Base class for reading structured data from a data source.  This class is intended to be
11866 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11867
11868 /**
11869  * @class Roo.data.DataReader
11870  * Base class for reading structured data from a data source.  This class is intended to be
11871  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11872  */
11873
11874 Roo.data.DataReader = function(meta, recordType){
11875     
11876     this.meta = meta;
11877     
11878     this.recordType = recordType instanceof Array ? 
11879         Roo.data.Record.create(recordType) : recordType;
11880 };
11881
11882 Roo.data.DataReader.prototype = {
11883      /**
11884      * Create an empty record
11885      * @param {Object} data (optional) - overlay some values
11886      * @return {Roo.data.Record} record created.
11887      */
11888     newRow :  function(d) {
11889         var da =  {};
11890         this.recordType.prototype.fields.each(function(c) {
11891             switch( c.type) {
11892                 case 'int' : da[c.name] = 0; break;
11893                 case 'date' : da[c.name] = new Date(); break;
11894                 case 'float' : da[c.name] = 0.0; break;
11895                 case 'boolean' : da[c.name] = false; break;
11896                 default : da[c.name] = ""; break;
11897             }
11898             
11899         });
11900         return new this.recordType(Roo.apply(da, d));
11901     }
11902     
11903 };/*
11904  * Based on:
11905  * Ext JS Library 1.1.1
11906  * Copyright(c) 2006-2007, Ext JS, LLC.
11907  *
11908  * Originally Released Under LGPL - original licence link has changed is not relivant.
11909  *
11910  * Fork - LGPL
11911  * <script type="text/javascript">
11912  */
11913
11914 /**
11915  * @class Roo.data.DataProxy
11916  * @extends Roo.data.Observable
11917  * This class is an abstract base class for implementations which provide retrieval of
11918  * unformatted data objects.<br>
11919  * <p>
11920  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11921  * (of the appropriate type which knows how to parse the data object) to provide a block of
11922  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11923  * <p>
11924  * Custom implementations must implement the load method as described in
11925  * {@link Roo.data.HttpProxy#load}.
11926  */
11927 Roo.data.DataProxy = function(){
11928     this.addEvents({
11929         /**
11930          * @event beforeload
11931          * Fires before a network request is made to retrieve a data object.
11932          * @param {Object} This DataProxy object.
11933          * @param {Object} params The params parameter to the load function.
11934          */
11935         beforeload : true,
11936         /**
11937          * @event load
11938          * Fires before the load method's callback is called.
11939          * @param {Object} This DataProxy object.
11940          * @param {Object} o The data object.
11941          * @param {Object} arg The callback argument object passed to the load function.
11942          */
11943         load : true,
11944         /**
11945          * @event loadexception
11946          * Fires if an Exception occurs during data retrieval.
11947          * @param {Object} This DataProxy object.
11948          * @param {Object} o The data object.
11949          * @param {Object} arg The callback argument object passed to the load function.
11950          * @param {Object} e The Exception.
11951          */
11952         loadexception : true
11953     });
11954     Roo.data.DataProxy.superclass.constructor.call(this);
11955 };
11956
11957 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11958
11959     /**
11960      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11961      */
11962 /*
11963  * Based on:
11964  * Ext JS Library 1.1.1
11965  * Copyright(c) 2006-2007, Ext JS, LLC.
11966  *
11967  * Originally Released Under LGPL - original licence link has changed is not relivant.
11968  *
11969  * Fork - LGPL
11970  * <script type="text/javascript">
11971  */
11972 /**
11973  * @class Roo.data.MemoryProxy
11974  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11975  * to the Reader when its load method is called.
11976  * @constructor
11977  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11978  */
11979 Roo.data.MemoryProxy = function(data){
11980     if (data.data) {
11981         data = data.data;
11982     }
11983     Roo.data.MemoryProxy.superclass.constructor.call(this);
11984     this.data = data;
11985 };
11986
11987 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11988     
11989     /**
11990      * Load data from the requested source (in this case an in-memory
11991      * data object passed to the constructor), read the data object into
11992      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11993      * process that block using the passed callback.
11994      * @param {Object} params This parameter is not used by the MemoryProxy class.
11995      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11996      * object into a block of Roo.data.Records.
11997      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11998      * The function must be passed <ul>
11999      * <li>The Record block object</li>
12000      * <li>The "arg" argument from the load function</li>
12001      * <li>A boolean success indicator</li>
12002      * </ul>
12003      * @param {Object} scope The scope in which to call the callback
12004      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12005      */
12006     load : function(params, reader, callback, scope, arg){
12007         params = params || {};
12008         var result;
12009         try {
12010             result = reader.readRecords(this.data);
12011         }catch(e){
12012             this.fireEvent("loadexception", this, arg, null, e);
12013             callback.call(scope, null, arg, false);
12014             return;
12015         }
12016         callback.call(scope, result, arg, true);
12017     },
12018     
12019     // private
12020     update : function(params, records){
12021         
12022     }
12023 });/*
12024  * Based on:
12025  * Ext JS Library 1.1.1
12026  * Copyright(c) 2006-2007, Ext JS, LLC.
12027  *
12028  * Originally Released Under LGPL - original licence link has changed is not relivant.
12029  *
12030  * Fork - LGPL
12031  * <script type="text/javascript">
12032  */
12033 /**
12034  * @class Roo.data.HttpProxy
12035  * @extends Roo.data.DataProxy
12036  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12037  * configured to reference a certain URL.<br><br>
12038  * <p>
12039  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12040  * from which the running page was served.<br><br>
12041  * <p>
12042  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12043  * <p>
12044  * Be aware that to enable the browser to parse an XML document, the server must set
12045  * the Content-Type header in the HTTP response to "text/xml".
12046  * @constructor
12047  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12048  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12049  * will be used to make the request.
12050  */
12051 Roo.data.HttpProxy = function(conn){
12052     Roo.data.HttpProxy.superclass.constructor.call(this);
12053     // is conn a conn config or a real conn?
12054     this.conn = conn;
12055     this.useAjax = !conn || !conn.events;
12056   
12057 };
12058
12059 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12060     // thse are take from connection...
12061     
12062     /**
12063      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12064      */
12065     /**
12066      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12067      * extra parameters to each request made by this object. (defaults to undefined)
12068      */
12069     /**
12070      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12071      *  to each request made by this object. (defaults to undefined)
12072      */
12073     /**
12074      * @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)
12075      */
12076     /**
12077      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12078      */
12079      /**
12080      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12081      * @type Boolean
12082      */
12083   
12084
12085     /**
12086      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12087      * @type Boolean
12088      */
12089     /**
12090      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12091      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12092      * a finer-grained basis than the DataProxy events.
12093      */
12094     getConnection : function(){
12095         return this.useAjax ? Roo.Ajax : this.conn;
12096     },
12097
12098     /**
12099      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12100      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12101      * process that block using the passed callback.
12102      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12103      * for the request to the remote server.
12104      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12105      * object into a block of Roo.data.Records.
12106      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12107      * The function must be passed <ul>
12108      * <li>The Record block object</li>
12109      * <li>The "arg" argument from the load function</li>
12110      * <li>A boolean success indicator</li>
12111      * </ul>
12112      * @param {Object} scope The scope in which to call the callback
12113      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12114      */
12115     load : function(params, reader, callback, scope, arg){
12116         if(this.fireEvent("beforeload", this, params) !== false){
12117             var  o = {
12118                 params : params || {},
12119                 request: {
12120                     callback : callback,
12121                     scope : scope,
12122                     arg : arg
12123                 },
12124                 reader: reader,
12125                 callback : this.loadResponse,
12126                 scope: this
12127             };
12128             if(this.useAjax){
12129                 Roo.applyIf(o, this.conn);
12130                 if(this.activeRequest){
12131                     Roo.Ajax.abort(this.activeRequest);
12132                 }
12133                 this.activeRequest = Roo.Ajax.request(o);
12134             }else{
12135                 this.conn.request(o);
12136             }
12137         }else{
12138             callback.call(scope||this, null, arg, false);
12139         }
12140     },
12141
12142     // private
12143     loadResponse : function(o, success, response){
12144         delete this.activeRequest;
12145         if(!success){
12146             this.fireEvent("loadexception", this, o, response);
12147             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12148             return;
12149         }
12150         var result;
12151         try {
12152             result = o.reader.read(response);
12153         }catch(e){
12154             this.fireEvent("loadexception", this, o, response, e);
12155             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12156             return;
12157         }
12158         
12159         this.fireEvent("load", this, o, o.request.arg);
12160         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12161     },
12162
12163     // private
12164     update : function(dataSet){
12165
12166     },
12167
12168     // private
12169     updateResponse : function(dataSet){
12170
12171     }
12172 });/*
12173  * Based on:
12174  * Ext JS Library 1.1.1
12175  * Copyright(c) 2006-2007, Ext JS, LLC.
12176  *
12177  * Originally Released Under LGPL - original licence link has changed is not relivant.
12178  *
12179  * Fork - LGPL
12180  * <script type="text/javascript">
12181  */
12182
12183 /**
12184  * @class Roo.data.ScriptTagProxy
12185  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12186  * other than the originating domain of the running page.<br><br>
12187  * <p>
12188  * <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
12189  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12190  * <p>
12191  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12192  * source code that is used as the source inside a &lt;script> tag.<br><br>
12193  * <p>
12194  * In order for the browser to process the returned data, the server must wrap the data object
12195  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12196  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12197  * depending on whether the callback name was passed:
12198  * <p>
12199  * <pre><code>
12200 boolean scriptTag = false;
12201 String cb = request.getParameter("callback");
12202 if (cb != null) {
12203     scriptTag = true;
12204     response.setContentType("text/javascript");
12205 } else {
12206     response.setContentType("application/x-json");
12207 }
12208 Writer out = response.getWriter();
12209 if (scriptTag) {
12210     out.write(cb + "(");
12211 }
12212 out.print(dataBlock.toJsonString());
12213 if (scriptTag) {
12214     out.write(");");
12215 }
12216 </pre></code>
12217  *
12218  * @constructor
12219  * @param {Object} config A configuration object.
12220  */
12221 Roo.data.ScriptTagProxy = function(config){
12222     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12223     Roo.apply(this, config);
12224     this.head = document.getElementsByTagName("head")[0];
12225 };
12226
12227 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12228
12229 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12230     /**
12231      * @cfg {String} url The URL from which to request the data object.
12232      */
12233     /**
12234      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12235      */
12236     timeout : 30000,
12237     /**
12238      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12239      * the server the name of the callback function set up by the load call to process the returned data object.
12240      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12241      * javascript output which calls this named function passing the data object as its only parameter.
12242      */
12243     callbackParam : "callback",
12244     /**
12245      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12246      * name to the request.
12247      */
12248     nocache : true,
12249
12250     /**
12251      * Load data from the configured URL, read the data object into
12252      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12253      * process that block using the passed callback.
12254      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12255      * for the request to the remote server.
12256      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12257      * object into a block of Roo.data.Records.
12258      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12259      * The function must be passed <ul>
12260      * <li>The Record block object</li>
12261      * <li>The "arg" argument from the load function</li>
12262      * <li>A boolean success indicator</li>
12263      * </ul>
12264      * @param {Object} scope The scope in which to call the callback
12265      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12266      */
12267     load : function(params, reader, callback, scope, arg){
12268         if(this.fireEvent("beforeload", this, params) !== false){
12269
12270             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12271
12272             var url = this.url;
12273             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12274             if(this.nocache){
12275                 url += "&_dc=" + (new Date().getTime());
12276             }
12277             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12278             var trans = {
12279                 id : transId,
12280                 cb : "stcCallback"+transId,
12281                 scriptId : "stcScript"+transId,
12282                 params : params,
12283                 arg : arg,
12284                 url : url,
12285                 callback : callback,
12286                 scope : scope,
12287                 reader : reader
12288             };
12289             var conn = this;
12290
12291             window[trans.cb] = function(o){
12292                 conn.handleResponse(o, trans);
12293             };
12294
12295             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12296
12297             if(this.autoAbort !== false){
12298                 this.abort();
12299             }
12300
12301             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12302
12303             var script = document.createElement("script");
12304             script.setAttribute("src", url);
12305             script.setAttribute("type", "text/javascript");
12306             script.setAttribute("id", trans.scriptId);
12307             this.head.appendChild(script);
12308
12309             this.trans = trans;
12310         }else{
12311             callback.call(scope||this, null, arg, false);
12312         }
12313     },
12314
12315     // private
12316     isLoading : function(){
12317         return this.trans ? true : false;
12318     },
12319
12320     /**
12321      * Abort the current server request.
12322      */
12323     abort : function(){
12324         if(this.isLoading()){
12325             this.destroyTrans(this.trans);
12326         }
12327     },
12328
12329     // private
12330     destroyTrans : function(trans, isLoaded){
12331         this.head.removeChild(document.getElementById(trans.scriptId));
12332         clearTimeout(trans.timeoutId);
12333         if(isLoaded){
12334             window[trans.cb] = undefined;
12335             try{
12336                 delete window[trans.cb];
12337             }catch(e){}
12338         }else{
12339             // if hasn't been loaded, wait for load to remove it to prevent script error
12340             window[trans.cb] = function(){
12341                 window[trans.cb] = undefined;
12342                 try{
12343                     delete window[trans.cb];
12344                 }catch(e){}
12345             };
12346         }
12347     },
12348
12349     // private
12350     handleResponse : function(o, trans){
12351         this.trans = false;
12352         this.destroyTrans(trans, true);
12353         var result;
12354         try {
12355             result = trans.reader.readRecords(o);
12356         }catch(e){
12357             this.fireEvent("loadexception", this, o, trans.arg, e);
12358             trans.callback.call(trans.scope||window, null, trans.arg, false);
12359             return;
12360         }
12361         this.fireEvent("load", this, o, trans.arg);
12362         trans.callback.call(trans.scope||window, result, trans.arg, true);
12363     },
12364
12365     // private
12366     handleFailure : function(trans){
12367         this.trans = false;
12368         this.destroyTrans(trans, false);
12369         this.fireEvent("loadexception", this, null, trans.arg);
12370         trans.callback.call(trans.scope||window, null, trans.arg, false);
12371     }
12372 });/*
12373  * Based on:
12374  * Ext JS Library 1.1.1
12375  * Copyright(c) 2006-2007, Ext JS, LLC.
12376  *
12377  * Originally Released Under LGPL - original licence link has changed is not relivant.
12378  *
12379  * Fork - LGPL
12380  * <script type="text/javascript">
12381  */
12382
12383 /**
12384  * @class Roo.data.JsonReader
12385  * @extends Roo.data.DataReader
12386  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12387  * based on mappings in a provided Roo.data.Record constructor.
12388  * 
12389  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12390  * in the reply previously. 
12391  * 
12392  * <p>
12393  * Example code:
12394  * <pre><code>
12395 var RecordDef = Roo.data.Record.create([
12396     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12397     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12398 ]);
12399 var myReader = new Roo.data.JsonReader({
12400     totalProperty: "results",    // The property which contains the total dataset size (optional)
12401     root: "rows",                // The property which contains an Array of row objects
12402     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12403 }, RecordDef);
12404 </code></pre>
12405  * <p>
12406  * This would consume a JSON file like this:
12407  * <pre><code>
12408 { 'results': 2, 'rows': [
12409     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12410     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12411 }
12412 </code></pre>
12413  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12414  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12415  * paged from the remote server.
12416  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12417  * @cfg {String} root name of the property which contains the Array of row objects.
12418  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12419  * @cfg {Array} fields Array of field definition objects
12420  * @constructor
12421  * Create a new JsonReader
12422  * @param {Object} meta Metadata configuration options
12423  * @param {Object} recordType Either an Array of field definition objects,
12424  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12425  */
12426 Roo.data.JsonReader = function(meta, recordType){
12427     
12428     meta = meta || {};
12429     // set some defaults:
12430     Roo.applyIf(meta, {
12431         totalProperty: 'total',
12432         successProperty : 'success',
12433         root : 'data',
12434         id : 'id'
12435     });
12436     
12437     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12438 };
12439 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12440     
12441     /**
12442      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12443      * Used by Store query builder to append _requestMeta to params.
12444      * 
12445      */
12446     metaFromRemote : false,
12447     /**
12448      * This method is only used by a DataProxy which has retrieved data from a remote server.
12449      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12450      * @return {Object} data A data block which is used by an Roo.data.Store object as
12451      * a cache of Roo.data.Records.
12452      */
12453     read : function(response){
12454         var json = response.responseText;
12455        
12456         var o = /* eval:var:o */ eval("("+json+")");
12457         if(!o) {
12458             throw {message: "JsonReader.read: Json object not found"};
12459         }
12460         
12461         if(o.metaData){
12462             
12463             delete this.ef;
12464             this.metaFromRemote = true;
12465             this.meta = o.metaData;
12466             this.recordType = Roo.data.Record.create(o.metaData.fields);
12467             this.onMetaChange(this.meta, this.recordType, o);
12468         }
12469         return this.readRecords(o);
12470     },
12471
12472     // private function a store will implement
12473     onMetaChange : function(meta, recordType, o){
12474
12475     },
12476
12477     /**
12478          * @ignore
12479          */
12480     simpleAccess: function(obj, subsc) {
12481         return obj[subsc];
12482     },
12483
12484         /**
12485          * @ignore
12486          */
12487     getJsonAccessor: function(){
12488         var re = /[\[\.]/;
12489         return function(expr) {
12490             try {
12491                 return(re.test(expr))
12492                     ? new Function("obj", "return obj." + expr)
12493                     : function(obj){
12494                         return obj[expr];
12495                     };
12496             } catch(e){}
12497             return Roo.emptyFn;
12498         };
12499     }(),
12500
12501     /**
12502      * Create a data block containing Roo.data.Records from an XML document.
12503      * @param {Object} o An object which contains an Array of row objects in the property specified
12504      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12505      * which contains the total size of the dataset.
12506      * @return {Object} data A data block which is used by an Roo.data.Store object as
12507      * a cache of Roo.data.Records.
12508      */
12509     readRecords : function(o){
12510         /**
12511          * After any data loads, the raw JSON data is available for further custom processing.
12512          * @type Object
12513          */
12514         this.o = o;
12515         var s = this.meta, Record = this.recordType,
12516             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12517
12518 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12519         if (!this.ef) {
12520             if(s.totalProperty) {
12521                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12522                 }
12523                 if(s.successProperty) {
12524                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12525                 }
12526                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12527                 if (s.id) {
12528                         var g = this.getJsonAccessor(s.id);
12529                         this.getId = function(rec) {
12530                                 var r = g(rec);  
12531                                 return (r === undefined || r === "") ? null : r;
12532                         };
12533                 } else {
12534                         this.getId = function(){return null;};
12535                 }
12536             this.ef = [];
12537             for(var jj = 0; jj < fl; jj++){
12538                 f = fi[jj];
12539                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12540                 this.ef[jj] = this.getJsonAccessor(map);
12541             }
12542         }
12543
12544         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12545         if(s.totalProperty){
12546             var vt = parseInt(this.getTotal(o), 10);
12547             if(!isNaN(vt)){
12548                 totalRecords = vt;
12549             }
12550         }
12551         if(s.successProperty){
12552             var vs = this.getSuccess(o);
12553             if(vs === false || vs === 'false'){
12554                 success = false;
12555             }
12556         }
12557         var records = [];
12558         for(var i = 0; i < c; i++){
12559                 var n = root[i];
12560             var values = {};
12561             var id = this.getId(n);
12562             for(var j = 0; j < fl; j++){
12563                 f = fi[j];
12564             var v = this.ef[j](n);
12565             if (!f.convert) {
12566                 Roo.log('missing convert for ' + f.name);
12567                 Roo.log(f);
12568                 continue;
12569             }
12570             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12571             }
12572             var record = new Record(values, id);
12573             record.json = n;
12574             records[i] = record;
12575         }
12576         return {
12577             raw : o,
12578             success : success,
12579             records : records,
12580             totalRecords : totalRecords
12581         };
12582     }
12583 });/*
12584  * Based on:
12585  * Ext JS Library 1.1.1
12586  * Copyright(c) 2006-2007, Ext JS, LLC.
12587  *
12588  * Originally Released Under LGPL - original licence link has changed is not relivant.
12589  *
12590  * Fork - LGPL
12591  * <script type="text/javascript">
12592  */
12593
12594 /**
12595  * @class Roo.data.ArrayReader
12596  * @extends Roo.data.DataReader
12597  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12598  * Each element of that Array represents a row of data fields. The
12599  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12600  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12601  * <p>
12602  * Example code:.
12603  * <pre><code>
12604 var RecordDef = Roo.data.Record.create([
12605     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12606     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12607 ]);
12608 var myReader = new Roo.data.ArrayReader({
12609     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12610 }, RecordDef);
12611 </code></pre>
12612  * <p>
12613  * This would consume an Array like this:
12614  * <pre><code>
12615 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12616   </code></pre>
12617  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12618  * @constructor
12619  * Create a new JsonReader
12620  * @param {Object} meta Metadata configuration options.
12621  * @param {Object} recordType Either an Array of field definition objects
12622  * as specified to {@link Roo.data.Record#create},
12623  * or an {@link Roo.data.Record} object
12624  * created using {@link Roo.data.Record#create}.
12625  */
12626 Roo.data.ArrayReader = function(meta, recordType){
12627     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12628 };
12629
12630 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12631     /**
12632      * Create a data block containing Roo.data.Records from an XML document.
12633      * @param {Object} o An Array of row objects which represents the dataset.
12634      * @return {Object} data A data block which is used by an Roo.data.Store object as
12635      * a cache of Roo.data.Records.
12636      */
12637     readRecords : function(o){
12638         var sid = this.meta ? this.meta.id : null;
12639         var recordType = this.recordType, fields = recordType.prototype.fields;
12640         var records = [];
12641         var root = o;
12642             for(var i = 0; i < root.length; i++){
12643                     var n = root[i];
12644                 var values = {};
12645                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12646                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12647                 var f = fields.items[j];
12648                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12649                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12650                 v = f.convert(v);
12651                 values[f.name] = v;
12652             }
12653                 var record = new recordType(values, id);
12654                 record.json = n;
12655                 records[records.length] = record;
12656             }
12657             return {
12658                 records : records,
12659                 totalRecords : records.length
12660             };
12661     }
12662 });/*
12663  * - LGPL
12664  * * 
12665  */
12666
12667 /**
12668  * @class Roo.bootstrap.ComboBox
12669  * @extends Roo.bootstrap.TriggerField
12670  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12671  * @cfg {Boolean} append (true|false) default false
12672  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12673  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12674  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12675  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12676  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12677  * @cfg {Boolean} animate default true
12678  * @cfg {Boolean} emptyResultText only for touch device
12679  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12680  * @cfg {String} emptyTitle default ''
12681  * @constructor
12682  * Create a new ComboBox.
12683  * @param {Object} config Configuration options
12684  */
12685 Roo.bootstrap.ComboBox = function(config){
12686     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12687     this.addEvents({
12688         /**
12689          * @event expand
12690          * Fires when the dropdown list is expanded
12691         * @param {Roo.bootstrap.ComboBox} combo This combo box
12692         */
12693         'expand' : true,
12694         /**
12695          * @event collapse
12696          * Fires when the dropdown list is collapsed
12697         * @param {Roo.bootstrap.ComboBox} combo This combo box
12698         */
12699         'collapse' : true,
12700         /**
12701          * @event beforeselect
12702          * Fires before a list item is selected. Return false to cancel the selection.
12703         * @param {Roo.bootstrap.ComboBox} combo This combo box
12704         * @param {Roo.data.Record} record The data record returned from the underlying store
12705         * @param {Number} index The index of the selected item in the dropdown list
12706         */
12707         'beforeselect' : true,
12708         /**
12709          * @event select
12710          * Fires when a list item is selected
12711         * @param {Roo.bootstrap.ComboBox} combo This combo box
12712         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12713         * @param {Number} index The index of the selected item in the dropdown list
12714         */
12715         'select' : true,
12716         /**
12717          * @event beforequery
12718          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12719          * The event object passed has these properties:
12720         * @param {Roo.bootstrap.ComboBox} combo This combo box
12721         * @param {String} query The query
12722         * @param {Boolean} forceAll true to force "all" query
12723         * @param {Boolean} cancel true to cancel the query
12724         * @param {Object} e The query event object
12725         */
12726         'beforequery': true,
12727          /**
12728          * @event add
12729          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12730         * @param {Roo.bootstrap.ComboBox} combo This combo box
12731         */
12732         'add' : true,
12733         /**
12734          * @event edit
12735          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12736         * @param {Roo.bootstrap.ComboBox} combo This combo box
12737         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12738         */
12739         'edit' : true,
12740         /**
12741          * @event remove
12742          * Fires when the remove value from the combobox array
12743         * @param {Roo.bootstrap.ComboBox} combo This combo box
12744         */
12745         'remove' : true,
12746         /**
12747          * @event afterremove
12748          * Fires when the remove value from the combobox array
12749         * @param {Roo.bootstrap.ComboBox} combo This combo box
12750         */
12751         'afterremove' : true,
12752         /**
12753          * @event specialfilter
12754          * Fires when specialfilter
12755             * @param {Roo.bootstrap.ComboBox} combo This combo box
12756             */
12757         'specialfilter' : true,
12758         /**
12759          * @event tick
12760          * Fires when tick the element
12761             * @param {Roo.bootstrap.ComboBox} combo This combo box
12762             */
12763         'tick' : true,
12764         /**
12765          * @event touchviewdisplay
12766          * Fires when touch view require special display (default is using displayField)
12767             * @param {Roo.bootstrap.ComboBox} combo This combo box
12768             * @param {Object} cfg set html .
12769             */
12770         'touchviewdisplay' : true
12771         
12772     });
12773     
12774     this.item = [];
12775     this.tickItems = [];
12776     
12777     this.selectedIndex = -1;
12778     if(this.mode == 'local'){
12779         if(config.queryDelay === undefined){
12780             this.queryDelay = 10;
12781         }
12782         if(config.minChars === undefined){
12783             this.minChars = 0;
12784         }
12785     }
12786 };
12787
12788 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12789      
12790     /**
12791      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12792      * rendering into an Roo.Editor, defaults to false)
12793      */
12794     /**
12795      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12796      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12797      */
12798     /**
12799      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12800      */
12801     /**
12802      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12803      * the dropdown list (defaults to undefined, with no header element)
12804      */
12805
12806      /**
12807      * @cfg {String/Roo.Template} tpl The template to use to render the output
12808      */
12809      
12810      /**
12811      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12812      */
12813     listWidth: undefined,
12814     /**
12815      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12816      * mode = 'remote' or 'text' if mode = 'local')
12817      */
12818     displayField: undefined,
12819     
12820     /**
12821      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12822      * mode = 'remote' or 'value' if mode = 'local'). 
12823      * Note: use of a valueField requires the user make a selection
12824      * in order for a value to be mapped.
12825      */
12826     valueField: undefined,
12827     /**
12828      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12829      */
12830     modalTitle : '',
12831     
12832     /**
12833      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12834      * field's data value (defaults to the underlying DOM element's name)
12835      */
12836     hiddenName: undefined,
12837     /**
12838      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12839      */
12840     listClass: '',
12841     /**
12842      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12843      */
12844     selectedClass: 'active',
12845     
12846     /**
12847      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12848      */
12849     shadow:'sides',
12850     /**
12851      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12852      * anchor positions (defaults to 'tl-bl')
12853      */
12854     listAlign: 'tl-bl?',
12855     /**
12856      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12857      */
12858     maxHeight: 300,
12859     /**
12860      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12861      * query specified by the allQuery config option (defaults to 'query')
12862      */
12863     triggerAction: 'query',
12864     /**
12865      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12866      * (defaults to 4, does not apply if editable = false)
12867      */
12868     minChars : 4,
12869     /**
12870      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12871      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12872      */
12873     typeAhead: false,
12874     /**
12875      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12876      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12877      */
12878     queryDelay: 500,
12879     /**
12880      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12881      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12882      */
12883     pageSize: 0,
12884     /**
12885      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12886      * when editable = true (defaults to false)
12887      */
12888     selectOnFocus:false,
12889     /**
12890      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12891      */
12892     queryParam: 'query',
12893     /**
12894      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12895      * when mode = 'remote' (defaults to 'Loading...')
12896      */
12897     loadingText: 'Loading...',
12898     /**
12899      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12900      */
12901     resizable: false,
12902     /**
12903      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12904      */
12905     handleHeight : 8,
12906     /**
12907      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12908      * traditional select (defaults to true)
12909      */
12910     editable: true,
12911     /**
12912      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12913      */
12914     allQuery: '',
12915     /**
12916      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12917      */
12918     mode: 'remote',
12919     /**
12920      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12921      * listWidth has a higher value)
12922      */
12923     minListWidth : 70,
12924     /**
12925      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12926      * allow the user to set arbitrary text into the field (defaults to false)
12927      */
12928     forceSelection:false,
12929     /**
12930      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12931      * if typeAhead = true (defaults to 250)
12932      */
12933     typeAheadDelay : 250,
12934     /**
12935      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12936      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12937      */
12938     valueNotFoundText : undefined,
12939     /**
12940      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12941      */
12942     blockFocus : false,
12943     
12944     /**
12945      * @cfg {Boolean} disableClear Disable showing of clear button.
12946      */
12947     disableClear : false,
12948     /**
12949      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12950      */
12951     alwaysQuery : false,
12952     
12953     /**
12954      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12955      */
12956     multiple : false,
12957     
12958     /**
12959      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12960      */
12961     invalidClass : "has-warning",
12962     
12963     /**
12964      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12965      */
12966     validClass : "has-success",
12967     
12968     /**
12969      * @cfg {Boolean} specialFilter (true|false) special filter default false
12970      */
12971     specialFilter : false,
12972     
12973     /**
12974      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12975      */
12976     mobileTouchView : true,
12977     
12978     /**
12979      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12980      */
12981     useNativeIOS : false,
12982     
12983     ios_options : false,
12984     
12985     //private
12986     addicon : false,
12987     editicon: false,
12988     
12989     page: 0,
12990     hasQuery: false,
12991     append: false,
12992     loadNext: false,
12993     autoFocus : true,
12994     tickable : false,
12995     btnPosition : 'right',
12996     triggerList : true,
12997     showToggleBtn : true,
12998     animate : true,
12999     emptyResultText: 'Empty',
13000     triggerText : 'Select',
13001     emptyTitle : '',
13002     
13003     // element that contains real text value.. (when hidden is used..)
13004     
13005     getAutoCreate : function()
13006     {   
13007         var cfg = false;
13008         //render
13009         /*
13010          * Render classic select for iso
13011          */
13012         
13013         if(Roo.isIOS && this.useNativeIOS){
13014             cfg = this.getAutoCreateNativeIOS();
13015             return cfg;
13016         }
13017         
13018         /*
13019          * Touch Devices
13020          */
13021         
13022         if(Roo.isTouch && this.mobileTouchView){
13023             cfg = this.getAutoCreateTouchView();
13024             return cfg;;
13025         }
13026         
13027         /*
13028          *  Normal ComboBox
13029          */
13030         if(!this.tickable){
13031             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13032             return cfg;
13033         }
13034         
13035         /*
13036          *  ComboBox with tickable selections
13037          */
13038              
13039         var align = this.labelAlign || this.parentLabelAlign();
13040         
13041         cfg = {
13042             cls : 'form-group roo-combobox-tickable' //input-group
13043         };
13044         
13045         var btn_text_select = '';
13046         var btn_text_done = '';
13047         var btn_text_cancel = '';
13048         
13049         if (this.btn_text_show) {
13050             btn_text_select = 'Select';
13051             btn_text_done = 'Done';
13052             btn_text_cancel = 'Cancel'; 
13053         }
13054         
13055         var buttons = {
13056             tag : 'div',
13057             cls : 'tickable-buttons',
13058             cn : [
13059                 {
13060                     tag : 'button',
13061                     type : 'button',
13062                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13063                     //html : this.triggerText
13064                     html: btn_text_select
13065                 },
13066                 {
13067                     tag : 'button',
13068                     type : 'button',
13069                     name : 'ok',
13070                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13071                     //html : 'Done'
13072                     html: btn_text_done
13073                 },
13074                 {
13075                     tag : 'button',
13076                     type : 'button',
13077                     name : 'cancel',
13078                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13079                     //html : 'Cancel'
13080                     html: btn_text_cancel
13081                 }
13082             ]
13083         };
13084         
13085         if(this.editable){
13086             buttons.cn.unshift({
13087                 tag: 'input',
13088                 cls: 'roo-select2-search-field-input'
13089             });
13090         }
13091         
13092         var _this = this;
13093         
13094         Roo.each(buttons.cn, function(c){
13095             if (_this.size) {
13096                 c.cls += ' btn-' + _this.size;
13097             }
13098
13099             if (_this.disabled) {
13100                 c.disabled = true;
13101             }
13102         });
13103         
13104         var box = {
13105             tag: 'div',
13106             cn: [
13107                 {
13108                     tag: 'input',
13109                     type : 'hidden',
13110                     cls: 'form-hidden-field'
13111                 },
13112                 {
13113                     tag: 'ul',
13114                     cls: 'roo-select2-choices',
13115                     cn:[
13116                         {
13117                             tag: 'li',
13118                             cls: 'roo-select2-search-field',
13119                             cn: [
13120                                 buttons
13121                             ]
13122                         }
13123                     ]
13124                 }
13125             ]
13126         };
13127         
13128         var combobox = {
13129             cls: 'roo-select2-container input-group roo-select2-container-multi',
13130             cn: [
13131                 box
13132 //                {
13133 //                    tag: 'ul',
13134 //                    cls: 'typeahead typeahead-long dropdown-menu',
13135 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13136 //                }
13137             ]
13138         };
13139         
13140         if(this.hasFeedback && !this.allowBlank){
13141             
13142             var feedback = {
13143                 tag: 'span',
13144                 cls: 'glyphicon form-control-feedback'
13145             };
13146
13147             combobox.cn.push(feedback);
13148         }
13149         
13150         
13151         if (align ==='left' && this.fieldLabel.length) {
13152             
13153             cfg.cls += ' roo-form-group-label-left';
13154             
13155             cfg.cn = [
13156                 {
13157                     tag : 'i',
13158                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13159                     tooltip : 'This field is required'
13160                 },
13161                 {
13162                     tag: 'label',
13163                     'for' :  id,
13164                     cls : 'control-label',
13165                     html : this.fieldLabel
13166
13167                 },
13168                 {
13169                     cls : "", 
13170                     cn: [
13171                         combobox
13172                     ]
13173                 }
13174
13175             ];
13176             
13177             var labelCfg = cfg.cn[1];
13178             var contentCfg = cfg.cn[2];
13179             
13180
13181             if(this.indicatorpos == 'right'){
13182                 
13183                 cfg.cn = [
13184                     {
13185                         tag: 'label',
13186                         'for' :  id,
13187                         cls : 'control-label',
13188                         cn : [
13189                             {
13190                                 tag : 'span',
13191                                 html : this.fieldLabel
13192                             },
13193                             {
13194                                 tag : 'i',
13195                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13196                                 tooltip : 'This field is required'
13197                             }
13198                         ]
13199                     },
13200                     {
13201                         cls : "",
13202                         cn: [
13203                             combobox
13204                         ]
13205                     }
13206
13207                 ];
13208                 
13209                 
13210                 
13211                 labelCfg = cfg.cn[0];
13212                 contentCfg = cfg.cn[1];
13213             
13214             }
13215             
13216             if(this.labelWidth > 12){
13217                 labelCfg.style = "width: " + this.labelWidth + 'px';
13218             }
13219             
13220             if(this.labelWidth < 13 && this.labelmd == 0){
13221                 this.labelmd = this.labelWidth;
13222             }
13223             
13224             if(this.labellg > 0){
13225                 labelCfg.cls += ' col-lg-' + this.labellg;
13226                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13227             }
13228             
13229             if(this.labelmd > 0){
13230                 labelCfg.cls += ' col-md-' + this.labelmd;
13231                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13232             }
13233             
13234             if(this.labelsm > 0){
13235                 labelCfg.cls += ' col-sm-' + this.labelsm;
13236                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13237             }
13238             
13239             if(this.labelxs > 0){
13240                 labelCfg.cls += ' col-xs-' + this.labelxs;
13241                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13242             }
13243                 
13244                 
13245         } else if ( this.fieldLabel.length) {
13246 //                Roo.log(" label");
13247                  cfg.cn = [
13248                     {
13249                         tag : 'i',
13250                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13251                         tooltip : 'This field is required'
13252                     },
13253                     {
13254                         tag: 'label',
13255                         //cls : 'input-group-addon',
13256                         html : this.fieldLabel
13257                     },
13258                     combobox
13259                 ];
13260                 
13261                 if(this.indicatorpos == 'right'){
13262                     cfg.cn = [
13263                         {
13264                             tag: 'label',
13265                             //cls : 'input-group-addon',
13266                             html : this.fieldLabel
13267                         },
13268                         {
13269                             tag : 'i',
13270                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13271                             tooltip : 'This field is required'
13272                         },
13273                         combobox
13274                     ];
13275                     
13276                 }
13277
13278         } else {
13279             
13280 //                Roo.log(" no label && no align");
13281                 cfg = combobox
13282                      
13283                 
13284         }
13285          
13286         var settings=this;
13287         ['xs','sm','md','lg'].map(function(size){
13288             if (settings[size]) {
13289                 cfg.cls += ' col-' + size + '-' + settings[size];
13290             }
13291         });
13292         
13293         return cfg;
13294         
13295     },
13296     
13297     _initEventsCalled : false,
13298     
13299     // private
13300     initEvents: function()
13301     {   
13302         if (this._initEventsCalled) { // as we call render... prevent looping...
13303             return;
13304         }
13305         this._initEventsCalled = true;
13306         
13307         if (!this.store) {
13308             throw "can not find store for combo";
13309         }
13310         
13311         this.indicator = this.indicatorEl();
13312         
13313         this.store = Roo.factory(this.store, Roo.data);
13314         this.store.parent = this;
13315         
13316         // if we are building from html. then this element is so complex, that we can not really
13317         // use the rendered HTML.
13318         // so we have to trash and replace the previous code.
13319         if (Roo.XComponent.build_from_html) {
13320             // remove this element....
13321             var e = this.el.dom, k=0;
13322             while (e ) { e = e.previousSibling;  ++k;}
13323
13324             this.el.remove();
13325             
13326             this.el=false;
13327             this.rendered = false;
13328             
13329             this.render(this.parent().getChildContainer(true), k);
13330         }
13331         
13332         if(Roo.isIOS && this.useNativeIOS){
13333             this.initIOSView();
13334             return;
13335         }
13336         
13337         /*
13338          * Touch Devices
13339          */
13340         
13341         if(Roo.isTouch && this.mobileTouchView){
13342             this.initTouchView();
13343             return;
13344         }
13345         
13346         if(this.tickable){
13347             this.initTickableEvents();
13348             return;
13349         }
13350         
13351         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13352         
13353         if(this.hiddenName){
13354             
13355             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13356             
13357             this.hiddenField.dom.value =
13358                 this.hiddenValue !== undefined ? this.hiddenValue :
13359                 this.value !== undefined ? this.value : '';
13360
13361             // prevent input submission
13362             this.el.dom.removeAttribute('name');
13363             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13364              
13365              
13366         }
13367         //if(Roo.isGecko){
13368         //    this.el.dom.setAttribute('autocomplete', 'off');
13369         //}
13370         
13371         var cls = 'x-combo-list';
13372         
13373         //this.list = new Roo.Layer({
13374         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13375         //});
13376         
13377         var _this = this;
13378         
13379         (function(){
13380             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13381             _this.list.setWidth(lw);
13382         }).defer(100);
13383         
13384         this.list.on('mouseover', this.onViewOver, this);
13385         this.list.on('mousemove', this.onViewMove, this);
13386         this.list.on('scroll', this.onViewScroll, this);
13387         
13388         /*
13389         this.list.swallowEvent('mousewheel');
13390         this.assetHeight = 0;
13391
13392         if(this.title){
13393             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13394             this.assetHeight += this.header.getHeight();
13395         }
13396
13397         this.innerList = this.list.createChild({cls:cls+'-inner'});
13398         this.innerList.on('mouseover', this.onViewOver, this);
13399         this.innerList.on('mousemove', this.onViewMove, this);
13400         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13401         
13402         if(this.allowBlank && !this.pageSize && !this.disableClear){
13403             this.footer = this.list.createChild({cls:cls+'-ft'});
13404             this.pageTb = new Roo.Toolbar(this.footer);
13405            
13406         }
13407         if(this.pageSize){
13408             this.footer = this.list.createChild({cls:cls+'-ft'});
13409             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13410                     {pageSize: this.pageSize});
13411             
13412         }
13413         
13414         if (this.pageTb && this.allowBlank && !this.disableClear) {
13415             var _this = this;
13416             this.pageTb.add(new Roo.Toolbar.Fill(), {
13417                 cls: 'x-btn-icon x-btn-clear',
13418                 text: '&#160;',
13419                 handler: function()
13420                 {
13421                     _this.collapse();
13422                     _this.clearValue();
13423                     _this.onSelect(false, -1);
13424                 }
13425             });
13426         }
13427         if (this.footer) {
13428             this.assetHeight += this.footer.getHeight();
13429         }
13430         */
13431             
13432         if(!this.tpl){
13433             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13434         }
13435
13436         this.view = new Roo.View(this.list, this.tpl, {
13437             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13438         });
13439         //this.view.wrapEl.setDisplayed(false);
13440         this.view.on('click', this.onViewClick, this);
13441         
13442         
13443         this.store.on('beforeload', this.onBeforeLoad, this);
13444         this.store.on('load', this.onLoad, this);
13445         this.store.on('loadexception', this.onLoadException, this);
13446         /*
13447         if(this.resizable){
13448             this.resizer = new Roo.Resizable(this.list,  {
13449                pinned:true, handles:'se'
13450             });
13451             this.resizer.on('resize', function(r, w, h){
13452                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13453                 this.listWidth = w;
13454                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13455                 this.restrictHeight();
13456             }, this);
13457             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13458         }
13459         */
13460         if(!this.editable){
13461             this.editable = true;
13462             this.setEditable(false);
13463         }
13464         
13465         /*
13466         
13467         if (typeof(this.events.add.listeners) != 'undefined') {
13468             
13469             this.addicon = this.wrap.createChild(
13470                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13471        
13472             this.addicon.on('click', function(e) {
13473                 this.fireEvent('add', this);
13474             }, this);
13475         }
13476         if (typeof(this.events.edit.listeners) != 'undefined') {
13477             
13478             this.editicon = this.wrap.createChild(
13479                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13480             if (this.addicon) {
13481                 this.editicon.setStyle('margin-left', '40px');
13482             }
13483             this.editicon.on('click', function(e) {
13484                 
13485                 // we fire even  if inothing is selected..
13486                 this.fireEvent('edit', this, this.lastData );
13487                 
13488             }, this);
13489         }
13490         */
13491         
13492         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13493             "up" : function(e){
13494                 this.inKeyMode = true;
13495                 this.selectPrev();
13496             },
13497
13498             "down" : function(e){
13499                 if(!this.isExpanded()){
13500                     this.onTriggerClick();
13501                 }else{
13502                     this.inKeyMode = true;
13503                     this.selectNext();
13504                 }
13505             },
13506
13507             "enter" : function(e){
13508 //                this.onViewClick();
13509                 //return true;
13510                 this.collapse();
13511                 
13512                 if(this.fireEvent("specialkey", this, e)){
13513                     this.onViewClick(false);
13514                 }
13515                 
13516                 return true;
13517             },
13518
13519             "esc" : function(e){
13520                 this.collapse();
13521             },
13522
13523             "tab" : function(e){
13524                 this.collapse();
13525                 
13526                 if(this.fireEvent("specialkey", this, e)){
13527                     this.onViewClick(false);
13528                 }
13529                 
13530                 return true;
13531             },
13532
13533             scope : this,
13534
13535             doRelay : function(foo, bar, hname){
13536                 if(hname == 'down' || this.scope.isExpanded()){
13537                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13538                 }
13539                 return true;
13540             },
13541
13542             forceKeyDown: true
13543         });
13544         
13545         
13546         this.queryDelay = Math.max(this.queryDelay || 10,
13547                 this.mode == 'local' ? 10 : 250);
13548         
13549         
13550         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13551         
13552         if(this.typeAhead){
13553             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13554         }
13555         if(this.editable !== false){
13556             this.inputEl().on("keyup", this.onKeyUp, this);
13557         }
13558         if(this.forceSelection){
13559             this.inputEl().on('blur', this.doForce, this);
13560         }
13561         
13562         if(this.multiple){
13563             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13564             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13565         }
13566     },
13567     
13568     initTickableEvents: function()
13569     {   
13570         this.createList();
13571         
13572         if(this.hiddenName){
13573             
13574             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13575             
13576             this.hiddenField.dom.value =
13577                 this.hiddenValue !== undefined ? this.hiddenValue :
13578                 this.value !== undefined ? this.value : '';
13579
13580             // prevent input submission
13581             this.el.dom.removeAttribute('name');
13582             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13583              
13584              
13585         }
13586         
13587 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13588         
13589         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13590         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13591         if(this.triggerList){
13592             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13593         }
13594          
13595         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13596         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13597         
13598         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13599         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13600         
13601         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13602         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13603         
13604         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13605         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13606         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13607         
13608         this.okBtn.hide();
13609         this.cancelBtn.hide();
13610         
13611         var _this = this;
13612         
13613         (function(){
13614             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13615             _this.list.setWidth(lw);
13616         }).defer(100);
13617         
13618         this.list.on('mouseover', this.onViewOver, this);
13619         this.list.on('mousemove', this.onViewMove, this);
13620         
13621         this.list.on('scroll', this.onViewScroll, this);
13622         
13623         if(!this.tpl){
13624             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13625                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13626         }
13627
13628         this.view = new Roo.View(this.list, this.tpl, {
13629             singleSelect:true,
13630             tickable:true,
13631             parent:this,
13632             store: this.store,
13633             selectedClass: this.selectedClass
13634         });
13635         
13636         //this.view.wrapEl.setDisplayed(false);
13637         this.view.on('click', this.onViewClick, this);
13638         
13639         
13640         
13641         this.store.on('beforeload', this.onBeforeLoad, this);
13642         this.store.on('load', this.onLoad, this);
13643         this.store.on('loadexception', this.onLoadException, this);
13644         
13645         if(this.editable){
13646             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13647                 "up" : function(e){
13648                     this.inKeyMode = true;
13649                     this.selectPrev();
13650                 },
13651
13652                 "down" : function(e){
13653                     this.inKeyMode = true;
13654                     this.selectNext();
13655                 },
13656
13657                 "enter" : function(e){
13658                     if(this.fireEvent("specialkey", this, e)){
13659                         this.onViewClick(false);
13660                     }
13661                     
13662                     return true;
13663                 },
13664
13665                 "esc" : function(e){
13666                     this.onTickableFooterButtonClick(e, false, false);
13667                 },
13668
13669                 "tab" : function(e){
13670                     this.fireEvent("specialkey", this, e);
13671                     
13672                     this.onTickableFooterButtonClick(e, false, false);
13673                     
13674                     return true;
13675                 },
13676
13677                 scope : this,
13678
13679                 doRelay : function(e, fn, key){
13680                     if(this.scope.isExpanded()){
13681                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13682                     }
13683                     return true;
13684                 },
13685
13686                 forceKeyDown: true
13687             });
13688         }
13689         
13690         this.queryDelay = Math.max(this.queryDelay || 10,
13691                 this.mode == 'local' ? 10 : 250);
13692         
13693         
13694         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13695         
13696         if(this.typeAhead){
13697             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13698         }
13699         
13700         if(this.editable !== false){
13701             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13702         }
13703         
13704         this.indicator = this.indicatorEl();
13705         
13706         if(this.indicator){
13707             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13708             this.indicator.hide();
13709         }
13710         
13711     },
13712
13713     onDestroy : function(){
13714         if(this.view){
13715             this.view.setStore(null);
13716             this.view.el.removeAllListeners();
13717             this.view.el.remove();
13718             this.view.purgeListeners();
13719         }
13720         if(this.list){
13721             this.list.dom.innerHTML  = '';
13722         }
13723         
13724         if(this.store){
13725             this.store.un('beforeload', this.onBeforeLoad, this);
13726             this.store.un('load', this.onLoad, this);
13727             this.store.un('loadexception', this.onLoadException, this);
13728         }
13729         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13730     },
13731
13732     // private
13733     fireKey : function(e){
13734         if(e.isNavKeyPress() && !this.list.isVisible()){
13735             this.fireEvent("specialkey", this, e);
13736         }
13737     },
13738
13739     // private
13740     onResize: function(w, h){
13741 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13742 //        
13743 //        if(typeof w != 'number'){
13744 //            // we do not handle it!?!?
13745 //            return;
13746 //        }
13747 //        var tw = this.trigger.getWidth();
13748 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13749 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13750 //        var x = w - tw;
13751 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13752 //            
13753 //        //this.trigger.setStyle('left', x+'px');
13754 //        
13755 //        if(this.list && this.listWidth === undefined){
13756 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13757 //            this.list.setWidth(lw);
13758 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13759 //        }
13760         
13761     
13762         
13763     },
13764
13765     /**
13766      * Allow or prevent the user from directly editing the field text.  If false is passed,
13767      * the user will only be able to select from the items defined in the dropdown list.  This method
13768      * is the runtime equivalent of setting the 'editable' config option at config time.
13769      * @param {Boolean} value True to allow the user to directly edit the field text
13770      */
13771     setEditable : function(value){
13772         if(value == this.editable){
13773             return;
13774         }
13775         this.editable = value;
13776         if(!value){
13777             this.inputEl().dom.setAttribute('readOnly', true);
13778             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13779             this.inputEl().addClass('x-combo-noedit');
13780         }else{
13781             this.inputEl().dom.setAttribute('readOnly', false);
13782             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13783             this.inputEl().removeClass('x-combo-noedit');
13784         }
13785     },
13786
13787     // private
13788     
13789     onBeforeLoad : function(combo,opts){
13790         if(!this.hasFocus){
13791             return;
13792         }
13793          if (!opts.add) {
13794             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13795          }
13796         this.restrictHeight();
13797         this.selectedIndex = -1;
13798     },
13799
13800     // private
13801     onLoad : function(){
13802         
13803         this.hasQuery = false;
13804         
13805         if(!this.hasFocus){
13806             return;
13807         }
13808         
13809         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13810             this.loading.hide();
13811         }
13812         
13813         if(this.store.getCount() > 0){
13814             
13815             this.expand();
13816             this.restrictHeight();
13817             if(this.lastQuery == this.allQuery){
13818                 if(this.editable && !this.tickable){
13819                     this.inputEl().dom.select();
13820                 }
13821                 
13822                 if(
13823                     !this.selectByValue(this.value, true) &&
13824                     this.autoFocus && 
13825                     (
13826                         !this.store.lastOptions ||
13827                         typeof(this.store.lastOptions.add) == 'undefined' || 
13828                         this.store.lastOptions.add != true
13829                     )
13830                 ){
13831                     this.select(0, true);
13832                 }
13833             }else{
13834                 if(this.autoFocus){
13835                     this.selectNext();
13836                 }
13837                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13838                     this.taTask.delay(this.typeAheadDelay);
13839                 }
13840             }
13841         }else{
13842             this.onEmptyResults();
13843         }
13844         
13845         //this.el.focus();
13846     },
13847     // private
13848     onLoadException : function()
13849     {
13850         this.hasQuery = false;
13851         
13852         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13853             this.loading.hide();
13854         }
13855         
13856         if(this.tickable && this.editable){
13857             return;
13858         }
13859         
13860         this.collapse();
13861         // only causes errors at present
13862         //Roo.log(this.store.reader.jsonData);
13863         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13864             // fixme
13865             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13866         //}
13867         
13868         
13869     },
13870     // private
13871     onTypeAhead : function(){
13872         if(this.store.getCount() > 0){
13873             var r = this.store.getAt(0);
13874             var newValue = r.data[this.displayField];
13875             var len = newValue.length;
13876             var selStart = this.getRawValue().length;
13877             
13878             if(selStart != len){
13879                 this.setRawValue(newValue);
13880                 this.selectText(selStart, newValue.length);
13881             }
13882         }
13883     },
13884
13885     // private
13886     onSelect : function(record, index){
13887         
13888         if(this.fireEvent('beforeselect', this, record, index) !== false){
13889         
13890             this.setFromData(index > -1 ? record.data : false);
13891             
13892             this.collapse();
13893             this.fireEvent('select', this, record, index);
13894         }
13895     },
13896
13897     /**
13898      * Returns the currently selected field value or empty string if no value is set.
13899      * @return {String} value The selected value
13900      */
13901     getValue : function()
13902     {
13903         if(Roo.isIOS && this.useNativeIOS){
13904             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13905         }
13906         
13907         if(this.multiple){
13908             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13909         }
13910         
13911         if(this.valueField){
13912             return typeof this.value != 'undefined' ? this.value : '';
13913         }else{
13914             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13915         }
13916     },
13917     
13918     getRawValue : function()
13919     {
13920         if(Roo.isIOS && this.useNativeIOS){
13921             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13922         }
13923         
13924         var v = this.inputEl().getValue();
13925         
13926         return v;
13927     },
13928
13929     /**
13930      * Clears any text/value currently set in the field
13931      */
13932     clearValue : function(){
13933         
13934         if(this.hiddenField){
13935             this.hiddenField.dom.value = '';
13936         }
13937         this.value = '';
13938         this.setRawValue('');
13939         this.lastSelectionText = '';
13940         this.lastData = false;
13941         
13942         var close = this.closeTriggerEl();
13943         
13944         if(close){
13945             close.hide();
13946         }
13947         
13948         this.validate();
13949         
13950     },
13951
13952     /**
13953      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13954      * will be displayed in the field.  If the value does not match the data value of an existing item,
13955      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13956      * Otherwise the field will be blank (although the value will still be set).
13957      * @param {String} value The value to match
13958      */
13959     setValue : function(v)
13960     {
13961         if(Roo.isIOS && this.useNativeIOS){
13962             this.setIOSValue(v);
13963             return;
13964         }
13965         
13966         if(this.multiple){
13967             this.syncValue();
13968             return;
13969         }
13970         
13971         var text = v;
13972         if(this.valueField){
13973             var r = this.findRecord(this.valueField, v);
13974             if(r){
13975                 text = r.data[this.displayField];
13976             }else if(this.valueNotFoundText !== undefined){
13977                 text = this.valueNotFoundText;
13978             }
13979         }
13980         this.lastSelectionText = text;
13981         if(this.hiddenField){
13982             this.hiddenField.dom.value = v;
13983         }
13984         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13985         this.value = v;
13986         
13987         var close = this.closeTriggerEl();
13988         
13989         if(close){
13990             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13991         }
13992         
13993         this.validate();
13994     },
13995     /**
13996      * @property {Object} the last set data for the element
13997      */
13998     
13999     lastData : false,
14000     /**
14001      * Sets the value of the field based on a object which is related to the record format for the store.
14002      * @param {Object} value the value to set as. or false on reset?
14003      */
14004     setFromData : function(o){
14005         
14006         if(this.multiple){
14007             this.addItem(o);
14008             return;
14009         }
14010             
14011         var dv = ''; // display value
14012         var vv = ''; // value value..
14013         this.lastData = o;
14014         if (this.displayField) {
14015             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14016         } else {
14017             // this is an error condition!!!
14018             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14019         }
14020         
14021         if(this.valueField){
14022             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14023         }
14024         
14025         var close = this.closeTriggerEl();
14026         
14027         if(close){
14028             if(dv.length || vv * 1 > 0){
14029                 close.show() ;
14030                 this.blockFocus=true;
14031             } else {
14032                 close.hide();
14033             }             
14034         }
14035         
14036         if(this.hiddenField){
14037             this.hiddenField.dom.value = vv;
14038             
14039             this.lastSelectionText = dv;
14040             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14041             this.value = vv;
14042             return;
14043         }
14044         // no hidden field.. - we store the value in 'value', but still display
14045         // display field!!!!
14046         this.lastSelectionText = dv;
14047         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14048         this.value = vv;
14049         
14050         
14051         
14052     },
14053     // private
14054     reset : function(){
14055         // overridden so that last data is reset..
14056         
14057         if(this.multiple){
14058             this.clearItem();
14059             return;
14060         }
14061         
14062         this.setValue(this.originalValue);
14063         //this.clearInvalid();
14064         this.lastData = false;
14065         if (this.view) {
14066             this.view.clearSelections();
14067         }
14068         
14069         this.validate();
14070     },
14071     // private
14072     findRecord : function(prop, value){
14073         var record;
14074         if(this.store.getCount() > 0){
14075             this.store.each(function(r){
14076                 if(r.data[prop] == value){
14077                     record = r;
14078                     return false;
14079                 }
14080                 return true;
14081             });
14082         }
14083         return record;
14084     },
14085     
14086     getName: function()
14087     {
14088         // returns hidden if it's set..
14089         if (!this.rendered) {return ''};
14090         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14091         
14092     },
14093     // private
14094     onViewMove : function(e, t){
14095         this.inKeyMode = false;
14096     },
14097
14098     // private
14099     onViewOver : function(e, t){
14100         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14101             return;
14102         }
14103         var item = this.view.findItemFromChild(t);
14104         
14105         if(item){
14106             var index = this.view.indexOf(item);
14107             this.select(index, false);
14108         }
14109     },
14110
14111     // private
14112     onViewClick : function(view, doFocus, el, e)
14113     {
14114         var index = this.view.getSelectedIndexes()[0];
14115         
14116         var r = this.store.getAt(index);
14117         
14118         if(this.tickable){
14119             
14120             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14121                 return;
14122             }
14123             
14124             var rm = false;
14125             var _this = this;
14126             
14127             Roo.each(this.tickItems, function(v,k){
14128                 
14129                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14130                     Roo.log(v);
14131                     _this.tickItems.splice(k, 1);
14132                     
14133                     if(typeof(e) == 'undefined' && view == false){
14134                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14135                     }
14136                     
14137                     rm = true;
14138                     return;
14139                 }
14140             });
14141             
14142             if(rm){
14143                 return;
14144             }
14145             
14146             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14147                 this.tickItems.push(r.data);
14148             }
14149             
14150             if(typeof(e) == 'undefined' && view == false){
14151                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14152             }
14153                     
14154             return;
14155         }
14156         
14157         if(r){
14158             this.onSelect(r, index);
14159         }
14160         if(doFocus !== false && !this.blockFocus){
14161             this.inputEl().focus();
14162         }
14163     },
14164
14165     // private
14166     restrictHeight : function(){
14167         //this.innerList.dom.style.height = '';
14168         //var inner = this.innerList.dom;
14169         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14170         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14171         //this.list.beginUpdate();
14172         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14173         this.list.alignTo(this.inputEl(), this.listAlign);
14174         this.list.alignTo(this.inputEl(), this.listAlign);
14175         //this.list.endUpdate();
14176     },
14177
14178     // private
14179     onEmptyResults : function(){
14180         
14181         if(this.tickable && this.editable){
14182             this.hasFocus = false;
14183             this.restrictHeight();
14184             return;
14185         }
14186         
14187         this.collapse();
14188     },
14189
14190     /**
14191      * Returns true if the dropdown list is expanded, else false.
14192      */
14193     isExpanded : function(){
14194         return this.list.isVisible();
14195     },
14196
14197     /**
14198      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14199      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14200      * @param {String} value The data value of the item to select
14201      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14202      * selected item if it is not currently in view (defaults to true)
14203      * @return {Boolean} True if the value matched an item in the list, else false
14204      */
14205     selectByValue : function(v, scrollIntoView){
14206         if(v !== undefined && v !== null){
14207             var r = this.findRecord(this.valueField || this.displayField, v);
14208             if(r){
14209                 this.select(this.store.indexOf(r), scrollIntoView);
14210                 return true;
14211             }
14212         }
14213         return false;
14214     },
14215
14216     /**
14217      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14218      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14219      * @param {Number} index The zero-based index of the list item to select
14220      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14221      * selected item if it is not currently in view (defaults to true)
14222      */
14223     select : function(index, scrollIntoView){
14224         this.selectedIndex = index;
14225         this.view.select(index);
14226         if(scrollIntoView !== false){
14227             var el = this.view.getNode(index);
14228             /*
14229              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14230              */
14231             if(el){
14232                 this.list.scrollChildIntoView(el, false);
14233             }
14234         }
14235     },
14236
14237     // private
14238     selectNext : function(){
14239         var ct = this.store.getCount();
14240         if(ct > 0){
14241             if(this.selectedIndex == -1){
14242                 this.select(0);
14243             }else if(this.selectedIndex < ct-1){
14244                 this.select(this.selectedIndex+1);
14245             }
14246         }
14247     },
14248
14249     // private
14250     selectPrev : function(){
14251         var ct = this.store.getCount();
14252         if(ct > 0){
14253             if(this.selectedIndex == -1){
14254                 this.select(0);
14255             }else if(this.selectedIndex != 0){
14256                 this.select(this.selectedIndex-1);
14257             }
14258         }
14259     },
14260
14261     // private
14262     onKeyUp : function(e){
14263         if(this.editable !== false && !e.isSpecialKey()){
14264             this.lastKey = e.getKey();
14265             this.dqTask.delay(this.queryDelay);
14266         }
14267     },
14268
14269     // private
14270     validateBlur : function(){
14271         return !this.list || !this.list.isVisible();   
14272     },
14273
14274     // private
14275     initQuery : function(){
14276         
14277         var v = this.getRawValue();
14278         
14279         if(this.tickable && this.editable){
14280             v = this.tickableInputEl().getValue();
14281         }
14282         
14283         this.doQuery(v);
14284     },
14285
14286     // private
14287     doForce : function(){
14288         if(this.inputEl().dom.value.length > 0){
14289             this.inputEl().dom.value =
14290                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14291              
14292         }
14293     },
14294
14295     /**
14296      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14297      * query allowing the query action to be canceled if needed.
14298      * @param {String} query The SQL query to execute
14299      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14300      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14301      * saved in the current store (defaults to false)
14302      */
14303     doQuery : function(q, forceAll){
14304         
14305         if(q === undefined || q === null){
14306             q = '';
14307         }
14308         var qe = {
14309             query: q,
14310             forceAll: forceAll,
14311             combo: this,
14312             cancel:false
14313         };
14314         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14315             return false;
14316         }
14317         q = qe.query;
14318         
14319         forceAll = qe.forceAll;
14320         if(forceAll === true || (q.length >= this.minChars)){
14321             
14322             this.hasQuery = true;
14323             
14324             if(this.lastQuery != q || this.alwaysQuery){
14325                 this.lastQuery = q;
14326                 if(this.mode == 'local'){
14327                     this.selectedIndex = -1;
14328                     if(forceAll){
14329                         this.store.clearFilter();
14330                     }else{
14331                         
14332                         if(this.specialFilter){
14333                             this.fireEvent('specialfilter', this);
14334                             this.onLoad();
14335                             return;
14336                         }
14337                         
14338                         this.store.filter(this.displayField, q);
14339                     }
14340                     
14341                     this.store.fireEvent("datachanged", this.store);
14342                     
14343                     this.onLoad();
14344                     
14345                     
14346                 }else{
14347                     
14348                     this.store.baseParams[this.queryParam] = q;
14349                     
14350                     var options = {params : this.getParams(q)};
14351                     
14352                     if(this.loadNext){
14353                         options.add = true;
14354                         options.params.start = this.page * this.pageSize;
14355                     }
14356                     
14357                     this.store.load(options);
14358                     
14359                     /*
14360                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14361                      *  we should expand the list on onLoad
14362                      *  so command out it
14363                      */
14364 //                    this.expand();
14365                 }
14366             }else{
14367                 this.selectedIndex = -1;
14368                 this.onLoad();   
14369             }
14370         }
14371         
14372         this.loadNext = false;
14373     },
14374     
14375     // private
14376     getParams : function(q){
14377         var p = {};
14378         //p[this.queryParam] = q;
14379         
14380         if(this.pageSize){
14381             p.start = 0;
14382             p.limit = this.pageSize;
14383         }
14384         return p;
14385     },
14386
14387     /**
14388      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14389      */
14390     collapse : function(){
14391         if(!this.isExpanded()){
14392             return;
14393         }
14394         
14395         this.list.hide();
14396         
14397         this.hasFocus = false;
14398         
14399         if(this.tickable){
14400             this.okBtn.hide();
14401             this.cancelBtn.hide();
14402             this.trigger.show();
14403             
14404             if(this.editable){
14405                 this.tickableInputEl().dom.value = '';
14406                 this.tickableInputEl().blur();
14407             }
14408             
14409         }
14410         
14411         Roo.get(document).un('mousedown', this.collapseIf, this);
14412         Roo.get(document).un('mousewheel', this.collapseIf, this);
14413         if (!this.editable) {
14414             Roo.get(document).un('keydown', this.listKeyPress, this);
14415         }
14416         this.fireEvent('collapse', this);
14417         
14418         this.validate();
14419     },
14420
14421     // private
14422     collapseIf : function(e){
14423         var in_combo  = e.within(this.el);
14424         var in_list =  e.within(this.list);
14425         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14426         
14427         if (in_combo || in_list || is_list) {
14428             //e.stopPropagation();
14429             return;
14430         }
14431         
14432         if(this.tickable){
14433             this.onTickableFooterButtonClick(e, false, false);
14434         }
14435
14436         this.collapse();
14437         
14438     },
14439
14440     /**
14441      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14442      */
14443     expand : function(){
14444        
14445         if(this.isExpanded() || !this.hasFocus){
14446             return;
14447         }
14448         
14449         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14450         this.list.setWidth(lw);
14451         
14452         Roo.log('expand');
14453         
14454         this.list.show();
14455         
14456         this.restrictHeight();
14457         
14458         if(this.tickable){
14459             
14460             this.tickItems = Roo.apply([], this.item);
14461             
14462             this.okBtn.show();
14463             this.cancelBtn.show();
14464             this.trigger.hide();
14465             
14466             if(this.editable){
14467                 this.tickableInputEl().focus();
14468             }
14469             
14470         }
14471         
14472         Roo.get(document).on('mousedown', this.collapseIf, this);
14473         Roo.get(document).on('mousewheel', this.collapseIf, this);
14474         if (!this.editable) {
14475             Roo.get(document).on('keydown', this.listKeyPress, this);
14476         }
14477         
14478         this.fireEvent('expand', this);
14479     },
14480
14481     // private
14482     // Implements the default empty TriggerField.onTriggerClick function
14483     onTriggerClick : function(e)
14484     {
14485         Roo.log('trigger click');
14486         
14487         if(this.disabled || !this.triggerList){
14488             return;
14489         }
14490         
14491         this.page = 0;
14492         this.loadNext = false;
14493         
14494         if(this.isExpanded()){
14495             this.collapse();
14496             if (!this.blockFocus) {
14497                 this.inputEl().focus();
14498             }
14499             
14500         }else {
14501             this.hasFocus = true;
14502             if(this.triggerAction == 'all') {
14503                 this.doQuery(this.allQuery, true);
14504             } else {
14505                 this.doQuery(this.getRawValue());
14506             }
14507             if (!this.blockFocus) {
14508                 this.inputEl().focus();
14509             }
14510         }
14511     },
14512     
14513     onTickableTriggerClick : function(e)
14514     {
14515         if(this.disabled){
14516             return;
14517         }
14518         
14519         this.page = 0;
14520         this.loadNext = false;
14521         this.hasFocus = true;
14522         
14523         if(this.triggerAction == 'all') {
14524             this.doQuery(this.allQuery, true);
14525         } else {
14526             this.doQuery(this.getRawValue());
14527         }
14528     },
14529     
14530     onSearchFieldClick : function(e)
14531     {
14532         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14533             this.onTickableFooterButtonClick(e, false, false);
14534             return;
14535         }
14536         
14537         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14538             return;
14539         }
14540         
14541         this.page = 0;
14542         this.loadNext = false;
14543         this.hasFocus = true;
14544         
14545         if(this.triggerAction == 'all') {
14546             this.doQuery(this.allQuery, true);
14547         } else {
14548             this.doQuery(this.getRawValue());
14549         }
14550     },
14551     
14552     listKeyPress : function(e)
14553     {
14554         //Roo.log('listkeypress');
14555         // scroll to first matching element based on key pres..
14556         if (e.isSpecialKey()) {
14557             return false;
14558         }
14559         var k = String.fromCharCode(e.getKey()).toUpperCase();
14560         //Roo.log(k);
14561         var match  = false;
14562         var csel = this.view.getSelectedNodes();
14563         var cselitem = false;
14564         if (csel.length) {
14565             var ix = this.view.indexOf(csel[0]);
14566             cselitem  = this.store.getAt(ix);
14567             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14568                 cselitem = false;
14569             }
14570             
14571         }
14572         
14573         this.store.each(function(v) { 
14574             if (cselitem) {
14575                 // start at existing selection.
14576                 if (cselitem.id == v.id) {
14577                     cselitem = false;
14578                 }
14579                 return true;
14580             }
14581                 
14582             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14583                 match = this.store.indexOf(v);
14584                 return false;
14585             }
14586             return true;
14587         }, this);
14588         
14589         if (match === false) {
14590             return true; // no more action?
14591         }
14592         // scroll to?
14593         this.view.select(match);
14594         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14595         sn.scrollIntoView(sn.dom.parentNode, false);
14596     },
14597     
14598     onViewScroll : function(e, t){
14599         
14600         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){
14601             return;
14602         }
14603         
14604         this.hasQuery = true;
14605         
14606         this.loading = this.list.select('.loading', true).first();
14607         
14608         if(this.loading === null){
14609             this.list.createChild({
14610                 tag: 'div',
14611                 cls: 'loading roo-select2-more-results roo-select2-active',
14612                 html: 'Loading more results...'
14613             });
14614             
14615             this.loading = this.list.select('.loading', true).first();
14616             
14617             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14618             
14619             this.loading.hide();
14620         }
14621         
14622         this.loading.show();
14623         
14624         var _combo = this;
14625         
14626         this.page++;
14627         this.loadNext = true;
14628         
14629         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14630         
14631         return;
14632     },
14633     
14634     addItem : function(o)
14635     {   
14636         var dv = ''; // display value
14637         
14638         if (this.displayField) {
14639             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14640         } else {
14641             // this is an error condition!!!
14642             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14643         }
14644         
14645         if(!dv.length){
14646             return;
14647         }
14648         
14649         var choice = this.choices.createChild({
14650             tag: 'li',
14651             cls: 'roo-select2-search-choice',
14652             cn: [
14653                 {
14654                     tag: 'div',
14655                     html: dv
14656                 },
14657                 {
14658                     tag: 'a',
14659                     href: '#',
14660                     cls: 'roo-select2-search-choice-close fa fa-times',
14661                     tabindex: '-1'
14662                 }
14663             ]
14664             
14665         }, this.searchField);
14666         
14667         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14668         
14669         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14670         
14671         this.item.push(o);
14672         
14673         this.lastData = o;
14674         
14675         this.syncValue();
14676         
14677         this.inputEl().dom.value = '';
14678         
14679         this.validate();
14680     },
14681     
14682     onRemoveItem : function(e, _self, o)
14683     {
14684         e.preventDefault();
14685         
14686         this.lastItem = Roo.apply([], this.item);
14687         
14688         var index = this.item.indexOf(o.data) * 1;
14689         
14690         if( index < 0){
14691             Roo.log('not this item?!');
14692             return;
14693         }
14694         
14695         this.item.splice(index, 1);
14696         o.item.remove();
14697         
14698         this.syncValue();
14699         
14700         this.fireEvent('remove', this, e);
14701         
14702         this.validate();
14703         
14704     },
14705     
14706     syncValue : function()
14707     {
14708         if(!this.item.length){
14709             this.clearValue();
14710             return;
14711         }
14712             
14713         var value = [];
14714         var _this = this;
14715         Roo.each(this.item, function(i){
14716             if(_this.valueField){
14717                 value.push(i[_this.valueField]);
14718                 return;
14719             }
14720
14721             value.push(i);
14722         });
14723
14724         this.value = value.join(',');
14725
14726         if(this.hiddenField){
14727             this.hiddenField.dom.value = this.value;
14728         }
14729         
14730         this.store.fireEvent("datachanged", this.store);
14731         
14732         this.validate();
14733     },
14734     
14735     clearItem : function()
14736     {
14737         if(!this.multiple){
14738             return;
14739         }
14740         
14741         this.item = [];
14742         
14743         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14744            c.remove();
14745         });
14746         
14747         this.syncValue();
14748         
14749         this.validate();
14750         
14751         if(this.tickable && !Roo.isTouch){
14752             this.view.refresh();
14753         }
14754     },
14755     
14756     inputEl: function ()
14757     {
14758         if(Roo.isIOS && this.useNativeIOS){
14759             return this.el.select('select.roo-ios-select', true).first();
14760         }
14761         
14762         if(Roo.isTouch && this.mobileTouchView){
14763             return this.el.select('input.form-control',true).first();
14764         }
14765         
14766         if(this.tickable){
14767             return this.searchField;
14768         }
14769         
14770         return this.el.select('input.form-control',true).first();
14771     },
14772     
14773     onTickableFooterButtonClick : function(e, btn, el)
14774     {
14775         e.preventDefault();
14776         
14777         this.lastItem = Roo.apply([], this.item);
14778         
14779         if(btn && btn.name == 'cancel'){
14780             this.tickItems = Roo.apply([], this.item);
14781             this.collapse();
14782             return;
14783         }
14784         
14785         this.clearItem();
14786         
14787         var _this = this;
14788         
14789         Roo.each(this.tickItems, function(o){
14790             _this.addItem(o);
14791         });
14792         
14793         this.collapse();
14794         
14795     },
14796     
14797     validate : function()
14798     {
14799         if(this.getVisibilityEl().hasClass('hidden')){
14800             return true;
14801         }
14802         
14803         var v = this.getRawValue();
14804         
14805         if(this.multiple){
14806             v = this.getValue();
14807         }
14808         
14809         if(this.disabled || this.allowBlank || v.length){
14810             this.markValid();
14811             return true;
14812         }
14813         
14814         this.markInvalid();
14815         return false;
14816     },
14817     
14818     tickableInputEl : function()
14819     {
14820         if(!this.tickable || !this.editable){
14821             return this.inputEl();
14822         }
14823         
14824         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14825     },
14826     
14827     
14828     getAutoCreateTouchView : function()
14829     {
14830         var id = Roo.id();
14831         
14832         var cfg = {
14833             cls: 'form-group' //input-group
14834         };
14835         
14836         var input =  {
14837             tag: 'input',
14838             id : id,
14839             type : this.inputType,
14840             cls : 'form-control x-combo-noedit',
14841             autocomplete: 'new-password',
14842             placeholder : this.placeholder || '',
14843             readonly : true
14844         };
14845         
14846         if (this.name) {
14847             input.name = this.name;
14848         }
14849         
14850         if (this.size) {
14851             input.cls += ' input-' + this.size;
14852         }
14853         
14854         if (this.disabled) {
14855             input.disabled = true;
14856         }
14857         
14858         var inputblock = {
14859             cls : '',
14860             cn : [
14861                 input
14862             ]
14863         };
14864         
14865         if(this.before){
14866             inputblock.cls += ' input-group';
14867             
14868             inputblock.cn.unshift({
14869                 tag :'span',
14870                 cls : 'input-group-addon',
14871                 html : this.before
14872             });
14873         }
14874         
14875         if(this.removable && !this.multiple){
14876             inputblock.cls += ' roo-removable';
14877             
14878             inputblock.cn.push({
14879                 tag: 'button',
14880                 html : 'x',
14881                 cls : 'roo-combo-removable-btn close'
14882             });
14883         }
14884
14885         if(this.hasFeedback && !this.allowBlank){
14886             
14887             inputblock.cls += ' has-feedback';
14888             
14889             inputblock.cn.push({
14890                 tag: 'span',
14891                 cls: 'glyphicon form-control-feedback'
14892             });
14893             
14894         }
14895         
14896         if (this.after) {
14897             
14898             inputblock.cls += (this.before) ? '' : ' input-group';
14899             
14900             inputblock.cn.push({
14901                 tag :'span',
14902                 cls : 'input-group-addon',
14903                 html : this.after
14904             });
14905         }
14906
14907         var box = {
14908             tag: 'div',
14909             cn: [
14910                 {
14911                     tag: 'input',
14912                     type : 'hidden',
14913                     cls: 'form-hidden-field'
14914                 },
14915                 inputblock
14916             ]
14917             
14918         };
14919         
14920         if(this.multiple){
14921             box = {
14922                 tag: 'div',
14923                 cn: [
14924                     {
14925                         tag: 'input',
14926                         type : 'hidden',
14927                         cls: 'form-hidden-field'
14928                     },
14929                     {
14930                         tag: 'ul',
14931                         cls: 'roo-select2-choices',
14932                         cn:[
14933                             {
14934                                 tag: 'li',
14935                                 cls: 'roo-select2-search-field',
14936                                 cn: [
14937
14938                                     inputblock
14939                                 ]
14940                             }
14941                         ]
14942                     }
14943                 ]
14944             }
14945         };
14946         
14947         var combobox = {
14948             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14949             cn: [
14950                 box
14951             ]
14952         };
14953         
14954         if(!this.multiple && this.showToggleBtn){
14955             
14956             var caret = {
14957                         tag: 'span',
14958                         cls: 'caret'
14959             };
14960             
14961             if (this.caret != false) {
14962                 caret = {
14963                      tag: 'i',
14964                      cls: 'fa fa-' + this.caret
14965                 };
14966                 
14967             }
14968             
14969             combobox.cn.push({
14970                 tag :'span',
14971                 cls : 'input-group-addon btn dropdown-toggle',
14972                 cn : [
14973                     caret,
14974                     {
14975                         tag: 'span',
14976                         cls: 'combobox-clear',
14977                         cn  : [
14978                             {
14979                                 tag : 'i',
14980                                 cls: 'icon-remove'
14981                             }
14982                         ]
14983                     }
14984                 ]
14985
14986             })
14987         }
14988         
14989         if(this.multiple){
14990             combobox.cls += ' roo-select2-container-multi';
14991         }
14992         
14993         var align = this.labelAlign || this.parentLabelAlign();
14994         
14995         if (align ==='left' && this.fieldLabel.length) {
14996
14997             cfg.cn = [
14998                 {
14999                    tag : 'i',
15000                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15001                    tooltip : 'This field is required'
15002                 },
15003                 {
15004                     tag: 'label',
15005                     cls : 'control-label',
15006                     html : this.fieldLabel
15007
15008                 },
15009                 {
15010                     cls : '', 
15011                     cn: [
15012                         combobox
15013                     ]
15014                 }
15015             ];
15016             
15017             var labelCfg = cfg.cn[1];
15018             var contentCfg = cfg.cn[2];
15019             
15020
15021             if(this.indicatorpos == 'right'){
15022                 cfg.cn = [
15023                     {
15024                         tag: 'label',
15025                         'for' :  id,
15026                         cls : 'control-label',
15027                         cn : [
15028                             {
15029                                 tag : 'span',
15030                                 html : this.fieldLabel
15031                             },
15032                             {
15033                                 tag : 'i',
15034                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15035                                 tooltip : 'This field is required'
15036                             }
15037                         ]
15038                     },
15039                     {
15040                         cls : "",
15041                         cn: [
15042                             combobox
15043                         ]
15044                     }
15045
15046                 ];
15047                 
15048                 labelCfg = cfg.cn[0];
15049                 contentCfg = cfg.cn[1];
15050             }
15051             
15052            
15053             
15054             if(this.labelWidth > 12){
15055                 labelCfg.style = "width: " + this.labelWidth + 'px';
15056             }
15057             
15058             if(this.labelWidth < 13 && this.labelmd == 0){
15059                 this.labelmd = this.labelWidth;
15060             }
15061             
15062             if(this.labellg > 0){
15063                 labelCfg.cls += ' col-lg-' + this.labellg;
15064                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15065             }
15066             
15067             if(this.labelmd > 0){
15068                 labelCfg.cls += ' col-md-' + this.labelmd;
15069                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15070             }
15071             
15072             if(this.labelsm > 0){
15073                 labelCfg.cls += ' col-sm-' + this.labelsm;
15074                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15075             }
15076             
15077             if(this.labelxs > 0){
15078                 labelCfg.cls += ' col-xs-' + this.labelxs;
15079                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15080             }
15081                 
15082                 
15083         } else if ( this.fieldLabel.length) {
15084             cfg.cn = [
15085                 {
15086                    tag : 'i',
15087                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15088                    tooltip : 'This field is required'
15089                 },
15090                 {
15091                     tag: 'label',
15092                     cls : 'control-label',
15093                     html : this.fieldLabel
15094
15095                 },
15096                 {
15097                     cls : '', 
15098                     cn: [
15099                         combobox
15100                     ]
15101                 }
15102             ];
15103             
15104             if(this.indicatorpos == 'right'){
15105                 cfg.cn = [
15106                     {
15107                         tag: 'label',
15108                         cls : 'control-label',
15109                         html : this.fieldLabel,
15110                         cn : [
15111                             {
15112                                tag : 'i',
15113                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15114                                tooltip : 'This field is required'
15115                             }
15116                         ]
15117                     },
15118                     {
15119                         cls : '', 
15120                         cn: [
15121                             combobox
15122                         ]
15123                     }
15124                 ];
15125             }
15126         } else {
15127             cfg.cn = combobox;    
15128         }
15129         
15130         
15131         var settings = this;
15132         
15133         ['xs','sm','md','lg'].map(function(size){
15134             if (settings[size]) {
15135                 cfg.cls += ' col-' + size + '-' + settings[size];
15136             }
15137         });
15138         
15139         return cfg;
15140     },
15141     
15142     initTouchView : function()
15143     {
15144         this.renderTouchView();
15145         
15146         this.touchViewEl.on('scroll', function(){
15147             this.el.dom.scrollTop = 0;
15148         }, this);
15149         
15150         this.originalValue = this.getValue();
15151         
15152         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15153         
15154         this.inputEl().on("click", this.showTouchView, this);
15155         if (this.triggerEl) {
15156             this.triggerEl.on("click", this.showTouchView, this);
15157         }
15158         
15159         
15160         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15161         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15162         
15163         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15164         
15165         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15166         this.store.on('load', this.onTouchViewLoad, this);
15167         this.store.on('loadexception', this.onTouchViewLoadException, this);
15168         
15169         if(this.hiddenName){
15170             
15171             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15172             
15173             this.hiddenField.dom.value =
15174                 this.hiddenValue !== undefined ? this.hiddenValue :
15175                 this.value !== undefined ? this.value : '';
15176         
15177             this.el.dom.removeAttribute('name');
15178             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15179         }
15180         
15181         if(this.multiple){
15182             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15183             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15184         }
15185         
15186         if(this.removable && !this.multiple){
15187             var close = this.closeTriggerEl();
15188             if(close){
15189                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15190                 close.on('click', this.removeBtnClick, this, close);
15191             }
15192         }
15193         /*
15194          * fix the bug in Safari iOS8
15195          */
15196         this.inputEl().on("focus", function(e){
15197             document.activeElement.blur();
15198         }, this);
15199         
15200         return;
15201         
15202         
15203     },
15204     
15205     renderTouchView : function()
15206     {
15207         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15208         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15209         
15210         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15211         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15212         
15213         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15214         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15215         this.touchViewBodyEl.setStyle('overflow', 'auto');
15216         
15217         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15218         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15219         
15220         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15221         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15222         
15223     },
15224     
15225     showTouchView : function()
15226     {
15227         if(this.disabled){
15228             return;
15229         }
15230         
15231         this.touchViewHeaderEl.hide();
15232
15233         if(this.modalTitle.length){
15234             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15235             this.touchViewHeaderEl.show();
15236         }
15237
15238         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15239         this.touchViewEl.show();
15240
15241         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15242         
15243         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15244         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15245
15246         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15247
15248         if(this.modalTitle.length){
15249             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15250         }
15251         
15252         this.touchViewBodyEl.setHeight(bodyHeight);
15253
15254         if(this.animate){
15255             var _this = this;
15256             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15257         }else{
15258             this.touchViewEl.addClass('in');
15259         }
15260
15261         this.doTouchViewQuery();
15262         
15263     },
15264     
15265     hideTouchView : function()
15266     {
15267         this.touchViewEl.removeClass('in');
15268
15269         if(this.animate){
15270             var _this = this;
15271             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15272         }else{
15273             this.touchViewEl.setStyle('display', 'none');
15274         }
15275         
15276     },
15277     
15278     setTouchViewValue : function()
15279     {
15280         if(this.multiple){
15281             this.clearItem();
15282         
15283             var _this = this;
15284
15285             Roo.each(this.tickItems, function(o){
15286                 this.addItem(o);
15287             }, this);
15288         }
15289         
15290         this.hideTouchView();
15291     },
15292     
15293     doTouchViewQuery : function()
15294     {
15295         var qe = {
15296             query: '',
15297             forceAll: true,
15298             combo: this,
15299             cancel:false
15300         };
15301         
15302         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15303             return false;
15304         }
15305         
15306         if(!this.alwaysQuery || this.mode == 'local'){
15307             this.onTouchViewLoad();
15308             return;
15309         }
15310         
15311         this.store.load();
15312     },
15313     
15314     onTouchViewBeforeLoad : function(combo,opts)
15315     {
15316         return;
15317     },
15318
15319     // private
15320     onTouchViewLoad : function()
15321     {
15322         if(this.store.getCount() < 1){
15323             this.onTouchViewEmptyResults();
15324             return;
15325         }
15326         
15327         this.clearTouchView();
15328         
15329         var rawValue = this.getRawValue();
15330         
15331         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15332         
15333         this.tickItems = [];
15334         
15335         this.store.data.each(function(d, rowIndex){
15336             var row = this.touchViewListGroup.createChild(template);
15337             
15338             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15339                 row.addClass(d.data.cls);
15340             }
15341             
15342             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15343                 var cfg = {
15344                     data : d.data,
15345                     html : d.data[this.displayField]
15346                 };
15347                 
15348                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15349                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15350                 }
15351             }
15352             row.removeClass('selected');
15353             if(!this.multiple && this.valueField &&
15354                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15355             {
15356                 // radio buttons..
15357                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15358                 row.addClass('selected');
15359             }
15360             
15361             if(this.multiple && this.valueField &&
15362                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15363             {
15364                 
15365                 // checkboxes...
15366                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15367                 this.tickItems.push(d.data);
15368             }
15369             
15370             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15371             
15372         }, this);
15373         
15374         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15375         
15376         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15377
15378         if(this.modalTitle.length){
15379             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15380         }
15381
15382         var listHeight = this.touchViewListGroup.getHeight();
15383         
15384         var _this = this;
15385         
15386         if(firstChecked && listHeight > bodyHeight){
15387             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15388         }
15389         
15390     },
15391     
15392     onTouchViewLoadException : function()
15393     {
15394         this.hideTouchView();
15395     },
15396     
15397     onTouchViewEmptyResults : function()
15398     {
15399         this.clearTouchView();
15400         
15401         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15402         
15403         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15404         
15405     },
15406     
15407     clearTouchView : function()
15408     {
15409         this.touchViewListGroup.dom.innerHTML = '';
15410     },
15411     
15412     onTouchViewClick : function(e, el, o)
15413     {
15414         e.preventDefault();
15415         
15416         var row = o.row;
15417         var rowIndex = o.rowIndex;
15418         
15419         var r = this.store.getAt(rowIndex);
15420         
15421         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15422             
15423             if(!this.multiple){
15424                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15425                     c.dom.removeAttribute('checked');
15426                 }, this);
15427
15428                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15429
15430                 this.setFromData(r.data);
15431
15432                 var close = this.closeTriggerEl();
15433
15434                 if(close){
15435                     close.show();
15436                 }
15437
15438                 this.hideTouchView();
15439
15440                 this.fireEvent('select', this, r, rowIndex);
15441
15442                 return;
15443             }
15444
15445             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15446                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15447                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15448                 return;
15449             }
15450
15451             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15452             this.addItem(r.data);
15453             this.tickItems.push(r.data);
15454         }
15455     },
15456     
15457     getAutoCreateNativeIOS : function()
15458     {
15459         var cfg = {
15460             cls: 'form-group' //input-group,
15461         };
15462         
15463         var combobox =  {
15464             tag: 'select',
15465             cls : 'roo-ios-select'
15466         };
15467         
15468         if (this.name) {
15469             combobox.name = this.name;
15470         }
15471         
15472         if (this.disabled) {
15473             combobox.disabled = true;
15474         }
15475         
15476         var settings = this;
15477         
15478         ['xs','sm','md','lg'].map(function(size){
15479             if (settings[size]) {
15480                 cfg.cls += ' col-' + size + '-' + settings[size];
15481             }
15482         });
15483         
15484         cfg.cn = combobox;
15485         
15486         return cfg;
15487         
15488     },
15489     
15490     initIOSView : function()
15491     {
15492         this.store.on('load', this.onIOSViewLoad, this);
15493         
15494         return;
15495     },
15496     
15497     onIOSViewLoad : function()
15498     {
15499         if(this.store.getCount() < 1){
15500             return;
15501         }
15502         
15503         this.clearIOSView();
15504         
15505         if(this.allowBlank) {
15506             
15507             var default_text = '-- SELECT --';
15508             
15509             if(this.placeholder.length){
15510                 default_text = this.placeholder;
15511             }
15512             
15513             if(this.emptyTitle.length){
15514                 default_text += ' - ' + this.emptyTitle + ' -';
15515             }
15516             
15517             var opt = this.inputEl().createChild({
15518                 tag: 'option',
15519                 value : 0,
15520                 html : default_text
15521             });
15522             
15523             var o = {};
15524             o[this.valueField] = 0;
15525             o[this.displayField] = default_text;
15526             
15527             this.ios_options.push({
15528                 data : o,
15529                 el : opt
15530             });
15531             
15532         }
15533         
15534         this.store.data.each(function(d, rowIndex){
15535             
15536             var html = '';
15537             
15538             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15539                 html = d.data[this.displayField];
15540             }
15541             
15542             var value = '';
15543             
15544             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15545                 value = d.data[this.valueField];
15546             }
15547             
15548             var option = {
15549                 tag: 'option',
15550                 value : value,
15551                 html : html
15552             };
15553             
15554             if(this.value == d.data[this.valueField]){
15555                 option['selected'] = true;
15556             }
15557             
15558             var opt = this.inputEl().createChild(option);
15559             
15560             this.ios_options.push({
15561                 data : d.data,
15562                 el : opt
15563             });
15564             
15565         }, this);
15566         
15567         this.inputEl().on('change', function(){
15568            this.fireEvent('select', this);
15569         }, this);
15570         
15571     },
15572     
15573     clearIOSView: function()
15574     {
15575         this.inputEl().dom.innerHTML = '';
15576         
15577         this.ios_options = [];
15578     },
15579     
15580     setIOSValue: function(v)
15581     {
15582         this.value = v;
15583         
15584         if(!this.ios_options){
15585             return;
15586         }
15587         
15588         Roo.each(this.ios_options, function(opts){
15589            
15590            opts.el.dom.removeAttribute('selected');
15591            
15592            if(opts.data[this.valueField] != v){
15593                return;
15594            }
15595            
15596            opts.el.dom.setAttribute('selected', true);
15597            
15598         }, this);
15599     }
15600
15601     /** 
15602     * @cfg {Boolean} grow 
15603     * @hide 
15604     */
15605     /** 
15606     * @cfg {Number} growMin 
15607     * @hide 
15608     */
15609     /** 
15610     * @cfg {Number} growMax 
15611     * @hide 
15612     */
15613     /**
15614      * @hide
15615      * @method autoSize
15616      */
15617 });
15618
15619 Roo.apply(Roo.bootstrap.ComboBox,  {
15620     
15621     header : {
15622         tag: 'div',
15623         cls: 'modal-header',
15624         cn: [
15625             {
15626                 tag: 'h4',
15627                 cls: 'modal-title'
15628             }
15629         ]
15630     },
15631     
15632     body : {
15633         tag: 'div',
15634         cls: 'modal-body',
15635         cn: [
15636             {
15637                 tag: 'ul',
15638                 cls: 'list-group'
15639             }
15640         ]
15641     },
15642     
15643     listItemRadio : {
15644         tag: 'li',
15645         cls: 'list-group-item',
15646         cn: [
15647             {
15648                 tag: 'span',
15649                 cls: 'roo-combobox-list-group-item-value'
15650             },
15651             {
15652                 tag: 'div',
15653                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15654                 cn: [
15655                     {
15656                         tag: 'input',
15657                         type: 'radio'
15658                     },
15659                     {
15660                         tag: 'label'
15661                     }
15662                 ]
15663             }
15664         ]
15665     },
15666     
15667     listItemCheckbox : {
15668         tag: 'li',
15669         cls: 'list-group-item',
15670         cn: [
15671             {
15672                 tag: 'span',
15673                 cls: 'roo-combobox-list-group-item-value'
15674             },
15675             {
15676                 tag: 'div',
15677                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15678                 cn: [
15679                     {
15680                         tag: 'input',
15681                         type: 'checkbox'
15682                     },
15683                     {
15684                         tag: 'label'
15685                     }
15686                 ]
15687             }
15688         ]
15689     },
15690     
15691     emptyResult : {
15692         tag: 'div',
15693         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15694     },
15695     
15696     footer : {
15697         tag: 'div',
15698         cls: 'modal-footer',
15699         cn: [
15700             {
15701                 tag: 'div',
15702                 cls: 'row',
15703                 cn: [
15704                     {
15705                         tag: 'div',
15706                         cls: 'col-xs-6 text-left',
15707                         cn: {
15708                             tag: 'button',
15709                             cls: 'btn btn-danger roo-touch-view-cancel',
15710                             html: 'Cancel'
15711                         }
15712                     },
15713                     {
15714                         tag: 'div',
15715                         cls: 'col-xs-6 text-right',
15716                         cn: {
15717                             tag: 'button',
15718                             cls: 'btn btn-success roo-touch-view-ok',
15719                             html: 'OK'
15720                         }
15721                     }
15722                 ]
15723             }
15724         ]
15725         
15726     }
15727 });
15728
15729 Roo.apply(Roo.bootstrap.ComboBox,  {
15730     
15731     touchViewTemplate : {
15732         tag: 'div',
15733         cls: 'modal fade roo-combobox-touch-view',
15734         cn: [
15735             {
15736                 tag: 'div',
15737                 cls: 'modal-dialog',
15738                 style : 'position:fixed', // we have to fix position....
15739                 cn: [
15740                     {
15741                         tag: 'div',
15742                         cls: 'modal-content',
15743                         cn: [
15744                             Roo.bootstrap.ComboBox.header,
15745                             Roo.bootstrap.ComboBox.body,
15746                             Roo.bootstrap.ComboBox.footer
15747                         ]
15748                     }
15749                 ]
15750             }
15751         ]
15752     }
15753 });/*
15754  * Based on:
15755  * Ext JS Library 1.1.1
15756  * Copyright(c) 2006-2007, Ext JS, LLC.
15757  *
15758  * Originally Released Under LGPL - original licence link has changed is not relivant.
15759  *
15760  * Fork - LGPL
15761  * <script type="text/javascript">
15762  */
15763
15764 /**
15765  * @class Roo.View
15766  * @extends Roo.util.Observable
15767  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15768  * This class also supports single and multi selection modes. <br>
15769  * Create a data model bound view:
15770  <pre><code>
15771  var store = new Roo.data.Store(...);
15772
15773  var view = new Roo.View({
15774     el : "my-element",
15775     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15776  
15777     singleSelect: true,
15778     selectedClass: "ydataview-selected",
15779     store: store
15780  });
15781
15782  // listen for node click?
15783  view.on("click", function(vw, index, node, e){
15784  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15785  });
15786
15787  // load XML data
15788  dataModel.load("foobar.xml");
15789  </code></pre>
15790  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15791  * <br><br>
15792  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15793  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15794  * 
15795  * Note: old style constructor is still suported (container, template, config)
15796  * 
15797  * @constructor
15798  * Create a new View
15799  * @param {Object} config The config object
15800  * 
15801  */
15802 Roo.View = function(config, depreciated_tpl, depreciated_config){
15803     
15804     this.parent = false;
15805     
15806     if (typeof(depreciated_tpl) == 'undefined') {
15807         // new way.. - universal constructor.
15808         Roo.apply(this, config);
15809         this.el  = Roo.get(this.el);
15810     } else {
15811         // old format..
15812         this.el  = Roo.get(config);
15813         this.tpl = depreciated_tpl;
15814         Roo.apply(this, depreciated_config);
15815     }
15816     this.wrapEl  = this.el.wrap().wrap();
15817     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15818     
15819     
15820     if(typeof(this.tpl) == "string"){
15821         this.tpl = new Roo.Template(this.tpl);
15822     } else {
15823         // support xtype ctors..
15824         this.tpl = new Roo.factory(this.tpl, Roo);
15825     }
15826     
15827     
15828     this.tpl.compile();
15829     
15830     /** @private */
15831     this.addEvents({
15832         /**
15833          * @event beforeclick
15834          * Fires before a click is processed. Returns false to cancel the default action.
15835          * @param {Roo.View} this
15836          * @param {Number} index The index of the target node
15837          * @param {HTMLElement} node The target node
15838          * @param {Roo.EventObject} e The raw event object
15839          */
15840             "beforeclick" : true,
15841         /**
15842          * @event click
15843          * Fires when a template node is clicked.
15844          * @param {Roo.View} this
15845          * @param {Number} index The index of the target node
15846          * @param {HTMLElement} node The target node
15847          * @param {Roo.EventObject} e The raw event object
15848          */
15849             "click" : true,
15850         /**
15851          * @event dblclick
15852          * Fires when a template node is double clicked.
15853          * @param {Roo.View} this
15854          * @param {Number} index The index of the target node
15855          * @param {HTMLElement} node The target node
15856          * @param {Roo.EventObject} e The raw event object
15857          */
15858             "dblclick" : true,
15859         /**
15860          * @event contextmenu
15861          * Fires when a template node is right clicked.
15862          * @param {Roo.View} this
15863          * @param {Number} index The index of the target node
15864          * @param {HTMLElement} node The target node
15865          * @param {Roo.EventObject} e The raw event object
15866          */
15867             "contextmenu" : true,
15868         /**
15869          * @event selectionchange
15870          * Fires when the selected nodes change.
15871          * @param {Roo.View} this
15872          * @param {Array} selections Array of the selected nodes
15873          */
15874             "selectionchange" : true,
15875     
15876         /**
15877          * @event beforeselect
15878          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15879          * @param {Roo.View} this
15880          * @param {HTMLElement} node The node to be selected
15881          * @param {Array} selections Array of currently selected nodes
15882          */
15883             "beforeselect" : true,
15884         /**
15885          * @event preparedata
15886          * Fires on every row to render, to allow you to change the data.
15887          * @param {Roo.View} this
15888          * @param {Object} data to be rendered (change this)
15889          */
15890           "preparedata" : true
15891           
15892           
15893         });
15894
15895
15896
15897     this.el.on({
15898         "click": this.onClick,
15899         "dblclick": this.onDblClick,
15900         "contextmenu": this.onContextMenu,
15901         scope:this
15902     });
15903
15904     this.selections = [];
15905     this.nodes = [];
15906     this.cmp = new Roo.CompositeElementLite([]);
15907     if(this.store){
15908         this.store = Roo.factory(this.store, Roo.data);
15909         this.setStore(this.store, true);
15910     }
15911     
15912     if ( this.footer && this.footer.xtype) {
15913            
15914          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15915         
15916         this.footer.dataSource = this.store;
15917         this.footer.container = fctr;
15918         this.footer = Roo.factory(this.footer, Roo);
15919         fctr.insertFirst(this.el);
15920         
15921         // this is a bit insane - as the paging toolbar seems to detach the el..
15922 //        dom.parentNode.parentNode.parentNode
15923          // they get detached?
15924     }
15925     
15926     
15927     Roo.View.superclass.constructor.call(this);
15928     
15929     
15930 };
15931
15932 Roo.extend(Roo.View, Roo.util.Observable, {
15933     
15934      /**
15935      * @cfg {Roo.data.Store} store Data store to load data from.
15936      */
15937     store : false,
15938     
15939     /**
15940      * @cfg {String|Roo.Element} el The container element.
15941      */
15942     el : '',
15943     
15944     /**
15945      * @cfg {String|Roo.Template} tpl The template used by this View 
15946      */
15947     tpl : false,
15948     /**
15949      * @cfg {String} dataName the named area of the template to use as the data area
15950      *                          Works with domtemplates roo-name="name"
15951      */
15952     dataName: false,
15953     /**
15954      * @cfg {String} selectedClass The css class to add to selected nodes
15955      */
15956     selectedClass : "x-view-selected",
15957      /**
15958      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15959      */
15960     emptyText : "",
15961     
15962     /**
15963      * @cfg {String} text to display on mask (default Loading)
15964      */
15965     mask : false,
15966     /**
15967      * @cfg {Boolean} multiSelect Allow multiple selection
15968      */
15969     multiSelect : false,
15970     /**
15971      * @cfg {Boolean} singleSelect Allow single selection
15972      */
15973     singleSelect:  false,
15974     
15975     /**
15976      * @cfg {Boolean} toggleSelect - selecting 
15977      */
15978     toggleSelect : false,
15979     
15980     /**
15981      * @cfg {Boolean} tickable - selecting 
15982      */
15983     tickable : false,
15984     
15985     /**
15986      * Returns the element this view is bound to.
15987      * @return {Roo.Element}
15988      */
15989     getEl : function(){
15990         return this.wrapEl;
15991     },
15992     
15993     
15994
15995     /**
15996      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15997      */
15998     refresh : function(){
15999         //Roo.log('refresh');
16000         var t = this.tpl;
16001         
16002         // if we are using something like 'domtemplate', then
16003         // the what gets used is:
16004         // t.applySubtemplate(NAME, data, wrapping data..)
16005         // the outer template then get' applied with
16006         //     the store 'extra data'
16007         // and the body get's added to the
16008         //      roo-name="data" node?
16009         //      <span class='roo-tpl-{name}'></span> ?????
16010         
16011         
16012         
16013         this.clearSelections();
16014         this.el.update("");
16015         var html = [];
16016         var records = this.store.getRange();
16017         if(records.length < 1) {
16018             
16019             // is this valid??  = should it render a template??
16020             
16021             this.el.update(this.emptyText);
16022             return;
16023         }
16024         var el = this.el;
16025         if (this.dataName) {
16026             this.el.update(t.apply(this.store.meta)); //????
16027             el = this.el.child('.roo-tpl-' + this.dataName);
16028         }
16029         
16030         for(var i = 0, len = records.length; i < len; i++){
16031             var data = this.prepareData(records[i].data, i, records[i]);
16032             this.fireEvent("preparedata", this, data, i, records[i]);
16033             
16034             var d = Roo.apply({}, data);
16035             
16036             if(this.tickable){
16037                 Roo.apply(d, {'roo-id' : Roo.id()});
16038                 
16039                 var _this = this;
16040             
16041                 Roo.each(this.parent.item, function(item){
16042                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16043                         return;
16044                     }
16045                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16046                 });
16047             }
16048             
16049             html[html.length] = Roo.util.Format.trim(
16050                 this.dataName ?
16051                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16052                     t.apply(d)
16053             );
16054         }
16055         
16056         
16057         
16058         el.update(html.join(""));
16059         this.nodes = el.dom.childNodes;
16060         this.updateIndexes(0);
16061     },
16062     
16063
16064     /**
16065      * Function to override to reformat the data that is sent to
16066      * the template for each node.
16067      * DEPRICATED - use the preparedata event handler.
16068      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16069      * a JSON object for an UpdateManager bound view).
16070      */
16071     prepareData : function(data, index, record)
16072     {
16073         this.fireEvent("preparedata", this, data, index, record);
16074         return data;
16075     },
16076
16077     onUpdate : function(ds, record){
16078         // Roo.log('on update');   
16079         this.clearSelections();
16080         var index = this.store.indexOf(record);
16081         var n = this.nodes[index];
16082         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16083         n.parentNode.removeChild(n);
16084         this.updateIndexes(index, index);
16085     },
16086
16087     
16088     
16089 // --------- FIXME     
16090     onAdd : function(ds, records, index)
16091     {
16092         //Roo.log(['on Add', ds, records, index] );        
16093         this.clearSelections();
16094         if(this.nodes.length == 0){
16095             this.refresh();
16096             return;
16097         }
16098         var n = this.nodes[index];
16099         for(var i = 0, len = records.length; i < len; i++){
16100             var d = this.prepareData(records[i].data, i, records[i]);
16101             if(n){
16102                 this.tpl.insertBefore(n, d);
16103             }else{
16104                 
16105                 this.tpl.append(this.el, d);
16106             }
16107         }
16108         this.updateIndexes(index);
16109     },
16110
16111     onRemove : function(ds, record, index){
16112        // Roo.log('onRemove');
16113         this.clearSelections();
16114         var el = this.dataName  ?
16115             this.el.child('.roo-tpl-' + this.dataName) :
16116             this.el; 
16117         
16118         el.dom.removeChild(this.nodes[index]);
16119         this.updateIndexes(index);
16120     },
16121
16122     /**
16123      * Refresh an individual node.
16124      * @param {Number} index
16125      */
16126     refreshNode : function(index){
16127         this.onUpdate(this.store, this.store.getAt(index));
16128     },
16129
16130     updateIndexes : function(startIndex, endIndex){
16131         var ns = this.nodes;
16132         startIndex = startIndex || 0;
16133         endIndex = endIndex || ns.length - 1;
16134         for(var i = startIndex; i <= endIndex; i++){
16135             ns[i].nodeIndex = i;
16136         }
16137     },
16138
16139     /**
16140      * Changes the data store this view uses and refresh the view.
16141      * @param {Store} store
16142      */
16143     setStore : function(store, initial){
16144         if(!initial && this.store){
16145             this.store.un("datachanged", this.refresh);
16146             this.store.un("add", this.onAdd);
16147             this.store.un("remove", this.onRemove);
16148             this.store.un("update", this.onUpdate);
16149             this.store.un("clear", this.refresh);
16150             this.store.un("beforeload", this.onBeforeLoad);
16151             this.store.un("load", this.onLoad);
16152             this.store.un("loadexception", this.onLoad);
16153         }
16154         if(store){
16155           
16156             store.on("datachanged", this.refresh, this);
16157             store.on("add", this.onAdd, this);
16158             store.on("remove", this.onRemove, this);
16159             store.on("update", this.onUpdate, this);
16160             store.on("clear", this.refresh, this);
16161             store.on("beforeload", this.onBeforeLoad, this);
16162             store.on("load", this.onLoad, this);
16163             store.on("loadexception", this.onLoad, this);
16164         }
16165         
16166         if(store){
16167             this.refresh();
16168         }
16169     },
16170     /**
16171      * onbeforeLoad - masks the loading area.
16172      *
16173      */
16174     onBeforeLoad : function(store,opts)
16175     {
16176          //Roo.log('onBeforeLoad');   
16177         if (!opts.add) {
16178             this.el.update("");
16179         }
16180         this.el.mask(this.mask ? this.mask : "Loading" ); 
16181     },
16182     onLoad : function ()
16183     {
16184         this.el.unmask();
16185     },
16186     
16187
16188     /**
16189      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16190      * @param {HTMLElement} node
16191      * @return {HTMLElement} The template node
16192      */
16193     findItemFromChild : function(node){
16194         var el = this.dataName  ?
16195             this.el.child('.roo-tpl-' + this.dataName,true) :
16196             this.el.dom; 
16197         
16198         if(!node || node.parentNode == el){
16199                     return node;
16200             }
16201             var p = node.parentNode;
16202             while(p && p != el){
16203             if(p.parentNode == el){
16204                 return p;
16205             }
16206             p = p.parentNode;
16207         }
16208             return null;
16209     },
16210
16211     /** @ignore */
16212     onClick : function(e){
16213         var item = this.findItemFromChild(e.getTarget());
16214         if(item){
16215             var index = this.indexOf(item);
16216             if(this.onItemClick(item, index, e) !== false){
16217                 this.fireEvent("click", this, index, item, e);
16218             }
16219         }else{
16220             this.clearSelections();
16221         }
16222     },
16223
16224     /** @ignore */
16225     onContextMenu : function(e){
16226         var item = this.findItemFromChild(e.getTarget());
16227         if(item){
16228             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16229         }
16230     },
16231
16232     /** @ignore */
16233     onDblClick : function(e){
16234         var item = this.findItemFromChild(e.getTarget());
16235         if(item){
16236             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16237         }
16238     },
16239
16240     onItemClick : function(item, index, e)
16241     {
16242         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16243             return false;
16244         }
16245         if (this.toggleSelect) {
16246             var m = this.isSelected(item) ? 'unselect' : 'select';
16247             //Roo.log(m);
16248             var _t = this;
16249             _t[m](item, true, false);
16250             return true;
16251         }
16252         if(this.multiSelect || this.singleSelect){
16253             if(this.multiSelect && e.shiftKey && this.lastSelection){
16254                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16255             }else{
16256                 this.select(item, this.multiSelect && e.ctrlKey);
16257                 this.lastSelection = item;
16258             }
16259             
16260             if(!this.tickable){
16261                 e.preventDefault();
16262             }
16263             
16264         }
16265         return true;
16266     },
16267
16268     /**
16269      * Get the number of selected nodes.
16270      * @return {Number}
16271      */
16272     getSelectionCount : function(){
16273         return this.selections.length;
16274     },
16275
16276     /**
16277      * Get the currently selected nodes.
16278      * @return {Array} An array of HTMLElements
16279      */
16280     getSelectedNodes : function(){
16281         return this.selections;
16282     },
16283
16284     /**
16285      * Get the indexes of the selected nodes.
16286      * @return {Array}
16287      */
16288     getSelectedIndexes : function(){
16289         var indexes = [], s = this.selections;
16290         for(var i = 0, len = s.length; i < len; i++){
16291             indexes.push(s[i].nodeIndex);
16292         }
16293         return indexes;
16294     },
16295
16296     /**
16297      * Clear all selections
16298      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16299      */
16300     clearSelections : function(suppressEvent){
16301         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16302             this.cmp.elements = this.selections;
16303             this.cmp.removeClass(this.selectedClass);
16304             this.selections = [];
16305             if(!suppressEvent){
16306                 this.fireEvent("selectionchange", this, this.selections);
16307             }
16308         }
16309     },
16310
16311     /**
16312      * Returns true if the passed node is selected
16313      * @param {HTMLElement/Number} node The node or node index
16314      * @return {Boolean}
16315      */
16316     isSelected : function(node){
16317         var s = this.selections;
16318         if(s.length < 1){
16319             return false;
16320         }
16321         node = this.getNode(node);
16322         return s.indexOf(node) !== -1;
16323     },
16324
16325     /**
16326      * Selects nodes.
16327      * @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
16328      * @param {Boolean} keepExisting (optional) true to keep existing selections
16329      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16330      */
16331     select : function(nodeInfo, keepExisting, suppressEvent){
16332         if(nodeInfo instanceof Array){
16333             if(!keepExisting){
16334                 this.clearSelections(true);
16335             }
16336             for(var i = 0, len = nodeInfo.length; i < len; i++){
16337                 this.select(nodeInfo[i], true, true);
16338             }
16339             return;
16340         } 
16341         var node = this.getNode(nodeInfo);
16342         if(!node || this.isSelected(node)){
16343             return; // already selected.
16344         }
16345         if(!keepExisting){
16346             this.clearSelections(true);
16347         }
16348         
16349         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16350             Roo.fly(node).addClass(this.selectedClass);
16351             this.selections.push(node);
16352             if(!suppressEvent){
16353                 this.fireEvent("selectionchange", this, this.selections);
16354             }
16355         }
16356         
16357         
16358     },
16359       /**
16360      * Unselects nodes.
16361      * @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
16362      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16363      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16364      */
16365     unselect : function(nodeInfo, keepExisting, suppressEvent)
16366     {
16367         if(nodeInfo instanceof Array){
16368             Roo.each(this.selections, function(s) {
16369                 this.unselect(s, nodeInfo);
16370             }, this);
16371             return;
16372         }
16373         var node = this.getNode(nodeInfo);
16374         if(!node || !this.isSelected(node)){
16375             //Roo.log("not selected");
16376             return; // not selected.
16377         }
16378         // fireevent???
16379         var ns = [];
16380         Roo.each(this.selections, function(s) {
16381             if (s == node ) {
16382                 Roo.fly(node).removeClass(this.selectedClass);
16383
16384                 return;
16385             }
16386             ns.push(s);
16387         },this);
16388         
16389         this.selections= ns;
16390         this.fireEvent("selectionchange", this, this.selections);
16391     },
16392
16393     /**
16394      * Gets a template node.
16395      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16396      * @return {HTMLElement} The node or null if it wasn't found
16397      */
16398     getNode : function(nodeInfo){
16399         if(typeof nodeInfo == "string"){
16400             return document.getElementById(nodeInfo);
16401         }else if(typeof nodeInfo == "number"){
16402             return this.nodes[nodeInfo];
16403         }
16404         return nodeInfo;
16405     },
16406
16407     /**
16408      * Gets a range template nodes.
16409      * @param {Number} startIndex
16410      * @param {Number} endIndex
16411      * @return {Array} An array of nodes
16412      */
16413     getNodes : function(start, end){
16414         var ns = this.nodes;
16415         start = start || 0;
16416         end = typeof end == "undefined" ? ns.length - 1 : end;
16417         var nodes = [];
16418         if(start <= end){
16419             for(var i = start; i <= end; i++){
16420                 nodes.push(ns[i]);
16421             }
16422         } else{
16423             for(var i = start; i >= end; i--){
16424                 nodes.push(ns[i]);
16425             }
16426         }
16427         return nodes;
16428     },
16429
16430     /**
16431      * Finds the index of the passed node
16432      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16433      * @return {Number} The index of the node or -1
16434      */
16435     indexOf : function(node){
16436         node = this.getNode(node);
16437         if(typeof node.nodeIndex == "number"){
16438             return node.nodeIndex;
16439         }
16440         var ns = this.nodes;
16441         for(var i = 0, len = ns.length; i < len; i++){
16442             if(ns[i] == node){
16443                 return i;
16444             }
16445         }
16446         return -1;
16447     }
16448 });
16449 /*
16450  * - LGPL
16451  *
16452  * based on jquery fullcalendar
16453  * 
16454  */
16455
16456 Roo.bootstrap = Roo.bootstrap || {};
16457 /**
16458  * @class Roo.bootstrap.Calendar
16459  * @extends Roo.bootstrap.Component
16460  * Bootstrap Calendar class
16461  * @cfg {Boolean} loadMask (true|false) default false
16462  * @cfg {Object} header generate the user specific header of the calendar, default false
16463
16464  * @constructor
16465  * Create a new Container
16466  * @param {Object} config The config object
16467  */
16468
16469
16470
16471 Roo.bootstrap.Calendar = function(config){
16472     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16473      this.addEvents({
16474         /**
16475              * @event select
16476              * Fires when a date is selected
16477              * @param {DatePicker} this
16478              * @param {Date} date The selected date
16479              */
16480         'select': true,
16481         /**
16482              * @event monthchange
16483              * Fires when the displayed month changes 
16484              * @param {DatePicker} this
16485              * @param {Date} date The selected month
16486              */
16487         'monthchange': true,
16488         /**
16489              * @event evententer
16490              * Fires when mouse over an event
16491              * @param {Calendar} this
16492              * @param {event} Event
16493              */
16494         'evententer': true,
16495         /**
16496              * @event eventleave
16497              * Fires when the mouse leaves an
16498              * @param {Calendar} this
16499              * @param {event}
16500              */
16501         'eventleave': true,
16502         /**
16503              * @event eventclick
16504              * Fires when the mouse click an
16505              * @param {Calendar} this
16506              * @param {event}
16507              */
16508         'eventclick': true
16509         
16510     });
16511
16512 };
16513
16514 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16515     
16516      /**
16517      * @cfg {Number} startDay
16518      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16519      */
16520     startDay : 0,
16521     
16522     loadMask : false,
16523     
16524     header : false,
16525       
16526     getAutoCreate : function(){
16527         
16528         
16529         var fc_button = function(name, corner, style, content ) {
16530             return Roo.apply({},{
16531                 tag : 'span',
16532                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16533                          (corner.length ?
16534                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16535                             ''
16536                         ),
16537                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16538                 unselectable: 'on'
16539             });
16540         };
16541         
16542         var header = {};
16543         
16544         if(!this.header){
16545             header = {
16546                 tag : 'table',
16547                 cls : 'fc-header',
16548                 style : 'width:100%',
16549                 cn : [
16550                     {
16551                         tag: 'tr',
16552                         cn : [
16553                             {
16554                                 tag : 'td',
16555                                 cls : 'fc-header-left',
16556                                 cn : [
16557                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16558                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16559                                     { tag: 'span', cls: 'fc-header-space' },
16560                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16561
16562
16563                                 ]
16564                             },
16565
16566                             {
16567                                 tag : 'td',
16568                                 cls : 'fc-header-center',
16569                                 cn : [
16570                                     {
16571                                         tag: 'span',
16572                                         cls: 'fc-header-title',
16573                                         cn : {
16574                                             tag: 'H2',
16575                                             html : 'month / year'
16576                                         }
16577                                     }
16578
16579                                 ]
16580                             },
16581                             {
16582                                 tag : 'td',
16583                                 cls : 'fc-header-right',
16584                                 cn : [
16585                               /*      fc_button('month', 'left', '', 'month' ),
16586                                     fc_button('week', '', '', 'week' ),
16587                                     fc_button('day', 'right', '', 'day' )
16588                                 */    
16589
16590                                 ]
16591                             }
16592
16593                         ]
16594                     }
16595                 ]
16596             };
16597         }
16598         
16599         header = this.header;
16600         
16601        
16602         var cal_heads = function() {
16603             var ret = [];
16604             // fixme - handle this.
16605             
16606             for (var i =0; i < Date.dayNames.length; i++) {
16607                 var d = Date.dayNames[i];
16608                 ret.push({
16609                     tag: 'th',
16610                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16611                     html : d.substring(0,3)
16612                 });
16613                 
16614             }
16615             ret[0].cls += ' fc-first';
16616             ret[6].cls += ' fc-last';
16617             return ret;
16618         };
16619         var cal_cell = function(n) {
16620             return  {
16621                 tag: 'td',
16622                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16623                 cn : [
16624                     {
16625                         cn : [
16626                             {
16627                                 cls: 'fc-day-number',
16628                                 html: 'D'
16629                             },
16630                             {
16631                                 cls: 'fc-day-content',
16632                              
16633                                 cn : [
16634                                      {
16635                                         style: 'position: relative;' // height: 17px;
16636                                     }
16637                                 ]
16638                             }
16639                             
16640                             
16641                         ]
16642                     }
16643                 ]
16644                 
16645             }
16646         };
16647         var cal_rows = function() {
16648             
16649             var ret = [];
16650             for (var r = 0; r < 6; r++) {
16651                 var row= {
16652                     tag : 'tr',
16653                     cls : 'fc-week',
16654                     cn : []
16655                 };
16656                 
16657                 for (var i =0; i < Date.dayNames.length; i++) {
16658                     var d = Date.dayNames[i];
16659                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16660
16661                 }
16662                 row.cn[0].cls+=' fc-first';
16663                 row.cn[0].cn[0].style = 'min-height:90px';
16664                 row.cn[6].cls+=' fc-last';
16665                 ret.push(row);
16666                 
16667             }
16668             ret[0].cls += ' fc-first';
16669             ret[4].cls += ' fc-prev-last';
16670             ret[5].cls += ' fc-last';
16671             return ret;
16672             
16673         };
16674         
16675         var cal_table = {
16676             tag: 'table',
16677             cls: 'fc-border-separate',
16678             style : 'width:100%',
16679             cellspacing  : 0,
16680             cn : [
16681                 { 
16682                     tag: 'thead',
16683                     cn : [
16684                         { 
16685                             tag: 'tr',
16686                             cls : 'fc-first fc-last',
16687                             cn : cal_heads()
16688                         }
16689                     ]
16690                 },
16691                 { 
16692                     tag: 'tbody',
16693                     cn : cal_rows()
16694                 }
16695                   
16696             ]
16697         };
16698          
16699          var cfg = {
16700             cls : 'fc fc-ltr',
16701             cn : [
16702                 header,
16703                 {
16704                     cls : 'fc-content',
16705                     style : "position: relative;",
16706                     cn : [
16707                         {
16708                             cls : 'fc-view fc-view-month fc-grid',
16709                             style : 'position: relative',
16710                             unselectable : 'on',
16711                             cn : [
16712                                 {
16713                                     cls : 'fc-event-container',
16714                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16715                                 },
16716                                 cal_table
16717                             ]
16718                         }
16719                     ]
16720     
16721                 }
16722            ] 
16723             
16724         };
16725         
16726          
16727         
16728         return cfg;
16729     },
16730     
16731     
16732     initEvents : function()
16733     {
16734         if(!this.store){
16735             throw "can not find store for calendar";
16736         }
16737         
16738         var mark = {
16739             tag: "div",
16740             cls:"x-dlg-mask",
16741             style: "text-align:center",
16742             cn: [
16743                 {
16744                     tag: "div",
16745                     style: "background-color:white;width:50%;margin:250 auto",
16746                     cn: [
16747                         {
16748                             tag: "img",
16749                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16750                         },
16751                         {
16752                             tag: "span",
16753                             html: "Loading"
16754                         }
16755                         
16756                     ]
16757                 }
16758             ]
16759         };
16760         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16761         
16762         var size = this.el.select('.fc-content', true).first().getSize();
16763         this.maskEl.setSize(size.width, size.height);
16764         this.maskEl.enableDisplayMode("block");
16765         if(!this.loadMask){
16766             this.maskEl.hide();
16767         }
16768         
16769         this.store = Roo.factory(this.store, Roo.data);
16770         this.store.on('load', this.onLoad, this);
16771         this.store.on('beforeload', this.onBeforeLoad, this);
16772         
16773         this.resize();
16774         
16775         this.cells = this.el.select('.fc-day',true);
16776         //Roo.log(this.cells);
16777         this.textNodes = this.el.query('.fc-day-number');
16778         this.cells.addClassOnOver('fc-state-hover');
16779         
16780         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16781         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16782         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16783         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16784         
16785         this.on('monthchange', this.onMonthChange, this);
16786         
16787         this.update(new Date().clearTime());
16788     },
16789     
16790     resize : function() {
16791         var sz  = this.el.getSize();
16792         
16793         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16794         this.el.select('.fc-day-content div',true).setHeight(34);
16795     },
16796     
16797     
16798     // private
16799     showPrevMonth : function(e){
16800         this.update(this.activeDate.add("mo", -1));
16801     },
16802     showToday : function(e){
16803         this.update(new Date().clearTime());
16804     },
16805     // private
16806     showNextMonth : function(e){
16807         this.update(this.activeDate.add("mo", 1));
16808     },
16809
16810     // private
16811     showPrevYear : function(){
16812         this.update(this.activeDate.add("y", -1));
16813     },
16814
16815     // private
16816     showNextYear : function(){
16817         this.update(this.activeDate.add("y", 1));
16818     },
16819
16820     
16821    // private
16822     update : function(date)
16823     {
16824         var vd = this.activeDate;
16825         this.activeDate = date;
16826 //        if(vd && this.el){
16827 //            var t = date.getTime();
16828 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16829 //                Roo.log('using add remove');
16830 //                
16831 //                this.fireEvent('monthchange', this, date);
16832 //                
16833 //                this.cells.removeClass("fc-state-highlight");
16834 //                this.cells.each(function(c){
16835 //                   if(c.dateValue == t){
16836 //                       c.addClass("fc-state-highlight");
16837 //                       setTimeout(function(){
16838 //                            try{c.dom.firstChild.focus();}catch(e){}
16839 //                       }, 50);
16840 //                       return false;
16841 //                   }
16842 //                   return true;
16843 //                });
16844 //                return;
16845 //            }
16846 //        }
16847         
16848         var days = date.getDaysInMonth();
16849         
16850         var firstOfMonth = date.getFirstDateOfMonth();
16851         var startingPos = firstOfMonth.getDay()-this.startDay;
16852         
16853         if(startingPos < this.startDay){
16854             startingPos += 7;
16855         }
16856         
16857         var pm = date.add(Date.MONTH, -1);
16858         var prevStart = pm.getDaysInMonth()-startingPos;
16859 //        
16860         this.cells = this.el.select('.fc-day',true);
16861         this.textNodes = this.el.query('.fc-day-number');
16862         this.cells.addClassOnOver('fc-state-hover');
16863         
16864         var cells = this.cells.elements;
16865         var textEls = this.textNodes;
16866         
16867         Roo.each(cells, function(cell){
16868             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16869         });
16870         
16871         days += startingPos;
16872
16873         // convert everything to numbers so it's fast
16874         var day = 86400000;
16875         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16876         //Roo.log(d);
16877         //Roo.log(pm);
16878         //Roo.log(prevStart);
16879         
16880         var today = new Date().clearTime().getTime();
16881         var sel = date.clearTime().getTime();
16882         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16883         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16884         var ddMatch = this.disabledDatesRE;
16885         var ddText = this.disabledDatesText;
16886         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16887         var ddaysText = this.disabledDaysText;
16888         var format = this.format;
16889         
16890         var setCellClass = function(cal, cell){
16891             cell.row = 0;
16892             cell.events = [];
16893             cell.more = [];
16894             //Roo.log('set Cell Class');
16895             cell.title = "";
16896             var t = d.getTime();
16897             
16898             //Roo.log(d);
16899             
16900             cell.dateValue = t;
16901             if(t == today){
16902                 cell.className += " fc-today";
16903                 cell.className += " fc-state-highlight";
16904                 cell.title = cal.todayText;
16905             }
16906             if(t == sel){
16907                 // disable highlight in other month..
16908                 //cell.className += " fc-state-highlight";
16909                 
16910             }
16911             // disabling
16912             if(t < min) {
16913                 cell.className = " fc-state-disabled";
16914                 cell.title = cal.minText;
16915                 return;
16916             }
16917             if(t > max) {
16918                 cell.className = " fc-state-disabled";
16919                 cell.title = cal.maxText;
16920                 return;
16921             }
16922             if(ddays){
16923                 if(ddays.indexOf(d.getDay()) != -1){
16924                     cell.title = ddaysText;
16925                     cell.className = " fc-state-disabled";
16926                 }
16927             }
16928             if(ddMatch && format){
16929                 var fvalue = d.dateFormat(format);
16930                 if(ddMatch.test(fvalue)){
16931                     cell.title = ddText.replace("%0", fvalue);
16932                     cell.className = " fc-state-disabled";
16933                 }
16934             }
16935             
16936             if (!cell.initialClassName) {
16937                 cell.initialClassName = cell.dom.className;
16938             }
16939             
16940             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16941         };
16942
16943         var i = 0;
16944         
16945         for(; i < startingPos; i++) {
16946             textEls[i].innerHTML = (++prevStart);
16947             d.setDate(d.getDate()+1);
16948             
16949             cells[i].className = "fc-past fc-other-month";
16950             setCellClass(this, cells[i]);
16951         }
16952         
16953         var intDay = 0;
16954         
16955         for(; i < days; i++){
16956             intDay = i - startingPos + 1;
16957             textEls[i].innerHTML = (intDay);
16958             d.setDate(d.getDate()+1);
16959             
16960             cells[i].className = ''; // "x-date-active";
16961             setCellClass(this, cells[i]);
16962         }
16963         var extraDays = 0;
16964         
16965         for(; i < 42; i++) {
16966             textEls[i].innerHTML = (++extraDays);
16967             d.setDate(d.getDate()+1);
16968             
16969             cells[i].className = "fc-future fc-other-month";
16970             setCellClass(this, cells[i]);
16971         }
16972         
16973         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16974         
16975         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16976         
16977         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16978         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16979         
16980         if(totalRows != 6){
16981             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16982             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16983         }
16984         
16985         this.fireEvent('monthchange', this, date);
16986         
16987         
16988         /*
16989         if(!this.internalRender){
16990             var main = this.el.dom.firstChild;
16991             var w = main.offsetWidth;
16992             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16993             Roo.fly(main).setWidth(w);
16994             this.internalRender = true;
16995             // opera does not respect the auto grow header center column
16996             // then, after it gets a width opera refuses to recalculate
16997             // without a second pass
16998             if(Roo.isOpera && !this.secondPass){
16999                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17000                 this.secondPass = true;
17001                 this.update.defer(10, this, [date]);
17002             }
17003         }
17004         */
17005         
17006     },
17007     
17008     findCell : function(dt) {
17009         dt = dt.clearTime().getTime();
17010         var ret = false;
17011         this.cells.each(function(c){
17012             //Roo.log("check " +c.dateValue + '?=' + dt);
17013             if(c.dateValue == dt){
17014                 ret = c;
17015                 return false;
17016             }
17017             return true;
17018         });
17019         
17020         return ret;
17021     },
17022     
17023     findCells : function(ev) {
17024         var s = ev.start.clone().clearTime().getTime();
17025        // Roo.log(s);
17026         var e= ev.end.clone().clearTime().getTime();
17027        // Roo.log(e);
17028         var ret = [];
17029         this.cells.each(function(c){
17030              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17031             
17032             if(c.dateValue > e){
17033                 return ;
17034             }
17035             if(c.dateValue < s){
17036                 return ;
17037             }
17038             ret.push(c);
17039         });
17040         
17041         return ret;    
17042     },
17043     
17044 //    findBestRow: function(cells)
17045 //    {
17046 //        var ret = 0;
17047 //        
17048 //        for (var i =0 ; i < cells.length;i++) {
17049 //            ret  = Math.max(cells[i].rows || 0,ret);
17050 //        }
17051 //        return ret;
17052 //        
17053 //    },
17054     
17055     
17056     addItem : function(ev)
17057     {
17058         // look for vertical location slot in
17059         var cells = this.findCells(ev);
17060         
17061 //        ev.row = this.findBestRow(cells);
17062         
17063         // work out the location.
17064         
17065         var crow = false;
17066         var rows = [];
17067         for(var i =0; i < cells.length; i++) {
17068             
17069             cells[i].row = cells[0].row;
17070             
17071             if(i == 0){
17072                 cells[i].row = cells[i].row + 1;
17073             }
17074             
17075             if (!crow) {
17076                 crow = {
17077                     start : cells[i],
17078                     end :  cells[i]
17079                 };
17080                 continue;
17081             }
17082             if (crow.start.getY() == cells[i].getY()) {
17083                 // on same row.
17084                 crow.end = cells[i];
17085                 continue;
17086             }
17087             // different row.
17088             rows.push(crow);
17089             crow = {
17090                 start: cells[i],
17091                 end : cells[i]
17092             };
17093             
17094         }
17095         
17096         rows.push(crow);
17097         ev.els = [];
17098         ev.rows = rows;
17099         ev.cells = cells;
17100         
17101         cells[0].events.push(ev);
17102         
17103         this.calevents.push(ev);
17104     },
17105     
17106     clearEvents: function() {
17107         
17108         if(!this.calevents){
17109             return;
17110         }
17111         
17112         Roo.each(this.cells.elements, function(c){
17113             c.row = 0;
17114             c.events = [];
17115             c.more = [];
17116         });
17117         
17118         Roo.each(this.calevents, function(e) {
17119             Roo.each(e.els, function(el) {
17120                 el.un('mouseenter' ,this.onEventEnter, this);
17121                 el.un('mouseleave' ,this.onEventLeave, this);
17122                 el.remove();
17123             },this);
17124         },this);
17125         
17126         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17127             e.remove();
17128         });
17129         
17130     },
17131     
17132     renderEvents: function()
17133     {   
17134         var _this = this;
17135         
17136         this.cells.each(function(c) {
17137             
17138             if(c.row < 5){
17139                 return;
17140             }
17141             
17142             var ev = c.events;
17143             
17144             var r = 4;
17145             if(c.row != c.events.length){
17146                 r = 4 - (4 - (c.row - c.events.length));
17147             }
17148             
17149             c.events = ev.slice(0, r);
17150             c.more = ev.slice(r);
17151             
17152             if(c.more.length && c.more.length == 1){
17153                 c.events.push(c.more.pop());
17154             }
17155             
17156             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17157             
17158         });
17159             
17160         this.cells.each(function(c) {
17161             
17162             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17163             
17164             
17165             for (var e = 0; e < c.events.length; e++){
17166                 var ev = c.events[e];
17167                 var rows = ev.rows;
17168                 
17169                 for(var i = 0; i < rows.length; i++) {
17170                 
17171                     // how many rows should it span..
17172
17173                     var  cfg = {
17174                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17175                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17176
17177                         unselectable : "on",
17178                         cn : [
17179                             {
17180                                 cls: 'fc-event-inner',
17181                                 cn : [
17182     //                                {
17183     //                                  tag:'span',
17184     //                                  cls: 'fc-event-time',
17185     //                                  html : cells.length > 1 ? '' : ev.time
17186     //                                },
17187                                     {
17188                                       tag:'span',
17189                                       cls: 'fc-event-title',
17190                                       html : String.format('{0}', ev.title)
17191                                     }
17192
17193
17194                                 ]
17195                             },
17196                             {
17197                                 cls: 'ui-resizable-handle ui-resizable-e',
17198                                 html : '&nbsp;&nbsp;&nbsp'
17199                             }
17200
17201                         ]
17202                     };
17203
17204                     if (i == 0) {
17205                         cfg.cls += ' fc-event-start';
17206                     }
17207                     if ((i+1) == rows.length) {
17208                         cfg.cls += ' fc-event-end';
17209                     }
17210
17211                     var ctr = _this.el.select('.fc-event-container',true).first();
17212                     var cg = ctr.createChild(cfg);
17213
17214                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17215                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17216
17217                     var r = (c.more.length) ? 1 : 0;
17218                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17219                     cg.setWidth(ebox.right - sbox.x -2);
17220
17221                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17222                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17223                     cg.on('click', _this.onEventClick, _this, ev);
17224
17225                     ev.els.push(cg);
17226                     
17227                 }
17228                 
17229             }
17230             
17231             
17232             if(c.more.length){
17233                 var  cfg = {
17234                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17235                     style : 'position: absolute',
17236                     unselectable : "on",
17237                     cn : [
17238                         {
17239                             cls: 'fc-event-inner',
17240                             cn : [
17241                                 {
17242                                   tag:'span',
17243                                   cls: 'fc-event-title',
17244                                   html : 'More'
17245                                 }
17246
17247
17248                             ]
17249                         },
17250                         {
17251                             cls: 'ui-resizable-handle ui-resizable-e',
17252                             html : '&nbsp;&nbsp;&nbsp'
17253                         }
17254
17255                     ]
17256                 };
17257
17258                 var ctr = _this.el.select('.fc-event-container',true).first();
17259                 var cg = ctr.createChild(cfg);
17260
17261                 var sbox = c.select('.fc-day-content',true).first().getBox();
17262                 var ebox = c.select('.fc-day-content',true).first().getBox();
17263                 //Roo.log(cg);
17264                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17265                 cg.setWidth(ebox.right - sbox.x -2);
17266
17267                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17268                 
17269             }
17270             
17271         });
17272         
17273         
17274         
17275     },
17276     
17277     onEventEnter: function (e, el,event,d) {
17278         this.fireEvent('evententer', this, el, event);
17279     },
17280     
17281     onEventLeave: function (e, el,event,d) {
17282         this.fireEvent('eventleave', this, el, event);
17283     },
17284     
17285     onEventClick: function (e, el,event,d) {
17286         this.fireEvent('eventclick', this, el, event);
17287     },
17288     
17289     onMonthChange: function () {
17290         this.store.load();
17291     },
17292     
17293     onMoreEventClick: function(e, el, more)
17294     {
17295         var _this = this;
17296         
17297         this.calpopover.placement = 'right';
17298         this.calpopover.setTitle('More');
17299         
17300         this.calpopover.setContent('');
17301         
17302         var ctr = this.calpopover.el.select('.popover-content', true).first();
17303         
17304         Roo.each(more, function(m){
17305             var cfg = {
17306                 cls : 'fc-event-hori fc-event-draggable',
17307                 html : m.title
17308             };
17309             var cg = ctr.createChild(cfg);
17310             
17311             cg.on('click', _this.onEventClick, _this, m);
17312         });
17313         
17314         this.calpopover.show(el);
17315         
17316         
17317     },
17318     
17319     onLoad: function () 
17320     {   
17321         this.calevents = [];
17322         var cal = this;
17323         
17324         if(this.store.getCount() > 0){
17325             this.store.data.each(function(d){
17326                cal.addItem({
17327                     id : d.data.id,
17328                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17329                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17330                     time : d.data.start_time,
17331                     title : d.data.title,
17332                     description : d.data.description,
17333                     venue : d.data.venue
17334                 });
17335             });
17336         }
17337         
17338         this.renderEvents();
17339         
17340         if(this.calevents.length && this.loadMask){
17341             this.maskEl.hide();
17342         }
17343     },
17344     
17345     onBeforeLoad: function()
17346     {
17347         this.clearEvents();
17348         if(this.loadMask){
17349             this.maskEl.show();
17350         }
17351     }
17352 });
17353
17354  
17355  /*
17356  * - LGPL
17357  *
17358  * element
17359  * 
17360  */
17361
17362 /**
17363  * @class Roo.bootstrap.Popover
17364  * @extends Roo.bootstrap.Component
17365  * Bootstrap Popover class
17366  * @cfg {String} html contents of the popover   (or false to use children..)
17367  * @cfg {String} title of popover (or false to hide)
17368  * @cfg {String} placement how it is placed
17369  * @cfg {String} trigger click || hover (or false to trigger manually)
17370  * @cfg {String} over what (parent or false to trigger manually.)
17371  * @cfg {Number} delay - delay before showing
17372  
17373  * @constructor
17374  * Create a new Popover
17375  * @param {Object} config The config object
17376  */
17377
17378 Roo.bootstrap.Popover = function(config){
17379     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17380     
17381     this.addEvents({
17382         // raw events
17383          /**
17384          * @event show
17385          * After the popover show
17386          * 
17387          * @param {Roo.bootstrap.Popover} this
17388          */
17389         "show" : true,
17390         /**
17391          * @event hide
17392          * After the popover hide
17393          * 
17394          * @param {Roo.bootstrap.Popover} this
17395          */
17396         "hide" : true
17397     });
17398 };
17399
17400 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17401     
17402     title: 'Fill in a title',
17403     html: false,
17404     
17405     placement : 'right',
17406     trigger : 'hover', // hover
17407     
17408     delay : 0,
17409     
17410     over: 'parent',
17411     
17412     can_build_overlaid : false,
17413     
17414     getChildContainer : function()
17415     {
17416         return this.el.select('.popover-content',true).first();
17417     },
17418     
17419     getAutoCreate : function(){
17420          
17421         var cfg = {
17422            cls : 'popover roo-dynamic',
17423            style: 'display:block',
17424            cn : [
17425                 {
17426                     cls : 'arrow'
17427                 },
17428                 {
17429                     cls : 'popover-inner',
17430                     cn : [
17431                         {
17432                             tag: 'h3',
17433                             cls: 'popover-title',
17434                             html : this.title
17435                         },
17436                         {
17437                             cls : 'popover-content',
17438                             html : this.html
17439                         }
17440                     ]
17441                     
17442                 }
17443            ]
17444         };
17445         
17446         return cfg;
17447     },
17448     setTitle: function(str)
17449     {
17450         this.title = str;
17451         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17452     },
17453     setContent: function(str)
17454     {
17455         this.html = str;
17456         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17457     },
17458     // as it get's added to the bottom of the page.
17459     onRender : function(ct, position)
17460     {
17461         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17462         if(!this.el){
17463             var cfg = Roo.apply({},  this.getAutoCreate());
17464             cfg.id = Roo.id();
17465             
17466             if (this.cls) {
17467                 cfg.cls += ' ' + this.cls;
17468             }
17469             if (this.style) {
17470                 cfg.style = this.style;
17471             }
17472             //Roo.log("adding to ");
17473             this.el = Roo.get(document.body).createChild(cfg, position);
17474 //            Roo.log(this.el);
17475         }
17476         this.initEvents();
17477     },
17478     
17479     initEvents : function()
17480     {
17481         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17482         this.el.enableDisplayMode('block');
17483         this.el.hide();
17484         if (this.over === false) {
17485             return; 
17486         }
17487         if (this.triggers === false) {
17488             return;
17489         }
17490         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17491         var triggers = this.trigger ? this.trigger.split(' ') : [];
17492         Roo.each(triggers, function(trigger) {
17493         
17494             if (trigger == 'click') {
17495                 on_el.on('click', this.toggle, this);
17496             } else if (trigger != 'manual') {
17497                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17498                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17499       
17500                 on_el.on(eventIn  ,this.enter, this);
17501                 on_el.on(eventOut, this.leave, this);
17502             }
17503         }, this);
17504         
17505     },
17506     
17507     
17508     // private
17509     timeout : null,
17510     hoverState : null,
17511     
17512     toggle : function () {
17513         this.hoverState == 'in' ? this.leave() : this.enter();
17514     },
17515     
17516     enter : function () {
17517         
17518         clearTimeout(this.timeout);
17519     
17520         this.hoverState = 'in';
17521     
17522         if (!this.delay || !this.delay.show) {
17523             this.show();
17524             return;
17525         }
17526         var _t = this;
17527         this.timeout = setTimeout(function () {
17528             if (_t.hoverState == 'in') {
17529                 _t.show();
17530             }
17531         }, this.delay.show)
17532     },
17533     
17534     leave : function() {
17535         clearTimeout(this.timeout);
17536     
17537         this.hoverState = 'out';
17538     
17539         if (!this.delay || !this.delay.hide) {
17540             this.hide();
17541             return;
17542         }
17543         var _t = this;
17544         this.timeout = setTimeout(function () {
17545             if (_t.hoverState == 'out') {
17546                 _t.hide();
17547             }
17548         }, this.delay.hide)
17549     },
17550     
17551     show : function (on_el)
17552     {
17553         if (!on_el) {
17554             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17555         }
17556         
17557         // set content.
17558         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17559         if (this.html !== false) {
17560             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17561         }
17562         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17563         if (!this.title.length) {
17564             this.el.select('.popover-title',true).hide();
17565         }
17566         
17567         var placement = typeof this.placement == 'function' ?
17568             this.placement.call(this, this.el, on_el) :
17569             this.placement;
17570             
17571         var autoToken = /\s?auto?\s?/i;
17572         var autoPlace = autoToken.test(placement);
17573         if (autoPlace) {
17574             placement = placement.replace(autoToken, '') || 'top';
17575         }
17576         
17577         //this.el.detach()
17578         //this.el.setXY([0,0]);
17579         this.el.show();
17580         this.el.dom.style.display='block';
17581         this.el.addClass(placement);
17582         
17583         //this.el.appendTo(on_el);
17584         
17585         var p = this.getPosition();
17586         var box = this.el.getBox();
17587         
17588         if (autoPlace) {
17589             // fixme..
17590         }
17591         var align = Roo.bootstrap.Popover.alignment[placement];
17592         
17593 //        Roo.log(align);
17594         this.el.alignTo(on_el, align[0],align[1]);
17595         //var arrow = this.el.select('.arrow',true).first();
17596         //arrow.set(align[2], 
17597         
17598         this.el.addClass('in');
17599         
17600         
17601         if (this.el.hasClass('fade')) {
17602             // fade it?
17603         }
17604         
17605         this.hoverState = 'in';
17606         
17607         this.fireEvent('show', this);
17608         
17609     },
17610     hide : function()
17611     {
17612         this.el.setXY([0,0]);
17613         this.el.removeClass('in');
17614         this.el.hide();
17615         this.hoverState = null;
17616         
17617         this.fireEvent('hide', this);
17618     }
17619     
17620 });
17621
17622 Roo.bootstrap.Popover.alignment = {
17623     'left' : ['r-l', [-10,0], 'right'],
17624     'right' : ['l-r', [10,0], 'left'],
17625     'bottom' : ['t-b', [0,10], 'top'],
17626     'top' : [ 'b-t', [0,-10], 'bottom']
17627 };
17628
17629  /*
17630  * - LGPL
17631  *
17632  * Progress
17633  * 
17634  */
17635
17636 /**
17637  * @class Roo.bootstrap.Progress
17638  * @extends Roo.bootstrap.Component
17639  * Bootstrap Progress class
17640  * @cfg {Boolean} striped striped of the progress bar
17641  * @cfg {Boolean} active animated of the progress bar
17642  * 
17643  * 
17644  * @constructor
17645  * Create a new Progress
17646  * @param {Object} config The config object
17647  */
17648
17649 Roo.bootstrap.Progress = function(config){
17650     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17651 };
17652
17653 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17654     
17655     striped : false,
17656     active: false,
17657     
17658     getAutoCreate : function(){
17659         var cfg = {
17660             tag: 'div',
17661             cls: 'progress'
17662         };
17663         
17664         
17665         if(this.striped){
17666             cfg.cls += ' progress-striped';
17667         }
17668       
17669         if(this.active){
17670             cfg.cls += ' active';
17671         }
17672         
17673         
17674         return cfg;
17675     }
17676    
17677 });
17678
17679  
17680
17681  /*
17682  * - LGPL
17683  *
17684  * ProgressBar
17685  * 
17686  */
17687
17688 /**
17689  * @class Roo.bootstrap.ProgressBar
17690  * @extends Roo.bootstrap.Component
17691  * Bootstrap ProgressBar class
17692  * @cfg {Number} aria_valuenow aria-value now
17693  * @cfg {Number} aria_valuemin aria-value min
17694  * @cfg {Number} aria_valuemax aria-value max
17695  * @cfg {String} label label for the progress bar
17696  * @cfg {String} panel (success | info | warning | danger )
17697  * @cfg {String} role role of the progress bar
17698  * @cfg {String} sr_only text
17699  * 
17700  * 
17701  * @constructor
17702  * Create a new ProgressBar
17703  * @param {Object} config The config object
17704  */
17705
17706 Roo.bootstrap.ProgressBar = function(config){
17707     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17708 };
17709
17710 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17711     
17712     aria_valuenow : 0,
17713     aria_valuemin : 0,
17714     aria_valuemax : 100,
17715     label : false,
17716     panel : false,
17717     role : false,
17718     sr_only: false,
17719     
17720     getAutoCreate : function()
17721     {
17722         
17723         var cfg = {
17724             tag: 'div',
17725             cls: 'progress-bar',
17726             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17727         };
17728         
17729         if(this.sr_only){
17730             cfg.cn = {
17731                 tag: 'span',
17732                 cls: 'sr-only',
17733                 html: this.sr_only
17734             }
17735         }
17736         
17737         if(this.role){
17738             cfg.role = this.role;
17739         }
17740         
17741         if(this.aria_valuenow){
17742             cfg['aria-valuenow'] = this.aria_valuenow;
17743         }
17744         
17745         if(this.aria_valuemin){
17746             cfg['aria-valuemin'] = this.aria_valuemin;
17747         }
17748         
17749         if(this.aria_valuemax){
17750             cfg['aria-valuemax'] = this.aria_valuemax;
17751         }
17752         
17753         if(this.label && !this.sr_only){
17754             cfg.html = this.label;
17755         }
17756         
17757         if(this.panel){
17758             cfg.cls += ' progress-bar-' + this.panel;
17759         }
17760         
17761         return cfg;
17762     },
17763     
17764     update : function(aria_valuenow)
17765     {
17766         this.aria_valuenow = aria_valuenow;
17767         
17768         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17769     }
17770    
17771 });
17772
17773  
17774
17775  /*
17776  * - LGPL
17777  *
17778  * column
17779  * 
17780  */
17781
17782 /**
17783  * @class Roo.bootstrap.TabGroup
17784  * @extends Roo.bootstrap.Column
17785  * Bootstrap Column class
17786  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17787  * @cfg {Boolean} carousel true to make the group behave like a carousel
17788  * @cfg {Boolean} bullets show bullets for the panels
17789  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17790  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17791  * @cfg {Boolean} showarrow (true|false) show arrow default true
17792  * 
17793  * @constructor
17794  * Create a new TabGroup
17795  * @param {Object} config The config object
17796  */
17797
17798 Roo.bootstrap.TabGroup = function(config){
17799     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17800     if (!this.navId) {
17801         this.navId = Roo.id();
17802     }
17803     this.tabs = [];
17804     Roo.bootstrap.TabGroup.register(this);
17805     
17806 };
17807
17808 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17809     
17810     carousel : false,
17811     transition : false,
17812     bullets : 0,
17813     timer : 0,
17814     autoslide : false,
17815     slideFn : false,
17816     slideOnTouch : false,
17817     showarrow : true,
17818     
17819     getAutoCreate : function()
17820     {
17821         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17822         
17823         cfg.cls += ' tab-content';
17824         
17825         if (this.carousel) {
17826             cfg.cls += ' carousel slide';
17827             
17828             cfg.cn = [{
17829                cls : 'carousel-inner',
17830                cn : []
17831             }];
17832         
17833             if(this.bullets  && !Roo.isTouch){
17834                 
17835                 var bullets = {
17836                     cls : 'carousel-bullets',
17837                     cn : []
17838                 };
17839                
17840                 if(this.bullets_cls){
17841                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17842                 }
17843                 
17844                 bullets.cn.push({
17845                     cls : 'clear'
17846                 });
17847                 
17848                 cfg.cn[0].cn.push(bullets);
17849             }
17850             
17851             if(this.showarrow){
17852                 cfg.cn[0].cn.push({
17853                     tag : 'div',
17854                     class : 'carousel-arrow',
17855                     cn : [
17856                         {
17857                             tag : 'div',
17858                             class : 'carousel-prev',
17859                             cn : [
17860                                 {
17861                                     tag : 'i',
17862                                     class : 'fa fa-chevron-left'
17863                                 }
17864                             ]
17865                         },
17866                         {
17867                             tag : 'div',
17868                             class : 'carousel-next',
17869                             cn : [
17870                                 {
17871                                     tag : 'i',
17872                                     class : 'fa fa-chevron-right'
17873                                 }
17874                             ]
17875                         }
17876                     ]
17877                 });
17878             }
17879             
17880         }
17881         
17882         return cfg;
17883     },
17884     
17885     initEvents:  function()
17886     {
17887 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17888 //            this.el.on("touchstart", this.onTouchStart, this);
17889 //        }
17890         
17891         if(this.autoslide){
17892             var _this = this;
17893             
17894             this.slideFn = window.setInterval(function() {
17895                 _this.showPanelNext();
17896             }, this.timer);
17897         }
17898         
17899         if(this.showarrow){
17900             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17901             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17902         }
17903         
17904         
17905     },
17906     
17907 //    onTouchStart : function(e, el, o)
17908 //    {
17909 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17910 //            return;
17911 //        }
17912 //        
17913 //        this.showPanelNext();
17914 //    },
17915     
17916     
17917     getChildContainer : function()
17918     {
17919         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17920     },
17921     
17922     /**
17923     * register a Navigation item
17924     * @param {Roo.bootstrap.NavItem} the navitem to add
17925     */
17926     register : function(item)
17927     {
17928         this.tabs.push( item);
17929         item.navId = this.navId; // not really needed..
17930         this.addBullet();
17931     
17932     },
17933     
17934     getActivePanel : function()
17935     {
17936         var r = false;
17937         Roo.each(this.tabs, function(t) {
17938             if (t.active) {
17939                 r = t;
17940                 return false;
17941             }
17942             return null;
17943         });
17944         return r;
17945         
17946     },
17947     getPanelByName : function(n)
17948     {
17949         var r = false;
17950         Roo.each(this.tabs, function(t) {
17951             if (t.tabId == n) {
17952                 r = t;
17953                 return false;
17954             }
17955             return null;
17956         });
17957         return r;
17958     },
17959     indexOfPanel : function(p)
17960     {
17961         var r = false;
17962         Roo.each(this.tabs, function(t,i) {
17963             if (t.tabId == p.tabId) {
17964                 r = i;
17965                 return false;
17966             }
17967             return null;
17968         });
17969         return r;
17970     },
17971     /**
17972      * show a specific panel
17973      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17974      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17975      */
17976     showPanel : function (pan)
17977     {
17978         if(this.transition || typeof(pan) == 'undefined'){
17979             Roo.log("waiting for the transitionend");
17980             return;
17981         }
17982         
17983         if (typeof(pan) == 'number') {
17984             pan = this.tabs[pan];
17985         }
17986         
17987         if (typeof(pan) == 'string') {
17988             pan = this.getPanelByName(pan);
17989         }
17990         
17991         var cur = this.getActivePanel();
17992         
17993         if(!pan || !cur){
17994             Roo.log('pan or acitve pan is undefined');
17995             return false;
17996         }
17997         
17998         if (pan.tabId == this.getActivePanel().tabId) {
17999             return true;
18000         }
18001         
18002         if (false === cur.fireEvent('beforedeactivate')) {
18003             return false;
18004         }
18005         
18006         if(this.bullets > 0 && !Roo.isTouch){
18007             this.setActiveBullet(this.indexOfPanel(pan));
18008         }
18009         
18010         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18011             
18012             this.transition = true;
18013             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18014             var lr = dir == 'next' ? 'left' : 'right';
18015             pan.el.addClass(dir); // or prev
18016             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18017             cur.el.addClass(lr); // or right
18018             pan.el.addClass(lr);
18019             
18020             var _this = this;
18021             cur.el.on('transitionend', function() {
18022                 Roo.log("trans end?");
18023                 
18024                 pan.el.removeClass([lr,dir]);
18025                 pan.setActive(true);
18026                 
18027                 cur.el.removeClass([lr]);
18028                 cur.setActive(false);
18029                 
18030                 _this.transition = false;
18031                 
18032             }, this, { single:  true } );
18033             
18034             return true;
18035         }
18036         
18037         cur.setActive(false);
18038         pan.setActive(true);
18039         
18040         return true;
18041         
18042     },
18043     showPanelNext : function()
18044     {
18045         var i = this.indexOfPanel(this.getActivePanel());
18046         
18047         if (i >= this.tabs.length - 1 && !this.autoslide) {
18048             return;
18049         }
18050         
18051         if (i >= this.tabs.length - 1 && this.autoslide) {
18052             i = -1;
18053         }
18054         
18055         this.showPanel(this.tabs[i+1]);
18056     },
18057     
18058     showPanelPrev : function()
18059     {
18060         var i = this.indexOfPanel(this.getActivePanel());
18061         
18062         if (i  < 1 && !this.autoslide) {
18063             return;
18064         }
18065         
18066         if (i < 1 && this.autoslide) {
18067             i = this.tabs.length;
18068         }
18069         
18070         this.showPanel(this.tabs[i-1]);
18071     },
18072     
18073     
18074     addBullet: function()
18075     {
18076         if(!this.bullets || Roo.isTouch){
18077             return;
18078         }
18079         var ctr = this.el.select('.carousel-bullets',true).first();
18080         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18081         var bullet = ctr.createChild({
18082             cls : 'bullet bullet-' + i
18083         },ctr.dom.lastChild);
18084         
18085         
18086         var _this = this;
18087         
18088         bullet.on('click', (function(e, el, o, ii, t){
18089
18090             e.preventDefault();
18091
18092             this.showPanel(ii);
18093
18094             if(this.autoslide && this.slideFn){
18095                 clearInterval(this.slideFn);
18096                 this.slideFn = window.setInterval(function() {
18097                     _this.showPanelNext();
18098                 }, this.timer);
18099             }
18100
18101         }).createDelegate(this, [i, bullet], true));
18102                 
18103         
18104     },
18105      
18106     setActiveBullet : function(i)
18107     {
18108         if(Roo.isTouch){
18109             return;
18110         }
18111         
18112         Roo.each(this.el.select('.bullet', true).elements, function(el){
18113             el.removeClass('selected');
18114         });
18115
18116         var bullet = this.el.select('.bullet-' + i, true).first();
18117         
18118         if(!bullet){
18119             return;
18120         }
18121         
18122         bullet.addClass('selected');
18123     }
18124     
18125     
18126   
18127 });
18128
18129  
18130
18131  
18132  
18133 Roo.apply(Roo.bootstrap.TabGroup, {
18134     
18135     groups: {},
18136      /**
18137     * register a Navigation Group
18138     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18139     */
18140     register : function(navgrp)
18141     {
18142         this.groups[navgrp.navId] = navgrp;
18143         
18144     },
18145     /**
18146     * fetch a Navigation Group based on the navigation ID
18147     * if one does not exist , it will get created.
18148     * @param {string} the navgroup to add
18149     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18150     */
18151     get: function(navId) {
18152         if (typeof(this.groups[navId]) == 'undefined') {
18153             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18154         }
18155         return this.groups[navId] ;
18156     }
18157     
18158     
18159     
18160 });
18161
18162  /*
18163  * - LGPL
18164  *
18165  * TabPanel
18166  * 
18167  */
18168
18169 /**
18170  * @class Roo.bootstrap.TabPanel
18171  * @extends Roo.bootstrap.Component
18172  * Bootstrap TabPanel class
18173  * @cfg {Boolean} active panel active
18174  * @cfg {String} html panel content
18175  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18176  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18177  * @cfg {String} href click to link..
18178  * 
18179  * 
18180  * @constructor
18181  * Create a new TabPanel
18182  * @param {Object} config The config object
18183  */
18184
18185 Roo.bootstrap.TabPanel = function(config){
18186     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18187     this.addEvents({
18188         /**
18189              * @event changed
18190              * Fires when the active status changes
18191              * @param {Roo.bootstrap.TabPanel} this
18192              * @param {Boolean} state the new state
18193             
18194          */
18195         'changed': true,
18196         /**
18197              * @event beforedeactivate
18198              * Fires before a tab is de-activated - can be used to do validation on a form.
18199              * @param {Roo.bootstrap.TabPanel} this
18200              * @return {Boolean} false if there is an error
18201             
18202          */
18203         'beforedeactivate': true
18204      });
18205     
18206     this.tabId = this.tabId || Roo.id();
18207   
18208 };
18209
18210 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18211     
18212     active: false,
18213     html: false,
18214     tabId: false,
18215     navId : false,
18216     href : '',
18217     
18218     getAutoCreate : function(){
18219         var cfg = {
18220             tag: 'div',
18221             // item is needed for carousel - not sure if it has any effect otherwise
18222             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18223             html: this.html || ''
18224         };
18225         
18226         if(this.active){
18227             cfg.cls += ' active';
18228         }
18229         
18230         if(this.tabId){
18231             cfg.tabId = this.tabId;
18232         }
18233         
18234         
18235         return cfg;
18236     },
18237     
18238     initEvents:  function()
18239     {
18240         var p = this.parent();
18241         
18242         this.navId = this.navId || p.navId;
18243         
18244         if (typeof(this.navId) != 'undefined') {
18245             // not really needed.. but just in case.. parent should be a NavGroup.
18246             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18247             
18248             tg.register(this);
18249             
18250             var i = tg.tabs.length - 1;
18251             
18252             if(this.active && tg.bullets > 0 && i < tg.bullets){
18253                 tg.setActiveBullet(i);
18254             }
18255         }
18256         
18257         this.el.on('click', this.onClick, this);
18258         
18259         if(Roo.isTouch){
18260             this.el.on("touchstart", this.onTouchStart, this);
18261             this.el.on("touchmove", this.onTouchMove, this);
18262             this.el.on("touchend", this.onTouchEnd, this);
18263         }
18264         
18265     },
18266     
18267     onRender : function(ct, position)
18268     {
18269         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18270     },
18271     
18272     setActive : function(state)
18273     {
18274         Roo.log("panel - set active " + this.tabId + "=" + state);
18275         
18276         this.active = state;
18277         if (!state) {
18278             this.el.removeClass('active');
18279             
18280         } else  if (!this.el.hasClass('active')) {
18281             this.el.addClass('active');
18282         }
18283         
18284         this.fireEvent('changed', this, state);
18285     },
18286     
18287     onClick : function(e)
18288     {
18289         e.preventDefault();
18290         
18291         if(!this.href.length){
18292             return;
18293         }
18294         
18295         window.location.href = this.href;
18296     },
18297     
18298     startX : 0,
18299     startY : 0,
18300     endX : 0,
18301     endY : 0,
18302     swiping : false,
18303     
18304     onTouchStart : function(e)
18305     {
18306         this.swiping = false;
18307         
18308         this.startX = e.browserEvent.touches[0].clientX;
18309         this.startY = e.browserEvent.touches[0].clientY;
18310     },
18311     
18312     onTouchMove : function(e)
18313     {
18314         this.swiping = true;
18315         
18316         this.endX = e.browserEvent.touches[0].clientX;
18317         this.endY = e.browserEvent.touches[0].clientY;
18318     },
18319     
18320     onTouchEnd : function(e)
18321     {
18322         if(!this.swiping){
18323             this.onClick(e);
18324             return;
18325         }
18326         
18327         var tabGroup = this.parent();
18328         
18329         if(this.endX > this.startX){ // swiping right
18330             tabGroup.showPanelPrev();
18331             return;
18332         }
18333         
18334         if(this.startX > this.endX){ // swiping left
18335             tabGroup.showPanelNext();
18336             return;
18337         }
18338     }
18339     
18340     
18341 });
18342  
18343
18344  
18345
18346  /*
18347  * - LGPL
18348  *
18349  * DateField
18350  * 
18351  */
18352
18353 /**
18354  * @class Roo.bootstrap.DateField
18355  * @extends Roo.bootstrap.Input
18356  * Bootstrap DateField class
18357  * @cfg {Number} weekStart default 0
18358  * @cfg {String} viewMode default empty, (months|years)
18359  * @cfg {String} minViewMode default empty, (months|years)
18360  * @cfg {Number} startDate default -Infinity
18361  * @cfg {Number} endDate default Infinity
18362  * @cfg {Boolean} todayHighlight default false
18363  * @cfg {Boolean} todayBtn default false
18364  * @cfg {Boolean} calendarWeeks default false
18365  * @cfg {Object} daysOfWeekDisabled default empty
18366  * @cfg {Boolean} singleMode default false (true | false)
18367  * 
18368  * @cfg {Boolean} keyboardNavigation default true
18369  * @cfg {String} language default en
18370  * 
18371  * @constructor
18372  * Create a new DateField
18373  * @param {Object} config The config object
18374  */
18375
18376 Roo.bootstrap.DateField = function(config){
18377     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18378      this.addEvents({
18379             /**
18380              * @event show
18381              * Fires when this field show.
18382              * @param {Roo.bootstrap.DateField} this
18383              * @param {Mixed} date The date value
18384              */
18385             show : true,
18386             /**
18387              * @event show
18388              * Fires when this field hide.
18389              * @param {Roo.bootstrap.DateField} this
18390              * @param {Mixed} date The date value
18391              */
18392             hide : true,
18393             /**
18394              * @event select
18395              * Fires when select a date.
18396              * @param {Roo.bootstrap.DateField} this
18397              * @param {Mixed} date The date value
18398              */
18399             select : true,
18400             /**
18401              * @event beforeselect
18402              * Fires when before select a date.
18403              * @param {Roo.bootstrap.DateField} this
18404              * @param {Mixed} date The date value
18405              */
18406             beforeselect : true
18407         });
18408 };
18409
18410 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18411     
18412     /**
18413      * @cfg {String} format
18414      * The default date format string which can be overriden for localization support.  The format must be
18415      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18416      */
18417     format : "m/d/y",
18418     /**
18419      * @cfg {String} altFormats
18420      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18421      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18422      */
18423     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18424     
18425     weekStart : 0,
18426     
18427     viewMode : '',
18428     
18429     minViewMode : '',
18430     
18431     todayHighlight : false,
18432     
18433     todayBtn: false,
18434     
18435     language: 'en',
18436     
18437     keyboardNavigation: true,
18438     
18439     calendarWeeks: false,
18440     
18441     startDate: -Infinity,
18442     
18443     endDate: Infinity,
18444     
18445     daysOfWeekDisabled: [],
18446     
18447     _events: [],
18448     
18449     singleMode : false,
18450     
18451     UTCDate: function()
18452     {
18453         return new Date(Date.UTC.apply(Date, arguments));
18454     },
18455     
18456     UTCToday: function()
18457     {
18458         var today = new Date();
18459         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18460     },
18461     
18462     getDate: function() {
18463             var d = this.getUTCDate();
18464             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18465     },
18466     
18467     getUTCDate: function() {
18468             return this.date;
18469     },
18470     
18471     setDate: function(d) {
18472             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18473     },
18474     
18475     setUTCDate: function(d) {
18476             this.date = d;
18477             this.setValue(this.formatDate(this.date));
18478     },
18479         
18480     onRender: function(ct, position)
18481     {
18482         
18483         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18484         
18485         this.language = this.language || 'en';
18486         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18487         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18488         
18489         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18490         this.format = this.format || 'm/d/y';
18491         this.isInline = false;
18492         this.isInput = true;
18493         this.component = this.el.select('.add-on', true).first() || false;
18494         this.component = (this.component && this.component.length === 0) ? false : this.component;
18495         this.hasInput = this.component && this.inputEl().length;
18496         
18497         if (typeof(this.minViewMode === 'string')) {
18498             switch (this.minViewMode) {
18499                 case 'months':
18500                     this.minViewMode = 1;
18501                     break;
18502                 case 'years':
18503                     this.minViewMode = 2;
18504                     break;
18505                 default:
18506                     this.minViewMode = 0;
18507                     break;
18508             }
18509         }
18510         
18511         if (typeof(this.viewMode === 'string')) {
18512             switch (this.viewMode) {
18513                 case 'months':
18514                     this.viewMode = 1;
18515                     break;
18516                 case 'years':
18517                     this.viewMode = 2;
18518                     break;
18519                 default:
18520                     this.viewMode = 0;
18521                     break;
18522             }
18523         }
18524                 
18525         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18526         
18527 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18528         
18529         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18530         
18531         this.picker().on('mousedown', this.onMousedown, this);
18532         this.picker().on('click', this.onClick, this);
18533         
18534         this.picker().addClass('datepicker-dropdown');
18535         
18536         this.startViewMode = this.viewMode;
18537         
18538         if(this.singleMode){
18539             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18540                 v.setVisibilityMode(Roo.Element.DISPLAY);
18541                 v.hide();
18542             });
18543             
18544             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18545                 v.setStyle('width', '189px');
18546             });
18547         }
18548         
18549         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18550             if(!this.calendarWeeks){
18551                 v.remove();
18552                 return;
18553             }
18554             
18555             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18556             v.attr('colspan', function(i, val){
18557                 return parseInt(val) + 1;
18558             });
18559         });
18560                         
18561         
18562         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18563         
18564         this.setStartDate(this.startDate);
18565         this.setEndDate(this.endDate);
18566         
18567         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18568         
18569         this.fillDow();
18570         this.fillMonths();
18571         this.update();
18572         this.showMode();
18573         
18574         if(this.isInline) {
18575             this.showPopup();
18576         }
18577     },
18578     
18579     picker : function()
18580     {
18581         return this.pickerEl;
18582 //        return this.el.select('.datepicker', true).first();
18583     },
18584     
18585     fillDow: function()
18586     {
18587         var dowCnt = this.weekStart;
18588         
18589         var dow = {
18590             tag: 'tr',
18591             cn: [
18592                 
18593             ]
18594         };
18595         
18596         if(this.calendarWeeks){
18597             dow.cn.push({
18598                 tag: 'th',
18599                 cls: 'cw',
18600                 html: '&nbsp;'
18601             })
18602         }
18603         
18604         while (dowCnt < this.weekStart + 7) {
18605             dow.cn.push({
18606                 tag: 'th',
18607                 cls: 'dow',
18608                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18609             });
18610         }
18611         
18612         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18613     },
18614     
18615     fillMonths: function()
18616     {    
18617         var i = 0;
18618         var months = this.picker().select('>.datepicker-months td', true).first();
18619         
18620         months.dom.innerHTML = '';
18621         
18622         while (i < 12) {
18623             var month = {
18624                 tag: 'span',
18625                 cls: 'month',
18626                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18627             };
18628             
18629             months.createChild(month);
18630         }
18631         
18632     },
18633     
18634     update: function()
18635     {
18636         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;
18637         
18638         if (this.date < this.startDate) {
18639             this.viewDate = new Date(this.startDate);
18640         } else if (this.date > this.endDate) {
18641             this.viewDate = new Date(this.endDate);
18642         } else {
18643             this.viewDate = new Date(this.date);
18644         }
18645         
18646         this.fill();
18647     },
18648     
18649     fill: function() 
18650     {
18651         var d = new Date(this.viewDate),
18652                 year = d.getUTCFullYear(),
18653                 month = d.getUTCMonth(),
18654                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18655                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18656                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18657                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18658                 currentDate = this.date && this.date.valueOf(),
18659                 today = this.UTCToday();
18660         
18661         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18662         
18663 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18664         
18665 //        this.picker.select('>tfoot th.today').
18666 //                                              .text(dates[this.language].today)
18667 //                                              .toggle(this.todayBtn !== false);
18668     
18669         this.updateNavArrows();
18670         this.fillMonths();
18671                                                 
18672         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18673         
18674         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18675          
18676         prevMonth.setUTCDate(day);
18677         
18678         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18679         
18680         var nextMonth = new Date(prevMonth);
18681         
18682         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18683         
18684         nextMonth = nextMonth.valueOf();
18685         
18686         var fillMonths = false;
18687         
18688         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18689         
18690         while(prevMonth.valueOf() <= nextMonth) {
18691             var clsName = '';
18692             
18693             if (prevMonth.getUTCDay() === this.weekStart) {
18694                 if(fillMonths){
18695                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18696                 }
18697                     
18698                 fillMonths = {
18699                     tag: 'tr',
18700                     cn: []
18701                 };
18702                 
18703                 if(this.calendarWeeks){
18704                     // ISO 8601: First week contains first thursday.
18705                     // ISO also states week starts on Monday, but we can be more abstract here.
18706                     var
18707                     // Start of current week: based on weekstart/current date
18708                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18709                     // Thursday of this week
18710                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18711                     // First Thursday of year, year from thursday
18712                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18713                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18714                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18715                     
18716                     fillMonths.cn.push({
18717                         tag: 'td',
18718                         cls: 'cw',
18719                         html: calWeek
18720                     });
18721                 }
18722             }
18723             
18724             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18725                 clsName += ' old';
18726             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18727                 clsName += ' new';
18728             }
18729             if (this.todayHighlight &&
18730                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18731                 prevMonth.getUTCMonth() == today.getMonth() &&
18732                 prevMonth.getUTCDate() == today.getDate()) {
18733                 clsName += ' today';
18734             }
18735             
18736             if (currentDate && prevMonth.valueOf() === currentDate) {
18737                 clsName += ' active';
18738             }
18739             
18740             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18741                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18742                     clsName += ' disabled';
18743             }
18744             
18745             fillMonths.cn.push({
18746                 tag: 'td',
18747                 cls: 'day ' + clsName,
18748                 html: prevMonth.getDate()
18749             });
18750             
18751             prevMonth.setDate(prevMonth.getDate()+1);
18752         }
18753           
18754         var currentYear = this.date && this.date.getUTCFullYear();
18755         var currentMonth = this.date && this.date.getUTCMonth();
18756         
18757         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18758         
18759         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18760             v.removeClass('active');
18761             
18762             if(currentYear === year && k === currentMonth){
18763                 v.addClass('active');
18764             }
18765             
18766             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18767                 v.addClass('disabled');
18768             }
18769             
18770         });
18771         
18772         
18773         year = parseInt(year/10, 10) * 10;
18774         
18775         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18776         
18777         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18778         
18779         year -= 1;
18780         for (var i = -1; i < 11; i++) {
18781             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18782                 tag: 'span',
18783                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18784                 html: year
18785             });
18786             
18787             year += 1;
18788         }
18789     },
18790     
18791     showMode: function(dir) 
18792     {
18793         if (dir) {
18794             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18795         }
18796         
18797         Roo.each(this.picker().select('>div',true).elements, function(v){
18798             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18799             v.hide();
18800         });
18801         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18802     },
18803     
18804     place: function()
18805     {
18806         if(this.isInline) {
18807             return;
18808         }
18809         
18810         this.picker().removeClass(['bottom', 'top']);
18811         
18812         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18813             /*
18814              * place to the top of element!
18815              *
18816              */
18817             
18818             this.picker().addClass('top');
18819             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18820             
18821             return;
18822         }
18823         
18824         this.picker().addClass('bottom');
18825         
18826         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18827     },
18828     
18829     parseDate : function(value)
18830     {
18831         if(!value || value instanceof Date){
18832             return value;
18833         }
18834         var v = Date.parseDate(value, this.format);
18835         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18836             v = Date.parseDate(value, 'Y-m-d');
18837         }
18838         if(!v && this.altFormats){
18839             if(!this.altFormatsArray){
18840                 this.altFormatsArray = this.altFormats.split("|");
18841             }
18842             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18843                 v = Date.parseDate(value, this.altFormatsArray[i]);
18844             }
18845         }
18846         return v;
18847     },
18848     
18849     formatDate : function(date, fmt)
18850     {   
18851         return (!date || !(date instanceof Date)) ?
18852         date : date.dateFormat(fmt || this.format);
18853     },
18854     
18855     onFocus : function()
18856     {
18857         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18858         this.showPopup();
18859     },
18860     
18861     onBlur : function()
18862     {
18863         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18864         
18865         var d = this.inputEl().getValue();
18866         
18867         this.setValue(d);
18868                 
18869         this.hidePopup();
18870     },
18871     
18872     showPopup : function()
18873     {
18874         this.picker().show();
18875         this.update();
18876         this.place();
18877         
18878         this.fireEvent('showpopup', this, this.date);
18879     },
18880     
18881     hidePopup : function()
18882     {
18883         if(this.isInline) {
18884             return;
18885         }
18886         this.picker().hide();
18887         this.viewMode = this.startViewMode;
18888         this.showMode();
18889         
18890         this.fireEvent('hidepopup', this, this.date);
18891         
18892     },
18893     
18894     onMousedown: function(e)
18895     {
18896         e.stopPropagation();
18897         e.preventDefault();
18898     },
18899     
18900     keyup: function(e)
18901     {
18902         Roo.bootstrap.DateField.superclass.keyup.call(this);
18903         this.update();
18904     },
18905
18906     setValue: function(v)
18907     {
18908         if(this.fireEvent('beforeselect', this, v) !== false){
18909             var d = new Date(this.parseDate(v) ).clearTime();
18910         
18911             if(isNaN(d.getTime())){
18912                 this.date = this.viewDate = '';
18913                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18914                 return;
18915             }
18916
18917             v = this.formatDate(d);
18918
18919             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18920
18921             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18922
18923             this.update();
18924
18925             this.fireEvent('select', this, this.date);
18926         }
18927     },
18928     
18929     getValue: function()
18930     {
18931         return this.formatDate(this.date);
18932     },
18933     
18934     fireKey: function(e)
18935     {
18936         if (!this.picker().isVisible()){
18937             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18938                 this.showPopup();
18939             }
18940             return;
18941         }
18942         
18943         var dateChanged = false,
18944         dir, day, month,
18945         newDate, newViewDate;
18946         
18947         switch(e.keyCode){
18948             case 27: // escape
18949                 this.hidePopup();
18950                 e.preventDefault();
18951                 break;
18952             case 37: // left
18953             case 39: // right
18954                 if (!this.keyboardNavigation) {
18955                     break;
18956                 }
18957                 dir = e.keyCode == 37 ? -1 : 1;
18958                 
18959                 if (e.ctrlKey){
18960                     newDate = this.moveYear(this.date, dir);
18961                     newViewDate = this.moveYear(this.viewDate, dir);
18962                 } else if (e.shiftKey){
18963                     newDate = this.moveMonth(this.date, dir);
18964                     newViewDate = this.moveMonth(this.viewDate, dir);
18965                 } else {
18966                     newDate = new Date(this.date);
18967                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18968                     newViewDate = new Date(this.viewDate);
18969                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18970                 }
18971                 if (this.dateWithinRange(newDate)){
18972                     this.date = newDate;
18973                     this.viewDate = newViewDate;
18974                     this.setValue(this.formatDate(this.date));
18975 //                    this.update();
18976                     e.preventDefault();
18977                     dateChanged = true;
18978                 }
18979                 break;
18980             case 38: // up
18981             case 40: // down
18982                 if (!this.keyboardNavigation) {
18983                     break;
18984                 }
18985                 dir = e.keyCode == 38 ? -1 : 1;
18986                 if (e.ctrlKey){
18987                     newDate = this.moveYear(this.date, dir);
18988                     newViewDate = this.moveYear(this.viewDate, dir);
18989                 } else if (e.shiftKey){
18990                     newDate = this.moveMonth(this.date, dir);
18991                     newViewDate = this.moveMonth(this.viewDate, dir);
18992                 } else {
18993                     newDate = new Date(this.date);
18994                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18995                     newViewDate = new Date(this.viewDate);
18996                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18997                 }
18998                 if (this.dateWithinRange(newDate)){
18999                     this.date = newDate;
19000                     this.viewDate = newViewDate;
19001                     this.setValue(this.formatDate(this.date));
19002 //                    this.update();
19003                     e.preventDefault();
19004                     dateChanged = true;
19005                 }
19006                 break;
19007             case 13: // enter
19008                 this.setValue(this.formatDate(this.date));
19009                 this.hidePopup();
19010                 e.preventDefault();
19011                 break;
19012             case 9: // tab
19013                 this.setValue(this.formatDate(this.date));
19014                 this.hidePopup();
19015                 break;
19016             case 16: // shift
19017             case 17: // ctrl
19018             case 18: // alt
19019                 break;
19020             default :
19021                 this.hide();
19022                 
19023         }
19024     },
19025     
19026     
19027     onClick: function(e) 
19028     {
19029         e.stopPropagation();
19030         e.preventDefault();
19031         
19032         var target = e.getTarget();
19033         
19034         if(target.nodeName.toLowerCase() === 'i'){
19035             target = Roo.get(target).dom.parentNode;
19036         }
19037         
19038         var nodeName = target.nodeName;
19039         var className = target.className;
19040         var html = target.innerHTML;
19041         //Roo.log(nodeName);
19042         
19043         switch(nodeName.toLowerCase()) {
19044             case 'th':
19045                 switch(className) {
19046                     case 'switch':
19047                         this.showMode(1);
19048                         break;
19049                     case 'prev':
19050                     case 'next':
19051                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19052                         switch(this.viewMode){
19053                                 case 0:
19054                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19055                                         break;
19056                                 case 1:
19057                                 case 2:
19058                                         this.viewDate = this.moveYear(this.viewDate, dir);
19059                                         break;
19060                         }
19061                         this.fill();
19062                         break;
19063                     case 'today':
19064                         var date = new Date();
19065                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19066 //                        this.fill()
19067                         this.setValue(this.formatDate(this.date));
19068                         
19069                         this.hidePopup();
19070                         break;
19071                 }
19072                 break;
19073             case 'span':
19074                 if (className.indexOf('disabled') < 0) {
19075                     this.viewDate.setUTCDate(1);
19076                     if (className.indexOf('month') > -1) {
19077                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19078                     } else {
19079                         var year = parseInt(html, 10) || 0;
19080                         this.viewDate.setUTCFullYear(year);
19081                         
19082                     }
19083                     
19084                     if(this.singleMode){
19085                         this.setValue(this.formatDate(this.viewDate));
19086                         this.hidePopup();
19087                         return;
19088                     }
19089                     
19090                     this.showMode(-1);
19091                     this.fill();
19092                 }
19093                 break;
19094                 
19095             case 'td':
19096                 //Roo.log(className);
19097                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19098                     var day = parseInt(html, 10) || 1;
19099                     var year = this.viewDate.getUTCFullYear(),
19100                         month = this.viewDate.getUTCMonth();
19101
19102                     if (className.indexOf('old') > -1) {
19103                         if(month === 0 ){
19104                             month = 11;
19105                             year -= 1;
19106                         }else{
19107                             month -= 1;
19108                         }
19109                     } else if (className.indexOf('new') > -1) {
19110                         if (month == 11) {
19111                             month = 0;
19112                             year += 1;
19113                         } else {
19114                             month += 1;
19115                         }
19116                     }
19117                     //Roo.log([year,month,day]);
19118                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19119                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19120 //                    this.fill();
19121                     //Roo.log(this.formatDate(this.date));
19122                     this.setValue(this.formatDate(this.date));
19123                     this.hidePopup();
19124                 }
19125                 break;
19126         }
19127     },
19128     
19129     setStartDate: function(startDate)
19130     {
19131         this.startDate = startDate || -Infinity;
19132         if (this.startDate !== -Infinity) {
19133             this.startDate = this.parseDate(this.startDate);
19134         }
19135         this.update();
19136         this.updateNavArrows();
19137     },
19138
19139     setEndDate: function(endDate)
19140     {
19141         this.endDate = endDate || Infinity;
19142         if (this.endDate !== Infinity) {
19143             this.endDate = this.parseDate(this.endDate);
19144         }
19145         this.update();
19146         this.updateNavArrows();
19147     },
19148     
19149     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19150     {
19151         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19152         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19153             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19154         }
19155         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19156             return parseInt(d, 10);
19157         });
19158         this.update();
19159         this.updateNavArrows();
19160     },
19161     
19162     updateNavArrows: function() 
19163     {
19164         if(this.singleMode){
19165             return;
19166         }
19167         
19168         var d = new Date(this.viewDate),
19169         year = d.getUTCFullYear(),
19170         month = d.getUTCMonth();
19171         
19172         Roo.each(this.picker().select('.prev', true).elements, function(v){
19173             v.show();
19174             switch (this.viewMode) {
19175                 case 0:
19176
19177                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19178                         v.hide();
19179                     }
19180                     break;
19181                 case 1:
19182                 case 2:
19183                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19184                         v.hide();
19185                     }
19186                     break;
19187             }
19188         });
19189         
19190         Roo.each(this.picker().select('.next', true).elements, function(v){
19191             v.show();
19192             switch (this.viewMode) {
19193                 case 0:
19194
19195                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19196                         v.hide();
19197                     }
19198                     break;
19199                 case 1:
19200                 case 2:
19201                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19202                         v.hide();
19203                     }
19204                     break;
19205             }
19206         })
19207     },
19208     
19209     moveMonth: function(date, dir)
19210     {
19211         if (!dir) {
19212             return date;
19213         }
19214         var new_date = new Date(date.valueOf()),
19215         day = new_date.getUTCDate(),
19216         month = new_date.getUTCMonth(),
19217         mag = Math.abs(dir),
19218         new_month, test;
19219         dir = dir > 0 ? 1 : -1;
19220         if (mag == 1){
19221             test = dir == -1
19222             // If going back one month, make sure month is not current month
19223             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19224             ? function(){
19225                 return new_date.getUTCMonth() == month;
19226             }
19227             // If going forward one month, make sure month is as expected
19228             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19229             : function(){
19230                 return new_date.getUTCMonth() != new_month;
19231             };
19232             new_month = month + dir;
19233             new_date.setUTCMonth(new_month);
19234             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19235             if (new_month < 0 || new_month > 11) {
19236                 new_month = (new_month + 12) % 12;
19237             }
19238         } else {
19239             // For magnitudes >1, move one month at a time...
19240             for (var i=0; i<mag; i++) {
19241                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19242                 new_date = this.moveMonth(new_date, dir);
19243             }
19244             // ...then reset the day, keeping it in the new month
19245             new_month = new_date.getUTCMonth();
19246             new_date.setUTCDate(day);
19247             test = function(){
19248                 return new_month != new_date.getUTCMonth();
19249             };
19250         }
19251         // Common date-resetting loop -- if date is beyond end of month, make it
19252         // end of month
19253         while (test()){
19254             new_date.setUTCDate(--day);
19255             new_date.setUTCMonth(new_month);
19256         }
19257         return new_date;
19258     },
19259
19260     moveYear: function(date, dir)
19261     {
19262         return this.moveMonth(date, dir*12);
19263     },
19264
19265     dateWithinRange: function(date)
19266     {
19267         return date >= this.startDate && date <= this.endDate;
19268     },
19269
19270     
19271     remove: function() 
19272     {
19273         this.picker().remove();
19274     },
19275     
19276     validateValue : function(value)
19277     {
19278         if(this.getVisibilityEl().hasClass('hidden')){
19279             return true;
19280         }
19281         
19282         if(value.length < 1)  {
19283             if(this.allowBlank){
19284                 return true;
19285             }
19286             return false;
19287         }
19288         
19289         if(value.length < this.minLength){
19290             return false;
19291         }
19292         if(value.length > this.maxLength){
19293             return false;
19294         }
19295         if(this.vtype){
19296             var vt = Roo.form.VTypes;
19297             if(!vt[this.vtype](value, this)){
19298                 return false;
19299             }
19300         }
19301         if(typeof this.validator == "function"){
19302             var msg = this.validator(value);
19303             if(msg !== true){
19304                 return false;
19305             }
19306         }
19307         
19308         if(this.regex && !this.regex.test(value)){
19309             return false;
19310         }
19311         
19312         if(typeof(this.parseDate(value)) == 'undefined'){
19313             return false;
19314         }
19315         
19316         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19317             return false;
19318         }      
19319         
19320         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19321             return false;
19322         } 
19323         
19324         
19325         return true;
19326     },
19327     
19328     reset : function()
19329     {
19330         this.date = this.viewDate = '';
19331         
19332         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19333     }
19334    
19335 });
19336
19337 Roo.apply(Roo.bootstrap.DateField,  {
19338     
19339     head : {
19340         tag: 'thead',
19341         cn: [
19342         {
19343             tag: 'tr',
19344             cn: [
19345             {
19346                 tag: 'th',
19347                 cls: 'prev',
19348                 html: '<i class="fa fa-arrow-left"/>'
19349             },
19350             {
19351                 tag: 'th',
19352                 cls: 'switch',
19353                 colspan: '5'
19354             },
19355             {
19356                 tag: 'th',
19357                 cls: 'next',
19358                 html: '<i class="fa fa-arrow-right"/>'
19359             }
19360
19361             ]
19362         }
19363         ]
19364     },
19365     
19366     content : {
19367         tag: 'tbody',
19368         cn: [
19369         {
19370             tag: 'tr',
19371             cn: [
19372             {
19373                 tag: 'td',
19374                 colspan: '7'
19375             }
19376             ]
19377         }
19378         ]
19379     },
19380     
19381     footer : {
19382         tag: 'tfoot',
19383         cn: [
19384         {
19385             tag: 'tr',
19386             cn: [
19387             {
19388                 tag: 'th',
19389                 colspan: '7',
19390                 cls: 'today'
19391             }
19392                     
19393             ]
19394         }
19395         ]
19396     },
19397     
19398     dates:{
19399         en: {
19400             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19401             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19402             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19403             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19404             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19405             today: "Today"
19406         }
19407     },
19408     
19409     modes: [
19410     {
19411         clsName: 'days',
19412         navFnc: 'Month',
19413         navStep: 1
19414     },
19415     {
19416         clsName: 'months',
19417         navFnc: 'FullYear',
19418         navStep: 1
19419     },
19420     {
19421         clsName: 'years',
19422         navFnc: 'FullYear',
19423         navStep: 10
19424     }]
19425 });
19426
19427 Roo.apply(Roo.bootstrap.DateField,  {
19428   
19429     template : {
19430         tag: 'div',
19431         cls: 'datepicker dropdown-menu roo-dynamic',
19432         cn: [
19433         {
19434             tag: 'div',
19435             cls: 'datepicker-days',
19436             cn: [
19437             {
19438                 tag: 'table',
19439                 cls: 'table-condensed',
19440                 cn:[
19441                 Roo.bootstrap.DateField.head,
19442                 {
19443                     tag: 'tbody'
19444                 },
19445                 Roo.bootstrap.DateField.footer
19446                 ]
19447             }
19448             ]
19449         },
19450         {
19451             tag: 'div',
19452             cls: 'datepicker-months',
19453             cn: [
19454             {
19455                 tag: 'table',
19456                 cls: 'table-condensed',
19457                 cn:[
19458                 Roo.bootstrap.DateField.head,
19459                 Roo.bootstrap.DateField.content,
19460                 Roo.bootstrap.DateField.footer
19461                 ]
19462             }
19463             ]
19464         },
19465         {
19466             tag: 'div',
19467             cls: 'datepicker-years',
19468             cn: [
19469             {
19470                 tag: 'table',
19471                 cls: 'table-condensed',
19472                 cn:[
19473                 Roo.bootstrap.DateField.head,
19474                 Roo.bootstrap.DateField.content,
19475                 Roo.bootstrap.DateField.footer
19476                 ]
19477             }
19478             ]
19479         }
19480         ]
19481     }
19482 });
19483
19484  
19485
19486  /*
19487  * - LGPL
19488  *
19489  * TimeField
19490  * 
19491  */
19492
19493 /**
19494  * @class Roo.bootstrap.TimeField
19495  * @extends Roo.bootstrap.Input
19496  * Bootstrap DateField class
19497  * 
19498  * 
19499  * @constructor
19500  * Create a new TimeField
19501  * @param {Object} config The config object
19502  */
19503
19504 Roo.bootstrap.TimeField = function(config){
19505     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19506     this.addEvents({
19507             /**
19508              * @event show
19509              * Fires when this field show.
19510              * @param {Roo.bootstrap.DateField} thisthis
19511              * @param {Mixed} date The date value
19512              */
19513             show : true,
19514             /**
19515              * @event show
19516              * Fires when this field hide.
19517              * @param {Roo.bootstrap.DateField} this
19518              * @param {Mixed} date The date value
19519              */
19520             hide : true,
19521             /**
19522              * @event select
19523              * Fires when select a date.
19524              * @param {Roo.bootstrap.DateField} this
19525              * @param {Mixed} date The date value
19526              */
19527             select : true
19528         });
19529 };
19530
19531 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19532     
19533     /**
19534      * @cfg {String} format
19535      * The default time format string which can be overriden for localization support.  The format must be
19536      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19537      */
19538     format : "H:i",
19539        
19540     onRender: function(ct, position)
19541     {
19542         
19543         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19544                 
19545         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19546         
19547         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19548         
19549         this.pop = this.picker().select('>.datepicker-time',true).first();
19550         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19551         
19552         this.picker().on('mousedown', this.onMousedown, this);
19553         this.picker().on('click', this.onClick, this);
19554         
19555         this.picker().addClass('datepicker-dropdown');
19556     
19557         this.fillTime();
19558         this.update();
19559             
19560         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19561         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19562         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19563         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19564         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19565         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19566
19567     },
19568     
19569     fireKey: function(e){
19570         if (!this.picker().isVisible()){
19571             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19572                 this.show();
19573             }
19574             return;
19575         }
19576
19577         e.preventDefault();
19578         
19579         switch(e.keyCode){
19580             case 27: // escape
19581                 this.hide();
19582                 break;
19583             case 37: // left
19584             case 39: // right
19585                 this.onTogglePeriod();
19586                 break;
19587             case 38: // up
19588                 this.onIncrementMinutes();
19589                 break;
19590             case 40: // down
19591                 this.onDecrementMinutes();
19592                 break;
19593             case 13: // enter
19594             case 9: // tab
19595                 this.setTime();
19596                 break;
19597         }
19598     },
19599     
19600     onClick: function(e) {
19601         e.stopPropagation();
19602         e.preventDefault();
19603     },
19604     
19605     picker : function()
19606     {
19607         return this.el.select('.datepicker', true).first();
19608     },
19609     
19610     fillTime: function()
19611     {    
19612         var time = this.pop.select('tbody', true).first();
19613         
19614         time.dom.innerHTML = '';
19615         
19616         time.createChild({
19617             tag: 'tr',
19618             cn: [
19619                 {
19620                     tag: 'td',
19621                     cn: [
19622                         {
19623                             tag: 'a',
19624                             href: '#',
19625                             cls: 'btn',
19626                             cn: [
19627                                 {
19628                                     tag: 'span',
19629                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19630                                 }
19631                             ]
19632                         } 
19633                     ]
19634                 },
19635                 {
19636                     tag: 'td',
19637                     cls: 'separator'
19638                 },
19639                 {
19640                     tag: 'td',
19641                     cn: [
19642                         {
19643                             tag: 'a',
19644                             href: '#',
19645                             cls: 'btn',
19646                             cn: [
19647                                 {
19648                                     tag: 'span',
19649                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19650                                 }
19651                             ]
19652                         }
19653                     ]
19654                 },
19655                 {
19656                     tag: 'td',
19657                     cls: 'separator'
19658                 }
19659             ]
19660         });
19661         
19662         time.createChild({
19663             tag: 'tr',
19664             cn: [
19665                 {
19666                     tag: 'td',
19667                     cn: [
19668                         {
19669                             tag: 'span',
19670                             cls: 'timepicker-hour',
19671                             html: '00'
19672                         }  
19673                     ]
19674                 },
19675                 {
19676                     tag: 'td',
19677                     cls: 'separator',
19678                     html: ':'
19679                 },
19680                 {
19681                     tag: 'td',
19682                     cn: [
19683                         {
19684                             tag: 'span',
19685                             cls: 'timepicker-minute',
19686                             html: '00'
19687                         }  
19688                     ]
19689                 },
19690                 {
19691                     tag: 'td',
19692                     cls: 'separator'
19693                 },
19694                 {
19695                     tag: 'td',
19696                     cn: [
19697                         {
19698                             tag: 'button',
19699                             type: 'button',
19700                             cls: 'btn btn-primary period',
19701                             html: 'AM'
19702                             
19703                         }
19704                     ]
19705                 }
19706             ]
19707         });
19708         
19709         time.createChild({
19710             tag: 'tr',
19711             cn: [
19712                 {
19713                     tag: 'td',
19714                     cn: [
19715                         {
19716                             tag: 'a',
19717                             href: '#',
19718                             cls: 'btn',
19719                             cn: [
19720                                 {
19721                                     tag: 'span',
19722                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19723                                 }
19724                             ]
19725                         }
19726                     ]
19727                 },
19728                 {
19729                     tag: 'td',
19730                     cls: 'separator'
19731                 },
19732                 {
19733                     tag: 'td',
19734                     cn: [
19735                         {
19736                             tag: 'a',
19737                             href: '#',
19738                             cls: 'btn',
19739                             cn: [
19740                                 {
19741                                     tag: 'span',
19742                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19743                                 }
19744                             ]
19745                         }
19746                     ]
19747                 },
19748                 {
19749                     tag: 'td',
19750                     cls: 'separator'
19751                 }
19752             ]
19753         });
19754         
19755     },
19756     
19757     update: function()
19758     {
19759         
19760         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19761         
19762         this.fill();
19763     },
19764     
19765     fill: function() 
19766     {
19767         var hours = this.time.getHours();
19768         var minutes = this.time.getMinutes();
19769         var period = 'AM';
19770         
19771         if(hours > 11){
19772             period = 'PM';
19773         }
19774         
19775         if(hours == 0){
19776             hours = 12;
19777         }
19778         
19779         
19780         if(hours > 12){
19781             hours = hours - 12;
19782         }
19783         
19784         if(hours < 10){
19785             hours = '0' + hours;
19786         }
19787         
19788         if(minutes < 10){
19789             minutes = '0' + minutes;
19790         }
19791         
19792         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19793         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19794         this.pop.select('button', true).first().dom.innerHTML = period;
19795         
19796     },
19797     
19798     place: function()
19799     {   
19800         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19801         
19802         var cls = ['bottom'];
19803         
19804         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19805             cls.pop();
19806             cls.push('top');
19807         }
19808         
19809         cls.push('right');
19810         
19811         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19812             cls.pop();
19813             cls.push('left');
19814         }
19815         
19816         this.picker().addClass(cls.join('-'));
19817         
19818         var _this = this;
19819         
19820         Roo.each(cls, function(c){
19821             if(c == 'bottom'){
19822                 _this.picker().setTop(_this.inputEl().getHeight());
19823                 return;
19824             }
19825             if(c == 'top'){
19826                 _this.picker().setTop(0 - _this.picker().getHeight());
19827                 return;
19828             }
19829             
19830             if(c == 'left'){
19831                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19832                 return;
19833             }
19834             if(c == 'right'){
19835                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19836                 return;
19837             }
19838         });
19839         
19840     },
19841   
19842     onFocus : function()
19843     {
19844         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19845         this.show();
19846     },
19847     
19848     onBlur : function()
19849     {
19850         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19851         this.hide();
19852     },
19853     
19854     show : function()
19855     {
19856         this.picker().show();
19857         this.pop.show();
19858         this.update();
19859         this.place();
19860         
19861         this.fireEvent('show', this, this.date);
19862     },
19863     
19864     hide : function()
19865     {
19866         this.picker().hide();
19867         this.pop.hide();
19868         
19869         this.fireEvent('hide', this, this.date);
19870     },
19871     
19872     setTime : function()
19873     {
19874         this.hide();
19875         this.setValue(this.time.format(this.format));
19876         
19877         this.fireEvent('select', this, this.date);
19878         
19879         
19880     },
19881     
19882     onMousedown: function(e){
19883         e.stopPropagation();
19884         e.preventDefault();
19885     },
19886     
19887     onIncrementHours: function()
19888     {
19889         Roo.log('onIncrementHours');
19890         this.time = this.time.add(Date.HOUR, 1);
19891         this.update();
19892         
19893     },
19894     
19895     onDecrementHours: function()
19896     {
19897         Roo.log('onDecrementHours');
19898         this.time = this.time.add(Date.HOUR, -1);
19899         this.update();
19900     },
19901     
19902     onIncrementMinutes: function()
19903     {
19904         Roo.log('onIncrementMinutes');
19905         this.time = this.time.add(Date.MINUTE, 1);
19906         this.update();
19907     },
19908     
19909     onDecrementMinutes: function()
19910     {
19911         Roo.log('onDecrementMinutes');
19912         this.time = this.time.add(Date.MINUTE, -1);
19913         this.update();
19914     },
19915     
19916     onTogglePeriod: function()
19917     {
19918         Roo.log('onTogglePeriod');
19919         this.time = this.time.add(Date.HOUR, 12);
19920         this.update();
19921     }
19922     
19923    
19924 });
19925
19926 Roo.apply(Roo.bootstrap.TimeField,  {
19927     
19928     content : {
19929         tag: 'tbody',
19930         cn: [
19931             {
19932                 tag: 'tr',
19933                 cn: [
19934                 {
19935                     tag: 'td',
19936                     colspan: '7'
19937                 }
19938                 ]
19939             }
19940         ]
19941     },
19942     
19943     footer : {
19944         tag: 'tfoot',
19945         cn: [
19946             {
19947                 tag: 'tr',
19948                 cn: [
19949                 {
19950                     tag: 'th',
19951                     colspan: '7',
19952                     cls: '',
19953                     cn: [
19954                         {
19955                             tag: 'button',
19956                             cls: 'btn btn-info ok',
19957                             html: 'OK'
19958                         }
19959                     ]
19960                 }
19961
19962                 ]
19963             }
19964         ]
19965     }
19966 });
19967
19968 Roo.apply(Roo.bootstrap.TimeField,  {
19969   
19970     template : {
19971         tag: 'div',
19972         cls: 'datepicker dropdown-menu',
19973         cn: [
19974             {
19975                 tag: 'div',
19976                 cls: 'datepicker-time',
19977                 cn: [
19978                 {
19979                     tag: 'table',
19980                     cls: 'table-condensed',
19981                     cn:[
19982                     Roo.bootstrap.TimeField.content,
19983                     Roo.bootstrap.TimeField.footer
19984                     ]
19985                 }
19986                 ]
19987             }
19988         ]
19989     }
19990 });
19991
19992  
19993
19994  /*
19995  * - LGPL
19996  *
19997  * MonthField
19998  * 
19999  */
20000
20001 /**
20002  * @class Roo.bootstrap.MonthField
20003  * @extends Roo.bootstrap.Input
20004  * Bootstrap MonthField class
20005  * 
20006  * @cfg {String} language default en
20007  * 
20008  * @constructor
20009  * Create a new MonthField
20010  * @param {Object} config The config object
20011  */
20012
20013 Roo.bootstrap.MonthField = function(config){
20014     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20015     
20016     this.addEvents({
20017         /**
20018          * @event show
20019          * Fires when this field show.
20020          * @param {Roo.bootstrap.MonthField} this
20021          * @param {Mixed} date The date value
20022          */
20023         show : true,
20024         /**
20025          * @event show
20026          * Fires when this field hide.
20027          * @param {Roo.bootstrap.MonthField} this
20028          * @param {Mixed} date The date value
20029          */
20030         hide : true,
20031         /**
20032          * @event select
20033          * Fires when select a date.
20034          * @param {Roo.bootstrap.MonthField} this
20035          * @param {String} oldvalue The old value
20036          * @param {String} newvalue The new value
20037          */
20038         select : true
20039     });
20040 };
20041
20042 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20043     
20044     onRender: function(ct, position)
20045     {
20046         
20047         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20048         
20049         this.language = this.language || 'en';
20050         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20051         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20052         
20053         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20054         this.isInline = false;
20055         this.isInput = true;
20056         this.component = this.el.select('.add-on', true).first() || false;
20057         this.component = (this.component && this.component.length === 0) ? false : this.component;
20058         this.hasInput = this.component && this.inputEL().length;
20059         
20060         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20061         
20062         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20063         
20064         this.picker().on('mousedown', this.onMousedown, this);
20065         this.picker().on('click', this.onClick, this);
20066         
20067         this.picker().addClass('datepicker-dropdown');
20068         
20069         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20070             v.setStyle('width', '189px');
20071         });
20072         
20073         this.fillMonths();
20074         
20075         this.update();
20076         
20077         if(this.isInline) {
20078             this.show();
20079         }
20080         
20081     },
20082     
20083     setValue: function(v, suppressEvent)
20084     {   
20085         var o = this.getValue();
20086         
20087         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20088         
20089         this.update();
20090
20091         if(suppressEvent !== true){
20092             this.fireEvent('select', this, o, v);
20093         }
20094         
20095     },
20096     
20097     getValue: function()
20098     {
20099         return this.value;
20100     },
20101     
20102     onClick: function(e) 
20103     {
20104         e.stopPropagation();
20105         e.preventDefault();
20106         
20107         var target = e.getTarget();
20108         
20109         if(target.nodeName.toLowerCase() === 'i'){
20110             target = Roo.get(target).dom.parentNode;
20111         }
20112         
20113         var nodeName = target.nodeName;
20114         var className = target.className;
20115         var html = target.innerHTML;
20116         
20117         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20118             return;
20119         }
20120         
20121         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20122         
20123         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20124         
20125         this.hide();
20126                         
20127     },
20128     
20129     picker : function()
20130     {
20131         return this.pickerEl;
20132     },
20133     
20134     fillMonths: function()
20135     {    
20136         var i = 0;
20137         var months = this.picker().select('>.datepicker-months td', true).first();
20138         
20139         months.dom.innerHTML = '';
20140         
20141         while (i < 12) {
20142             var month = {
20143                 tag: 'span',
20144                 cls: 'month',
20145                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20146             };
20147             
20148             months.createChild(month);
20149         }
20150         
20151     },
20152     
20153     update: function()
20154     {
20155         var _this = this;
20156         
20157         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20158             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20159         }
20160         
20161         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20162             e.removeClass('active');
20163             
20164             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20165                 e.addClass('active');
20166             }
20167         })
20168     },
20169     
20170     place: function()
20171     {
20172         if(this.isInline) {
20173             return;
20174         }
20175         
20176         this.picker().removeClass(['bottom', 'top']);
20177         
20178         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20179             /*
20180              * place to the top of element!
20181              *
20182              */
20183             
20184             this.picker().addClass('top');
20185             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20186             
20187             return;
20188         }
20189         
20190         this.picker().addClass('bottom');
20191         
20192         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20193     },
20194     
20195     onFocus : function()
20196     {
20197         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20198         this.show();
20199     },
20200     
20201     onBlur : function()
20202     {
20203         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20204         
20205         var d = this.inputEl().getValue();
20206         
20207         this.setValue(d);
20208                 
20209         this.hide();
20210     },
20211     
20212     show : function()
20213     {
20214         this.picker().show();
20215         this.picker().select('>.datepicker-months', true).first().show();
20216         this.update();
20217         this.place();
20218         
20219         this.fireEvent('show', this, this.date);
20220     },
20221     
20222     hide : function()
20223     {
20224         if(this.isInline) {
20225             return;
20226         }
20227         this.picker().hide();
20228         this.fireEvent('hide', this, this.date);
20229         
20230     },
20231     
20232     onMousedown: function(e)
20233     {
20234         e.stopPropagation();
20235         e.preventDefault();
20236     },
20237     
20238     keyup: function(e)
20239     {
20240         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20241         this.update();
20242     },
20243
20244     fireKey: function(e)
20245     {
20246         if (!this.picker().isVisible()){
20247             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20248                 this.show();
20249             }
20250             return;
20251         }
20252         
20253         var dir;
20254         
20255         switch(e.keyCode){
20256             case 27: // escape
20257                 this.hide();
20258                 e.preventDefault();
20259                 break;
20260             case 37: // left
20261             case 39: // right
20262                 dir = e.keyCode == 37 ? -1 : 1;
20263                 
20264                 this.vIndex = this.vIndex + dir;
20265                 
20266                 if(this.vIndex < 0){
20267                     this.vIndex = 0;
20268                 }
20269                 
20270                 if(this.vIndex > 11){
20271                     this.vIndex = 11;
20272                 }
20273                 
20274                 if(isNaN(this.vIndex)){
20275                     this.vIndex = 0;
20276                 }
20277                 
20278                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20279                 
20280                 break;
20281             case 38: // up
20282             case 40: // down
20283                 
20284                 dir = e.keyCode == 38 ? -1 : 1;
20285                 
20286                 this.vIndex = this.vIndex + dir * 4;
20287                 
20288                 if(this.vIndex < 0){
20289                     this.vIndex = 0;
20290                 }
20291                 
20292                 if(this.vIndex > 11){
20293                     this.vIndex = 11;
20294                 }
20295                 
20296                 if(isNaN(this.vIndex)){
20297                     this.vIndex = 0;
20298                 }
20299                 
20300                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20301                 break;
20302                 
20303             case 13: // enter
20304                 
20305                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20306                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20307                 }
20308                 
20309                 this.hide();
20310                 e.preventDefault();
20311                 break;
20312             case 9: // tab
20313                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20314                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20315                 }
20316                 this.hide();
20317                 break;
20318             case 16: // shift
20319             case 17: // ctrl
20320             case 18: // alt
20321                 break;
20322             default :
20323                 this.hide();
20324                 
20325         }
20326     },
20327     
20328     remove: function() 
20329     {
20330         this.picker().remove();
20331     }
20332    
20333 });
20334
20335 Roo.apply(Roo.bootstrap.MonthField,  {
20336     
20337     content : {
20338         tag: 'tbody',
20339         cn: [
20340         {
20341             tag: 'tr',
20342             cn: [
20343             {
20344                 tag: 'td',
20345                 colspan: '7'
20346             }
20347             ]
20348         }
20349         ]
20350     },
20351     
20352     dates:{
20353         en: {
20354             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20355             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20356         }
20357     }
20358 });
20359
20360 Roo.apply(Roo.bootstrap.MonthField,  {
20361   
20362     template : {
20363         tag: 'div',
20364         cls: 'datepicker dropdown-menu roo-dynamic',
20365         cn: [
20366             {
20367                 tag: 'div',
20368                 cls: 'datepicker-months',
20369                 cn: [
20370                 {
20371                     tag: 'table',
20372                     cls: 'table-condensed',
20373                     cn:[
20374                         Roo.bootstrap.DateField.content
20375                     ]
20376                 }
20377                 ]
20378             }
20379         ]
20380     }
20381 });
20382
20383  
20384
20385  
20386  /*
20387  * - LGPL
20388  *
20389  * CheckBox
20390  * 
20391  */
20392
20393 /**
20394  * @class Roo.bootstrap.CheckBox
20395  * @extends Roo.bootstrap.Input
20396  * Bootstrap CheckBox class
20397  * 
20398  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20399  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20400  * @cfg {String} boxLabel The text that appears beside the checkbox
20401  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20402  * @cfg {Boolean} checked initnal the element
20403  * @cfg {Boolean} inline inline the element (default false)
20404  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20405  * @cfg {String} tooltip label tooltip
20406  * 
20407  * @constructor
20408  * Create a new CheckBox
20409  * @param {Object} config The config object
20410  */
20411
20412 Roo.bootstrap.CheckBox = function(config){
20413     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20414    
20415     this.addEvents({
20416         /**
20417         * @event check
20418         * Fires when the element is checked or unchecked.
20419         * @param {Roo.bootstrap.CheckBox} this This input
20420         * @param {Boolean} checked The new checked value
20421         */
20422        check : true,
20423        /**
20424         * @event click
20425         * Fires when the element is click.
20426         * @param {Roo.bootstrap.CheckBox} this This input
20427         */
20428        click : true
20429     });
20430     
20431 };
20432
20433 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20434   
20435     inputType: 'checkbox',
20436     inputValue: 1,
20437     valueOff: 0,
20438     boxLabel: false,
20439     checked: false,
20440     weight : false,
20441     inline: false,
20442     tooltip : '',
20443     
20444     getAutoCreate : function()
20445     {
20446         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20447         
20448         var id = Roo.id();
20449         
20450         var cfg = {};
20451         
20452         cfg.cls = 'form-group ' + this.inputType; //input-group
20453         
20454         if(this.inline){
20455             cfg.cls += ' ' + this.inputType + '-inline';
20456         }
20457         
20458         var input =  {
20459             tag: 'input',
20460             id : id,
20461             type : this.inputType,
20462             value : this.inputValue,
20463             cls : 'roo-' + this.inputType, //'form-box',
20464             placeholder : this.placeholder || ''
20465             
20466         };
20467         
20468         if(this.inputType != 'radio'){
20469             var hidden =  {
20470                 tag: 'input',
20471                 type : 'hidden',
20472                 cls : 'roo-hidden-value',
20473                 value : this.checked ? this.inputValue : this.valueOff
20474             };
20475         }
20476         
20477             
20478         if (this.weight) { // Validity check?
20479             cfg.cls += " " + this.inputType + "-" + this.weight;
20480         }
20481         
20482         if (this.disabled) {
20483             input.disabled=true;
20484         }
20485         
20486         if(this.checked){
20487             input.checked = this.checked;
20488         }
20489         
20490         if (this.name) {
20491             
20492             input.name = this.name;
20493             
20494             if(this.inputType != 'radio'){
20495                 hidden.name = this.name;
20496                 input.name = '_hidden_' + this.name;
20497             }
20498         }
20499         
20500         if (this.size) {
20501             input.cls += ' input-' + this.size;
20502         }
20503         
20504         var settings=this;
20505         
20506         ['xs','sm','md','lg'].map(function(size){
20507             if (settings[size]) {
20508                 cfg.cls += ' col-' + size + '-' + settings[size];
20509             }
20510         });
20511         
20512         var inputblock = input;
20513          
20514         if (this.before || this.after) {
20515             
20516             inputblock = {
20517                 cls : 'input-group',
20518                 cn :  [] 
20519             };
20520             
20521             if (this.before) {
20522                 inputblock.cn.push({
20523                     tag :'span',
20524                     cls : 'input-group-addon',
20525                     html : this.before
20526                 });
20527             }
20528             
20529             inputblock.cn.push(input);
20530             
20531             if(this.inputType != 'radio'){
20532                 inputblock.cn.push(hidden);
20533             }
20534             
20535             if (this.after) {
20536                 inputblock.cn.push({
20537                     tag :'span',
20538                     cls : 'input-group-addon',
20539                     html : this.after
20540                 });
20541             }
20542             
20543         }
20544         
20545         if (align ==='left' && this.fieldLabel.length) {
20546 //                Roo.log("left and has label");
20547             cfg.cn = [
20548                 {
20549                     tag: 'label',
20550                     'for' :  id,
20551                     cls : 'control-label',
20552                     html : this.fieldLabel
20553                 },
20554                 {
20555                     cls : "", 
20556                     cn: [
20557                         inputblock
20558                     ]
20559                 }
20560             ];
20561             
20562             if(this.labelWidth > 12){
20563                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20564             }
20565             
20566             if(this.labelWidth < 13 && this.labelmd == 0){
20567                 this.labelmd = this.labelWidth;
20568             }
20569             
20570             if(this.labellg > 0){
20571                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20572                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20573             }
20574             
20575             if(this.labelmd > 0){
20576                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20577                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20578             }
20579             
20580             if(this.labelsm > 0){
20581                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20582                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20583             }
20584             
20585             if(this.labelxs > 0){
20586                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20587                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20588             }
20589             
20590         } else if ( this.fieldLabel.length) {
20591 //                Roo.log(" label");
20592                 cfg.cn = [
20593                    
20594                     {
20595                         tag: this.boxLabel ? 'span' : 'label',
20596                         'for': id,
20597                         cls: 'control-label box-input-label',
20598                         //cls : 'input-group-addon',
20599                         html : this.fieldLabel
20600                     },
20601                     
20602                     inputblock
20603                     
20604                 ];
20605
20606         } else {
20607             
20608 //                Roo.log(" no label && no align");
20609                 cfg.cn = [  inputblock ] ;
20610                 
20611                 
20612         }
20613         
20614         if(this.boxLabel){
20615              var boxLabelCfg = {
20616                 tag: 'label',
20617                 //'for': id, // box label is handled by onclick - so no for...
20618                 cls: 'box-label',
20619                 html: this.boxLabel
20620             };
20621             
20622             if(this.tooltip){
20623                 boxLabelCfg.tooltip = this.tooltip;
20624             }
20625              
20626             cfg.cn.push(boxLabelCfg);
20627         }
20628         
20629         if(this.inputType != 'radio'){
20630             cfg.cn.push(hidden);
20631         }
20632         
20633         return cfg;
20634         
20635     },
20636     
20637     /**
20638      * return the real input element.
20639      */
20640     inputEl: function ()
20641     {
20642         return this.el.select('input.roo-' + this.inputType,true).first();
20643     },
20644     hiddenEl: function ()
20645     {
20646         return this.el.select('input.roo-hidden-value',true).first();
20647     },
20648     
20649     labelEl: function()
20650     {
20651         return this.el.select('label.control-label',true).first();
20652     },
20653     /* depricated... */
20654     
20655     label: function()
20656     {
20657         return this.labelEl();
20658     },
20659     
20660     boxLabelEl: function()
20661     {
20662         return this.el.select('label.box-label',true).first();
20663     },
20664     
20665     initEvents : function()
20666     {
20667 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20668         
20669         this.inputEl().on('click', this.onClick,  this);
20670         
20671         if (this.boxLabel) { 
20672             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20673         }
20674         
20675         this.startValue = this.getValue();
20676         
20677         if(this.groupId){
20678             Roo.bootstrap.CheckBox.register(this);
20679         }
20680     },
20681     
20682     onClick : function(e)
20683     {   
20684         if(this.fireEvent('click', this, e) !== false){
20685             this.setChecked(!this.checked);
20686         }
20687         
20688     },
20689     
20690     setChecked : function(state,suppressEvent)
20691     {
20692         this.startValue = this.getValue();
20693
20694         if(this.inputType == 'radio'){
20695             
20696             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20697                 e.dom.checked = false;
20698             });
20699             
20700             this.inputEl().dom.checked = true;
20701             
20702             this.inputEl().dom.value = this.inputValue;
20703             
20704             if(suppressEvent !== true){
20705                 this.fireEvent('check', this, true);
20706             }
20707             
20708             this.validate();
20709             
20710             return;
20711         }
20712         
20713         this.checked = state;
20714         
20715         this.inputEl().dom.checked = state;
20716         
20717         
20718         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20719         
20720         if(suppressEvent !== true){
20721             this.fireEvent('check', this, state);
20722         }
20723         
20724         this.validate();
20725     },
20726     
20727     getValue : function()
20728     {
20729         if(this.inputType == 'radio'){
20730             return this.getGroupValue();
20731         }
20732         
20733         return this.hiddenEl().dom.value;
20734         
20735     },
20736     
20737     getGroupValue : function()
20738     {
20739         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20740             return '';
20741         }
20742         
20743         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20744     },
20745     
20746     setValue : function(v,suppressEvent)
20747     {
20748         if(this.inputType == 'radio'){
20749             this.setGroupValue(v, suppressEvent);
20750             return;
20751         }
20752         
20753         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20754         
20755         this.validate();
20756     },
20757     
20758     setGroupValue : function(v, suppressEvent)
20759     {
20760         this.startValue = this.getValue();
20761         
20762         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20763             e.dom.checked = false;
20764             
20765             if(e.dom.value == v){
20766                 e.dom.checked = true;
20767             }
20768         });
20769         
20770         if(suppressEvent !== true){
20771             this.fireEvent('check', this, true);
20772         }
20773
20774         this.validate();
20775         
20776         return;
20777     },
20778     
20779     validate : function()
20780     {
20781         if(this.getVisibilityEl().hasClass('hidden')){
20782             return true;
20783         }
20784         
20785         if(
20786                 this.disabled || 
20787                 (this.inputType == 'radio' && this.validateRadio()) ||
20788                 (this.inputType == 'checkbox' && this.validateCheckbox())
20789         ){
20790             this.markValid();
20791             return true;
20792         }
20793         
20794         this.markInvalid();
20795         return false;
20796     },
20797     
20798     validateRadio : function()
20799     {
20800         if(this.getVisibilityEl().hasClass('hidden')){
20801             return true;
20802         }
20803         
20804         if(this.allowBlank){
20805             return true;
20806         }
20807         
20808         var valid = false;
20809         
20810         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20811             if(!e.dom.checked){
20812                 return;
20813             }
20814             
20815             valid = true;
20816             
20817             return false;
20818         });
20819         
20820         return valid;
20821     },
20822     
20823     validateCheckbox : function()
20824     {
20825         if(!this.groupId){
20826             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20827             //return (this.getValue() == this.inputValue) ? true : false;
20828         }
20829         
20830         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20831         
20832         if(!group){
20833             return false;
20834         }
20835         
20836         var r = false;
20837         
20838         for(var i in group){
20839             if(group[i].el.isVisible(true)){
20840                 r = false;
20841                 break;
20842             }
20843             
20844             r = true;
20845         }
20846         
20847         for(var i in group){
20848             if(r){
20849                 break;
20850             }
20851             
20852             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20853         }
20854         
20855         return r;
20856     },
20857     
20858     /**
20859      * Mark this field as valid
20860      */
20861     markValid : function()
20862     {
20863         var _this = this;
20864         
20865         this.fireEvent('valid', this);
20866         
20867         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20868         
20869         if(this.groupId){
20870             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20871         }
20872         
20873         if(label){
20874             label.markValid();
20875         }
20876
20877         if(this.inputType == 'radio'){
20878             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20879                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20880                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20881             });
20882             
20883             return;
20884         }
20885
20886         if(!this.groupId){
20887             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20888             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20889             return;
20890         }
20891         
20892         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20893         
20894         if(!group){
20895             return;
20896         }
20897         
20898         for(var i in group){
20899             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20900             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20901         }
20902     },
20903     
20904      /**
20905      * Mark this field as invalid
20906      * @param {String} msg The validation message
20907      */
20908     markInvalid : function(msg)
20909     {
20910         if(this.allowBlank){
20911             return;
20912         }
20913         
20914         var _this = this;
20915         
20916         this.fireEvent('invalid', this, msg);
20917         
20918         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20919         
20920         if(this.groupId){
20921             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20922         }
20923         
20924         if(label){
20925             label.markInvalid();
20926         }
20927             
20928         if(this.inputType == 'radio'){
20929             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20930                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20931                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20932             });
20933             
20934             return;
20935         }
20936         
20937         if(!this.groupId){
20938             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20939             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20940             return;
20941         }
20942         
20943         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20944         
20945         if(!group){
20946             return;
20947         }
20948         
20949         for(var i in group){
20950             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20951             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20952         }
20953         
20954     },
20955     
20956     clearInvalid : function()
20957     {
20958         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20959         
20960         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20961         
20962         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20963         
20964         if (label && label.iconEl) {
20965             label.iconEl.removeClass(label.validClass);
20966             label.iconEl.removeClass(label.invalidClass);
20967         }
20968     },
20969     
20970     disable : function()
20971     {
20972         if(this.inputType != 'radio'){
20973             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20974             return;
20975         }
20976         
20977         var _this = this;
20978         
20979         if(this.rendered){
20980             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20981                 _this.getActionEl().addClass(this.disabledClass);
20982                 e.dom.disabled = true;
20983             });
20984         }
20985         
20986         this.disabled = true;
20987         this.fireEvent("disable", this);
20988         return this;
20989     },
20990
20991     enable : function()
20992     {
20993         if(this.inputType != 'radio'){
20994             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20995             return;
20996         }
20997         
20998         var _this = this;
20999         
21000         if(this.rendered){
21001             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21002                 _this.getActionEl().removeClass(this.disabledClass);
21003                 e.dom.disabled = false;
21004             });
21005         }
21006         
21007         this.disabled = false;
21008         this.fireEvent("enable", this);
21009         return this;
21010     },
21011     
21012     setBoxLabel : function(v)
21013     {
21014         this.boxLabel = v;
21015         
21016         if(this.rendered){
21017             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21018         }
21019     }
21020
21021 });
21022
21023 Roo.apply(Roo.bootstrap.CheckBox, {
21024     
21025     groups: {},
21026     
21027      /**
21028     * register a CheckBox Group
21029     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21030     */
21031     register : function(checkbox)
21032     {
21033         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21034             this.groups[checkbox.groupId] = {};
21035         }
21036         
21037         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21038             return;
21039         }
21040         
21041         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21042         
21043     },
21044     /**
21045     * fetch a CheckBox Group based on the group ID
21046     * @param {string} the group ID
21047     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21048     */
21049     get: function(groupId) {
21050         if (typeof(this.groups[groupId]) == 'undefined') {
21051             return false;
21052         }
21053         
21054         return this.groups[groupId] ;
21055     }
21056     
21057     
21058 });
21059 /*
21060  * - LGPL
21061  *
21062  * RadioItem
21063  * 
21064  */
21065
21066 /**
21067  * @class Roo.bootstrap.Radio
21068  * @extends Roo.bootstrap.Component
21069  * Bootstrap Radio class
21070  * @cfg {String} boxLabel - the label associated
21071  * @cfg {String} value - the value of radio
21072  * 
21073  * @constructor
21074  * Create a new Radio
21075  * @param {Object} config The config object
21076  */
21077 Roo.bootstrap.Radio = function(config){
21078     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21079     
21080 };
21081
21082 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21083     
21084     boxLabel : '',
21085     
21086     value : '',
21087     
21088     getAutoCreate : function()
21089     {
21090         var cfg = {
21091             tag : 'div',
21092             cls : 'form-group radio',
21093             cn : [
21094                 {
21095                     tag : 'label',
21096                     cls : 'box-label',
21097                     html : this.boxLabel
21098                 }
21099             ]
21100         };
21101         
21102         return cfg;
21103     },
21104     
21105     initEvents : function() 
21106     {
21107         this.parent().register(this);
21108         
21109         this.el.on('click', this.onClick, this);
21110         
21111     },
21112     
21113     onClick : function(e)
21114     {
21115         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21116             this.setChecked(true);
21117         }
21118     },
21119     
21120     setChecked : function(state, suppressEvent)
21121     {
21122         this.parent().setValue(this.value, suppressEvent);
21123         
21124     },
21125     
21126     setBoxLabel : function(v)
21127     {
21128         this.boxLabel = v;
21129         
21130         if(this.rendered){
21131             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21132         }
21133     }
21134     
21135 });
21136  
21137
21138  /*
21139  * - LGPL
21140  *
21141  * Input
21142  * 
21143  */
21144
21145 /**
21146  * @class Roo.bootstrap.SecurePass
21147  * @extends Roo.bootstrap.Input
21148  * Bootstrap SecurePass class
21149  *
21150  * 
21151  * @constructor
21152  * Create a new SecurePass
21153  * @param {Object} config The config object
21154  */
21155  
21156 Roo.bootstrap.SecurePass = function (config) {
21157     // these go here, so the translation tool can replace them..
21158     this.errors = {
21159         PwdEmpty: "Please type a password, and then retype it to confirm.",
21160         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21161         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21162         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21163         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21164         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21165         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21166         TooWeak: "Your password is Too Weak."
21167     },
21168     this.meterLabel = "Password strength:";
21169     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21170     this.meterClass = [
21171         "roo-password-meter-tooweak", 
21172         "roo-password-meter-weak", 
21173         "roo-password-meter-medium", 
21174         "roo-password-meter-strong", 
21175         "roo-password-meter-grey"
21176     ];
21177     
21178     this.errors = {};
21179     
21180     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21181 }
21182
21183 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21184     /**
21185      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21186      * {
21187      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21188      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21189      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21190      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21191      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21192      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21193      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21194      * })
21195      */
21196     // private
21197     
21198     meterWidth: 300,
21199     errorMsg :'',    
21200     errors: false,
21201     imageRoot: '/',
21202     /**
21203      * @cfg {String/Object} Label for the strength meter (defaults to
21204      * 'Password strength:')
21205      */
21206     // private
21207     meterLabel: '',
21208     /**
21209      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21210      * ['Weak', 'Medium', 'Strong'])
21211      */
21212     // private    
21213     pwdStrengths: false,    
21214     // private
21215     strength: 0,
21216     // private
21217     _lastPwd: null,
21218     // private
21219     kCapitalLetter: 0,
21220     kSmallLetter: 1,
21221     kDigit: 2,
21222     kPunctuation: 3,
21223     
21224     insecure: false,
21225     // private
21226     initEvents: function ()
21227     {
21228         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21229
21230         if (this.el.is('input[type=password]') && Roo.isSafari) {
21231             this.el.on('keydown', this.SafariOnKeyDown, this);
21232         }
21233
21234         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21235     },
21236     // private
21237     onRender: function (ct, position)
21238     {
21239         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21240         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21241         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21242
21243         this.trigger.createChild({
21244                    cn: [
21245                     {
21246                     //id: 'PwdMeter',
21247                     tag: 'div',
21248                     cls: 'roo-password-meter-grey col-xs-12',
21249                     style: {
21250                         //width: 0,
21251                         //width: this.meterWidth + 'px'                                                
21252                         }
21253                     },
21254                     {                            
21255                          cls: 'roo-password-meter-text'                          
21256                     }
21257                 ]            
21258         });
21259
21260          
21261         if (this.hideTrigger) {
21262             this.trigger.setDisplayed(false);
21263         }
21264         this.setSize(this.width || '', this.height || '');
21265     },
21266     // private
21267     onDestroy: function ()
21268     {
21269         if (this.trigger) {
21270             this.trigger.removeAllListeners();
21271             this.trigger.remove();
21272         }
21273         if (this.wrap) {
21274             this.wrap.remove();
21275         }
21276         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21277     },
21278     // private
21279     checkStrength: function ()
21280     {
21281         var pwd = this.inputEl().getValue();
21282         if (pwd == this._lastPwd) {
21283             return;
21284         }
21285
21286         var strength;
21287         if (this.ClientSideStrongPassword(pwd)) {
21288             strength = 3;
21289         } else if (this.ClientSideMediumPassword(pwd)) {
21290             strength = 2;
21291         } else if (this.ClientSideWeakPassword(pwd)) {
21292             strength = 1;
21293         } else {
21294             strength = 0;
21295         }
21296         
21297         Roo.log('strength1: ' + strength);
21298         
21299         //var pm = this.trigger.child('div/div/div').dom;
21300         var pm = this.trigger.child('div/div');
21301         pm.removeClass(this.meterClass);
21302         pm.addClass(this.meterClass[strength]);
21303                 
21304         
21305         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21306                 
21307         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21308         
21309         this._lastPwd = pwd;
21310     },
21311     reset: function ()
21312     {
21313         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21314         
21315         this._lastPwd = '';
21316         
21317         var pm = this.trigger.child('div/div');
21318         pm.removeClass(this.meterClass);
21319         pm.addClass('roo-password-meter-grey');        
21320         
21321         
21322         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21323         
21324         pt.innerHTML = '';
21325         this.inputEl().dom.type='password';
21326     },
21327     // private
21328     validateValue: function (value)
21329     {
21330         
21331         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21332             return false;
21333         }
21334         if (value.length == 0) {
21335             if (this.allowBlank) {
21336                 this.clearInvalid();
21337                 return true;
21338             }
21339
21340             this.markInvalid(this.errors.PwdEmpty);
21341             this.errorMsg = this.errors.PwdEmpty;
21342             return false;
21343         }
21344         
21345         if(this.insecure){
21346             return true;
21347         }
21348         
21349         if ('[\x21-\x7e]*'.match(value)) {
21350             this.markInvalid(this.errors.PwdBadChar);
21351             this.errorMsg = this.errors.PwdBadChar;
21352             return false;
21353         }
21354         if (value.length < 6) {
21355             this.markInvalid(this.errors.PwdShort);
21356             this.errorMsg = this.errors.PwdShort;
21357             return false;
21358         }
21359         if (value.length > 16) {
21360             this.markInvalid(this.errors.PwdLong);
21361             this.errorMsg = this.errors.PwdLong;
21362             return false;
21363         }
21364         var strength;
21365         if (this.ClientSideStrongPassword(value)) {
21366             strength = 3;
21367         } else if (this.ClientSideMediumPassword(value)) {
21368             strength = 2;
21369         } else if (this.ClientSideWeakPassword(value)) {
21370             strength = 1;
21371         } else {
21372             strength = 0;
21373         }
21374
21375         
21376         if (strength < 2) {
21377             //this.markInvalid(this.errors.TooWeak);
21378             this.errorMsg = this.errors.TooWeak;
21379             //return false;
21380         }
21381         
21382         
21383         console.log('strength2: ' + strength);
21384         
21385         //var pm = this.trigger.child('div/div/div').dom;
21386         
21387         var pm = this.trigger.child('div/div');
21388         pm.removeClass(this.meterClass);
21389         pm.addClass(this.meterClass[strength]);
21390                 
21391         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21392                 
21393         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21394         
21395         this.errorMsg = ''; 
21396         return true;
21397     },
21398     // private
21399     CharacterSetChecks: function (type)
21400     {
21401         this.type = type;
21402         this.fResult = false;
21403     },
21404     // private
21405     isctype: function (character, type)
21406     {
21407         switch (type) {  
21408             case this.kCapitalLetter:
21409                 if (character >= 'A' && character <= 'Z') {
21410                     return true;
21411                 }
21412                 break;
21413             
21414             case this.kSmallLetter:
21415                 if (character >= 'a' && character <= 'z') {
21416                     return true;
21417                 }
21418                 break;
21419             
21420             case this.kDigit:
21421                 if (character >= '0' && character <= '9') {
21422                     return true;
21423                 }
21424                 break;
21425             
21426             case this.kPunctuation:
21427                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21428                     return true;
21429                 }
21430                 break;
21431             
21432             default:
21433                 return false;
21434         }
21435
21436     },
21437     // private
21438     IsLongEnough: function (pwd, size)
21439     {
21440         return !(pwd == null || isNaN(size) || pwd.length < size);
21441     },
21442     // private
21443     SpansEnoughCharacterSets: function (word, nb)
21444     {
21445         if (!this.IsLongEnough(word, nb))
21446         {
21447             return false;
21448         }
21449
21450         var characterSetChecks = new Array(
21451             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21452             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21453         );
21454         
21455         for (var index = 0; index < word.length; ++index) {
21456             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21457                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21458                     characterSetChecks[nCharSet].fResult = true;
21459                     break;
21460                 }
21461             }
21462         }
21463
21464         var nCharSets = 0;
21465         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21466             if (characterSetChecks[nCharSet].fResult) {
21467                 ++nCharSets;
21468             }
21469         }
21470
21471         if (nCharSets < nb) {
21472             return false;
21473         }
21474         return true;
21475     },
21476     // private
21477     ClientSideStrongPassword: function (pwd)
21478     {
21479         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21480     },
21481     // private
21482     ClientSideMediumPassword: function (pwd)
21483     {
21484         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21485     },
21486     // private
21487     ClientSideWeakPassword: function (pwd)
21488     {
21489         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21490     }
21491           
21492 })//<script type="text/javascript">
21493
21494 /*
21495  * Based  Ext JS Library 1.1.1
21496  * Copyright(c) 2006-2007, Ext JS, LLC.
21497  * LGPL
21498  *
21499  */
21500  
21501 /**
21502  * @class Roo.HtmlEditorCore
21503  * @extends Roo.Component
21504  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21505  *
21506  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21507  */
21508
21509 Roo.HtmlEditorCore = function(config){
21510     
21511     
21512     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21513     
21514     
21515     this.addEvents({
21516         /**
21517          * @event initialize
21518          * Fires when the editor is fully initialized (including the iframe)
21519          * @param {Roo.HtmlEditorCore} this
21520          */
21521         initialize: true,
21522         /**
21523          * @event activate
21524          * Fires when the editor is first receives the focus. Any insertion must wait
21525          * until after this event.
21526          * @param {Roo.HtmlEditorCore} this
21527          */
21528         activate: true,
21529          /**
21530          * @event beforesync
21531          * Fires before the textarea is updated with content from the editor iframe. Return false
21532          * to cancel the sync.
21533          * @param {Roo.HtmlEditorCore} this
21534          * @param {String} html
21535          */
21536         beforesync: true,
21537          /**
21538          * @event beforepush
21539          * Fires before the iframe editor is updated with content from the textarea. Return false
21540          * to cancel the push.
21541          * @param {Roo.HtmlEditorCore} this
21542          * @param {String} html
21543          */
21544         beforepush: true,
21545          /**
21546          * @event sync
21547          * Fires when the textarea is updated with content from the editor iframe.
21548          * @param {Roo.HtmlEditorCore} this
21549          * @param {String} html
21550          */
21551         sync: true,
21552          /**
21553          * @event push
21554          * Fires when the iframe editor is updated with content from the textarea.
21555          * @param {Roo.HtmlEditorCore} this
21556          * @param {String} html
21557          */
21558         push: true,
21559         
21560         /**
21561          * @event editorevent
21562          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21563          * @param {Roo.HtmlEditorCore} this
21564          */
21565         editorevent: true
21566         
21567     });
21568     
21569     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21570     
21571     // defaults : white / black...
21572     this.applyBlacklists();
21573     
21574     
21575     
21576 };
21577
21578
21579 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21580
21581
21582      /**
21583      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21584      */
21585     
21586     owner : false,
21587     
21588      /**
21589      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21590      *                        Roo.resizable.
21591      */
21592     resizable : false,
21593      /**
21594      * @cfg {Number} height (in pixels)
21595      */   
21596     height: 300,
21597    /**
21598      * @cfg {Number} width (in pixels)
21599      */   
21600     width: 500,
21601     
21602     /**
21603      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21604      * 
21605      */
21606     stylesheets: false,
21607     
21608     // id of frame..
21609     frameId: false,
21610     
21611     // private properties
21612     validationEvent : false,
21613     deferHeight: true,
21614     initialized : false,
21615     activated : false,
21616     sourceEditMode : false,
21617     onFocus : Roo.emptyFn,
21618     iframePad:3,
21619     hideMode:'offsets',
21620     
21621     clearUp: true,
21622     
21623     // blacklist + whitelisted elements..
21624     black: false,
21625     white: false,
21626      
21627     bodyCls : '',
21628
21629     /**
21630      * Protected method that will not generally be called directly. It
21631      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21632      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21633      */
21634     getDocMarkup : function(){
21635         // body styles..
21636         var st = '';
21637         
21638         // inherit styels from page...?? 
21639         if (this.stylesheets === false) {
21640             
21641             Roo.get(document.head).select('style').each(function(node) {
21642                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21643             });
21644             
21645             Roo.get(document.head).select('link').each(function(node) { 
21646                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21647             });
21648             
21649         } else if (!this.stylesheets.length) {
21650                 // simple..
21651                 st = '<style type="text/css">' +
21652                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21653                    '</style>';
21654         } else { 
21655             st = '<style type="text/css">' +
21656                     this.stylesheets +
21657                 '</style>';
21658         }
21659         
21660         st +=  '<style type="text/css">' +
21661             'IMG { cursor: pointer } ' +
21662         '</style>';
21663
21664         var cls = 'roo-htmleditor-body';
21665         
21666         if(this.bodyCls.length){
21667             cls += ' ' + this.bodyCls;
21668         }
21669         
21670         return '<html><head>' + st  +
21671             //<style type="text/css">' +
21672             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21673             //'</style>' +
21674             ' </head><body class="' +  cls + '"></body></html>';
21675     },
21676
21677     // private
21678     onRender : function(ct, position)
21679     {
21680         var _t = this;
21681         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21682         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21683         
21684         
21685         this.el.dom.style.border = '0 none';
21686         this.el.dom.setAttribute('tabIndex', -1);
21687         this.el.addClass('x-hidden hide');
21688         
21689         
21690         
21691         if(Roo.isIE){ // fix IE 1px bogus margin
21692             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21693         }
21694        
21695         
21696         this.frameId = Roo.id();
21697         
21698          
21699         
21700         var iframe = this.owner.wrap.createChild({
21701             tag: 'iframe',
21702             cls: 'form-control', // bootstrap..
21703             id: this.frameId,
21704             name: this.frameId,
21705             frameBorder : 'no',
21706             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21707         }, this.el
21708         );
21709         
21710         
21711         this.iframe = iframe.dom;
21712
21713          this.assignDocWin();
21714         
21715         this.doc.designMode = 'on';
21716        
21717         this.doc.open();
21718         this.doc.write(this.getDocMarkup());
21719         this.doc.close();
21720
21721         
21722         var task = { // must defer to wait for browser to be ready
21723             run : function(){
21724                 //console.log("run task?" + this.doc.readyState);
21725                 this.assignDocWin();
21726                 if(this.doc.body || this.doc.readyState == 'complete'){
21727                     try {
21728                         this.doc.designMode="on";
21729                     } catch (e) {
21730                         return;
21731                     }
21732                     Roo.TaskMgr.stop(task);
21733                     this.initEditor.defer(10, this);
21734                 }
21735             },
21736             interval : 10,
21737             duration: 10000,
21738             scope: this
21739         };
21740         Roo.TaskMgr.start(task);
21741
21742     },
21743
21744     // private
21745     onResize : function(w, h)
21746     {
21747          Roo.log('resize: ' +w + ',' + h );
21748         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21749         if(!this.iframe){
21750             return;
21751         }
21752         if(typeof w == 'number'){
21753             
21754             this.iframe.style.width = w + 'px';
21755         }
21756         if(typeof h == 'number'){
21757             
21758             this.iframe.style.height = h + 'px';
21759             if(this.doc){
21760                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21761             }
21762         }
21763         
21764     },
21765
21766     /**
21767      * Toggles the editor between standard and source edit mode.
21768      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21769      */
21770     toggleSourceEdit : function(sourceEditMode){
21771         
21772         this.sourceEditMode = sourceEditMode === true;
21773         
21774         if(this.sourceEditMode){
21775  
21776             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21777             
21778         }else{
21779             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21780             //this.iframe.className = '';
21781             this.deferFocus();
21782         }
21783         //this.setSize(this.owner.wrap.getSize());
21784         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21785     },
21786
21787     
21788   
21789
21790     /**
21791      * Protected method that will not generally be called directly. If you need/want
21792      * custom HTML cleanup, this is the method you should override.
21793      * @param {String} html The HTML to be cleaned
21794      * return {String} The cleaned HTML
21795      */
21796     cleanHtml : function(html){
21797         html = String(html);
21798         if(html.length > 5){
21799             if(Roo.isSafari){ // strip safari nonsense
21800                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21801             }
21802         }
21803         if(html == '&nbsp;'){
21804             html = '';
21805         }
21806         return html;
21807     },
21808
21809     /**
21810      * HTML Editor -> Textarea
21811      * Protected method that will not generally be called directly. Syncs the contents
21812      * of the editor iframe with the textarea.
21813      */
21814     syncValue : function(){
21815         if(this.initialized){
21816             var bd = (this.doc.body || this.doc.documentElement);
21817             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21818             var html = bd.innerHTML;
21819             if(Roo.isSafari){
21820                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21821                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21822                 if(m && m[1]){
21823                     html = '<div style="'+m[0]+'">' + html + '</div>';
21824                 }
21825             }
21826             html = this.cleanHtml(html);
21827             // fix up the special chars.. normaly like back quotes in word...
21828             // however we do not want to do this with chinese..
21829             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21830                 var cc = b.charCodeAt();
21831                 if (
21832                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21833                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21834                     (cc >= 0xf900 && cc < 0xfb00 )
21835                 ) {
21836                         return b;
21837                 }
21838                 return "&#"+cc+";" 
21839             });
21840             if(this.owner.fireEvent('beforesync', this, html) !== false){
21841                 this.el.dom.value = html;
21842                 this.owner.fireEvent('sync', this, html);
21843             }
21844         }
21845     },
21846
21847     /**
21848      * Protected method that will not generally be called directly. Pushes the value of the textarea
21849      * into the iframe editor.
21850      */
21851     pushValue : function(){
21852         if(this.initialized){
21853             var v = this.el.dom.value.trim();
21854             
21855 //            if(v.length < 1){
21856 //                v = '&#160;';
21857 //            }
21858             
21859             if(this.owner.fireEvent('beforepush', this, v) !== false){
21860                 var d = (this.doc.body || this.doc.documentElement);
21861                 d.innerHTML = v;
21862                 this.cleanUpPaste();
21863                 this.el.dom.value = d.innerHTML;
21864                 this.owner.fireEvent('push', this, v);
21865             }
21866         }
21867     },
21868
21869     // private
21870     deferFocus : function(){
21871         this.focus.defer(10, this);
21872     },
21873
21874     // doc'ed in Field
21875     focus : function(){
21876         if(this.win && !this.sourceEditMode){
21877             this.win.focus();
21878         }else{
21879             this.el.focus();
21880         }
21881     },
21882     
21883     assignDocWin: function()
21884     {
21885         var iframe = this.iframe;
21886         
21887          if(Roo.isIE){
21888             this.doc = iframe.contentWindow.document;
21889             this.win = iframe.contentWindow;
21890         } else {
21891 //            if (!Roo.get(this.frameId)) {
21892 //                return;
21893 //            }
21894 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21895 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21896             
21897             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21898                 return;
21899             }
21900             
21901             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21902             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21903         }
21904     },
21905     
21906     // private
21907     initEditor : function(){
21908         //console.log("INIT EDITOR");
21909         this.assignDocWin();
21910         
21911         
21912         
21913         this.doc.designMode="on";
21914         this.doc.open();
21915         this.doc.write(this.getDocMarkup());
21916         this.doc.close();
21917         
21918         var dbody = (this.doc.body || this.doc.documentElement);
21919         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21920         // this copies styles from the containing element into thsi one..
21921         // not sure why we need all of this..
21922         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21923         
21924         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21925         //ss['background-attachment'] = 'fixed'; // w3c
21926         dbody.bgProperties = 'fixed'; // ie
21927         //Roo.DomHelper.applyStyles(dbody, ss);
21928         Roo.EventManager.on(this.doc, {
21929             //'mousedown': this.onEditorEvent,
21930             'mouseup': this.onEditorEvent,
21931             'dblclick': this.onEditorEvent,
21932             'click': this.onEditorEvent,
21933             'keyup': this.onEditorEvent,
21934             buffer:100,
21935             scope: this
21936         });
21937         if(Roo.isGecko){
21938             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21939         }
21940         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21941             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21942         }
21943         this.initialized = true;
21944
21945         this.owner.fireEvent('initialize', this);
21946         this.pushValue();
21947     },
21948
21949     // private
21950     onDestroy : function(){
21951         
21952         
21953         
21954         if(this.rendered){
21955             
21956             //for (var i =0; i < this.toolbars.length;i++) {
21957             //    // fixme - ask toolbars for heights?
21958             //    this.toolbars[i].onDestroy();
21959            // }
21960             
21961             //this.wrap.dom.innerHTML = '';
21962             //this.wrap.remove();
21963         }
21964     },
21965
21966     // private
21967     onFirstFocus : function(){
21968         
21969         this.assignDocWin();
21970         
21971         
21972         this.activated = true;
21973          
21974     
21975         if(Roo.isGecko){ // prevent silly gecko errors
21976             this.win.focus();
21977             var s = this.win.getSelection();
21978             if(!s.focusNode || s.focusNode.nodeType != 3){
21979                 var r = s.getRangeAt(0);
21980                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21981                 r.collapse(true);
21982                 this.deferFocus();
21983             }
21984             try{
21985                 this.execCmd('useCSS', true);
21986                 this.execCmd('styleWithCSS', false);
21987             }catch(e){}
21988         }
21989         this.owner.fireEvent('activate', this);
21990     },
21991
21992     // private
21993     adjustFont: function(btn){
21994         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21995         //if(Roo.isSafari){ // safari
21996         //    adjust *= 2;
21997        // }
21998         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21999         if(Roo.isSafari){ // safari
22000             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22001             v =  (v < 10) ? 10 : v;
22002             v =  (v > 48) ? 48 : v;
22003             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22004             
22005         }
22006         
22007         
22008         v = Math.max(1, v+adjust);
22009         
22010         this.execCmd('FontSize', v  );
22011     },
22012
22013     onEditorEvent : function(e)
22014     {
22015         this.owner.fireEvent('editorevent', this, e);
22016       //  this.updateToolbar();
22017         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22018     },
22019
22020     insertTag : function(tg)
22021     {
22022         // could be a bit smarter... -> wrap the current selected tRoo..
22023         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22024             
22025             range = this.createRange(this.getSelection());
22026             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22027             wrappingNode.appendChild(range.extractContents());
22028             range.insertNode(wrappingNode);
22029
22030             return;
22031             
22032             
22033             
22034         }
22035         this.execCmd("formatblock",   tg);
22036         
22037     },
22038     
22039     insertText : function(txt)
22040     {
22041         
22042         
22043         var range = this.createRange();
22044         range.deleteContents();
22045                //alert(Sender.getAttribute('label'));
22046                
22047         range.insertNode(this.doc.createTextNode(txt));
22048     } ,
22049     
22050      
22051
22052     /**
22053      * Executes a Midas editor command on the editor document and performs necessary focus and
22054      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22055      * @param {String} cmd The Midas command
22056      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22057      */
22058     relayCmd : function(cmd, value){
22059         this.win.focus();
22060         this.execCmd(cmd, value);
22061         this.owner.fireEvent('editorevent', this);
22062         //this.updateToolbar();
22063         this.owner.deferFocus();
22064     },
22065
22066     /**
22067      * Executes a Midas editor command directly on the editor document.
22068      * For visual commands, you should use {@link #relayCmd} instead.
22069      * <b>This should only be called after the editor is initialized.</b>
22070      * @param {String} cmd The Midas command
22071      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22072      */
22073     execCmd : function(cmd, value){
22074         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22075         this.syncValue();
22076     },
22077  
22078  
22079    
22080     /**
22081      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22082      * to insert tRoo.
22083      * @param {String} text | dom node.. 
22084      */
22085     insertAtCursor : function(text)
22086     {
22087         
22088         if(!this.activated){
22089             return;
22090         }
22091         /*
22092         if(Roo.isIE){
22093             this.win.focus();
22094             var r = this.doc.selection.createRange();
22095             if(r){
22096                 r.collapse(true);
22097                 r.pasteHTML(text);
22098                 this.syncValue();
22099                 this.deferFocus();
22100             
22101             }
22102             return;
22103         }
22104         */
22105         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22106             this.win.focus();
22107             
22108             
22109             // from jquery ui (MIT licenced)
22110             var range, node;
22111             var win = this.win;
22112             
22113             if (win.getSelection && win.getSelection().getRangeAt) {
22114                 range = win.getSelection().getRangeAt(0);
22115                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22116                 range.insertNode(node);
22117             } else if (win.document.selection && win.document.selection.createRange) {
22118                 // no firefox support
22119                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22120                 win.document.selection.createRange().pasteHTML(txt);
22121             } else {
22122                 // no firefox support
22123                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22124                 this.execCmd('InsertHTML', txt);
22125             } 
22126             
22127             this.syncValue();
22128             
22129             this.deferFocus();
22130         }
22131     },
22132  // private
22133     mozKeyPress : function(e){
22134         if(e.ctrlKey){
22135             var c = e.getCharCode(), cmd;
22136           
22137             if(c > 0){
22138                 c = String.fromCharCode(c).toLowerCase();
22139                 switch(c){
22140                     case 'b':
22141                         cmd = 'bold';
22142                         break;
22143                     case 'i':
22144                         cmd = 'italic';
22145                         break;
22146                     
22147                     case 'u':
22148                         cmd = 'underline';
22149                         break;
22150                     
22151                     case 'v':
22152                         this.cleanUpPaste.defer(100, this);
22153                         return;
22154                         
22155                 }
22156                 if(cmd){
22157                     this.win.focus();
22158                     this.execCmd(cmd);
22159                     this.deferFocus();
22160                     e.preventDefault();
22161                 }
22162                 
22163             }
22164         }
22165     },
22166
22167     // private
22168     fixKeys : function(){ // load time branching for fastest keydown performance
22169         if(Roo.isIE){
22170             return function(e){
22171                 var k = e.getKey(), r;
22172                 if(k == e.TAB){
22173                     e.stopEvent();
22174                     r = this.doc.selection.createRange();
22175                     if(r){
22176                         r.collapse(true);
22177                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22178                         this.deferFocus();
22179                     }
22180                     return;
22181                 }
22182                 
22183                 if(k == e.ENTER){
22184                     r = this.doc.selection.createRange();
22185                     if(r){
22186                         var target = r.parentElement();
22187                         if(!target || target.tagName.toLowerCase() != 'li'){
22188                             e.stopEvent();
22189                             r.pasteHTML('<br />');
22190                             r.collapse(false);
22191                             r.select();
22192                         }
22193                     }
22194                 }
22195                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22196                     this.cleanUpPaste.defer(100, this);
22197                     return;
22198                 }
22199                 
22200                 
22201             };
22202         }else if(Roo.isOpera){
22203             return function(e){
22204                 var k = e.getKey();
22205                 if(k == e.TAB){
22206                     e.stopEvent();
22207                     this.win.focus();
22208                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22209                     this.deferFocus();
22210                 }
22211                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22212                     this.cleanUpPaste.defer(100, this);
22213                     return;
22214                 }
22215                 
22216             };
22217         }else if(Roo.isSafari){
22218             return function(e){
22219                 var k = e.getKey();
22220                 
22221                 if(k == e.TAB){
22222                     e.stopEvent();
22223                     this.execCmd('InsertText','\t');
22224                     this.deferFocus();
22225                     return;
22226                 }
22227                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22228                     this.cleanUpPaste.defer(100, this);
22229                     return;
22230                 }
22231                 
22232              };
22233         }
22234     }(),
22235     
22236     getAllAncestors: function()
22237     {
22238         var p = this.getSelectedNode();
22239         var a = [];
22240         if (!p) {
22241             a.push(p); // push blank onto stack..
22242             p = this.getParentElement();
22243         }
22244         
22245         
22246         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22247             a.push(p);
22248             p = p.parentNode;
22249         }
22250         a.push(this.doc.body);
22251         return a;
22252     },
22253     lastSel : false,
22254     lastSelNode : false,
22255     
22256     
22257     getSelection : function() 
22258     {
22259         this.assignDocWin();
22260         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22261     },
22262     
22263     getSelectedNode: function() 
22264     {
22265         // this may only work on Gecko!!!
22266         
22267         // should we cache this!!!!
22268         
22269         
22270         
22271          
22272         var range = this.createRange(this.getSelection()).cloneRange();
22273         
22274         if (Roo.isIE) {
22275             var parent = range.parentElement();
22276             while (true) {
22277                 var testRange = range.duplicate();
22278                 testRange.moveToElementText(parent);
22279                 if (testRange.inRange(range)) {
22280                     break;
22281                 }
22282                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22283                     break;
22284                 }
22285                 parent = parent.parentElement;
22286             }
22287             return parent;
22288         }
22289         
22290         // is ancestor a text element.
22291         var ac =  range.commonAncestorContainer;
22292         if (ac.nodeType == 3) {
22293             ac = ac.parentNode;
22294         }
22295         
22296         var ar = ac.childNodes;
22297          
22298         var nodes = [];
22299         var other_nodes = [];
22300         var has_other_nodes = false;
22301         for (var i=0;i<ar.length;i++) {
22302             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22303                 continue;
22304             }
22305             // fullly contained node.
22306             
22307             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22308                 nodes.push(ar[i]);
22309                 continue;
22310             }
22311             
22312             // probably selected..
22313             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22314                 other_nodes.push(ar[i]);
22315                 continue;
22316             }
22317             // outer..
22318             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22319                 continue;
22320             }
22321             
22322             
22323             has_other_nodes = true;
22324         }
22325         if (!nodes.length && other_nodes.length) {
22326             nodes= other_nodes;
22327         }
22328         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22329             return false;
22330         }
22331         
22332         return nodes[0];
22333     },
22334     createRange: function(sel)
22335     {
22336         // this has strange effects when using with 
22337         // top toolbar - not sure if it's a great idea.
22338         //this.editor.contentWindow.focus();
22339         if (typeof sel != "undefined") {
22340             try {
22341                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22342             } catch(e) {
22343                 return this.doc.createRange();
22344             }
22345         } else {
22346             return this.doc.createRange();
22347         }
22348     },
22349     getParentElement: function()
22350     {
22351         
22352         this.assignDocWin();
22353         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22354         
22355         var range = this.createRange(sel);
22356          
22357         try {
22358             var p = range.commonAncestorContainer;
22359             while (p.nodeType == 3) { // text node
22360                 p = p.parentNode;
22361             }
22362             return p;
22363         } catch (e) {
22364             return null;
22365         }
22366     
22367     },
22368     /***
22369      *
22370      * Range intersection.. the hard stuff...
22371      *  '-1' = before
22372      *  '0' = hits..
22373      *  '1' = after.
22374      *         [ -- selected range --- ]
22375      *   [fail]                        [fail]
22376      *
22377      *    basically..
22378      *      if end is before start or  hits it. fail.
22379      *      if start is after end or hits it fail.
22380      *
22381      *   if either hits (but other is outside. - then it's not 
22382      *   
22383      *    
22384      **/
22385     
22386     
22387     // @see http://www.thismuchiknow.co.uk/?p=64.
22388     rangeIntersectsNode : function(range, node)
22389     {
22390         var nodeRange = node.ownerDocument.createRange();
22391         try {
22392             nodeRange.selectNode(node);
22393         } catch (e) {
22394             nodeRange.selectNodeContents(node);
22395         }
22396     
22397         var rangeStartRange = range.cloneRange();
22398         rangeStartRange.collapse(true);
22399     
22400         var rangeEndRange = range.cloneRange();
22401         rangeEndRange.collapse(false);
22402     
22403         var nodeStartRange = nodeRange.cloneRange();
22404         nodeStartRange.collapse(true);
22405     
22406         var nodeEndRange = nodeRange.cloneRange();
22407         nodeEndRange.collapse(false);
22408     
22409         return rangeStartRange.compareBoundaryPoints(
22410                  Range.START_TO_START, nodeEndRange) == -1 &&
22411                rangeEndRange.compareBoundaryPoints(
22412                  Range.START_TO_START, nodeStartRange) == 1;
22413         
22414          
22415     },
22416     rangeCompareNode : function(range, node)
22417     {
22418         var nodeRange = node.ownerDocument.createRange();
22419         try {
22420             nodeRange.selectNode(node);
22421         } catch (e) {
22422             nodeRange.selectNodeContents(node);
22423         }
22424         
22425         
22426         range.collapse(true);
22427     
22428         nodeRange.collapse(true);
22429      
22430         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22431         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22432          
22433         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22434         
22435         var nodeIsBefore   =  ss == 1;
22436         var nodeIsAfter    = ee == -1;
22437         
22438         if (nodeIsBefore && nodeIsAfter) {
22439             return 0; // outer
22440         }
22441         if (!nodeIsBefore && nodeIsAfter) {
22442             return 1; //right trailed.
22443         }
22444         
22445         if (nodeIsBefore && !nodeIsAfter) {
22446             return 2;  // left trailed.
22447         }
22448         // fully contined.
22449         return 3;
22450     },
22451
22452     // private? - in a new class?
22453     cleanUpPaste :  function()
22454     {
22455         // cleans up the whole document..
22456         Roo.log('cleanuppaste');
22457         
22458         this.cleanUpChildren(this.doc.body);
22459         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22460         if (clean != this.doc.body.innerHTML) {
22461             this.doc.body.innerHTML = clean;
22462         }
22463         
22464     },
22465     
22466     cleanWordChars : function(input) {// change the chars to hex code
22467         var he = Roo.HtmlEditorCore;
22468         
22469         var output = input;
22470         Roo.each(he.swapCodes, function(sw) { 
22471             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22472             
22473             output = output.replace(swapper, sw[1]);
22474         });
22475         
22476         return output;
22477     },
22478     
22479     
22480     cleanUpChildren : function (n)
22481     {
22482         if (!n.childNodes.length) {
22483             return;
22484         }
22485         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22486            this.cleanUpChild(n.childNodes[i]);
22487         }
22488     },
22489     
22490     
22491         
22492     
22493     cleanUpChild : function (node)
22494     {
22495         var ed = this;
22496         //console.log(node);
22497         if (node.nodeName == "#text") {
22498             // clean up silly Windows -- stuff?
22499             return; 
22500         }
22501         if (node.nodeName == "#comment") {
22502             node.parentNode.removeChild(node);
22503             // clean up silly Windows -- stuff?
22504             return; 
22505         }
22506         var lcname = node.tagName.toLowerCase();
22507         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22508         // whitelist of tags..
22509         
22510         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22511             // remove node.
22512             node.parentNode.removeChild(node);
22513             return;
22514             
22515         }
22516         
22517         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22518         
22519         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22520         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22521         
22522         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22523         //    remove_keep_children = true;
22524         //}
22525         
22526         if (remove_keep_children) {
22527             this.cleanUpChildren(node);
22528             // inserts everything just before this node...
22529             while (node.childNodes.length) {
22530                 var cn = node.childNodes[0];
22531                 node.removeChild(cn);
22532                 node.parentNode.insertBefore(cn, node);
22533             }
22534             node.parentNode.removeChild(node);
22535             return;
22536         }
22537         
22538         if (!node.attributes || !node.attributes.length) {
22539             this.cleanUpChildren(node);
22540             return;
22541         }
22542         
22543         function cleanAttr(n,v)
22544         {
22545             
22546             if (v.match(/^\./) || v.match(/^\//)) {
22547                 return;
22548             }
22549             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22550                 return;
22551             }
22552             if (v.match(/^#/)) {
22553                 return;
22554             }
22555 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22556             node.removeAttribute(n);
22557             
22558         }
22559         
22560         var cwhite = this.cwhite;
22561         var cblack = this.cblack;
22562             
22563         function cleanStyle(n,v)
22564         {
22565             if (v.match(/expression/)) { //XSS?? should we even bother..
22566                 node.removeAttribute(n);
22567                 return;
22568             }
22569             
22570             var parts = v.split(/;/);
22571             var clean = [];
22572             
22573             Roo.each(parts, function(p) {
22574                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22575                 if (!p.length) {
22576                     return true;
22577                 }
22578                 var l = p.split(':').shift().replace(/\s+/g,'');
22579                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22580                 
22581                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22582 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22583                     //node.removeAttribute(n);
22584                     return true;
22585                 }
22586                 //Roo.log()
22587                 // only allow 'c whitelisted system attributes'
22588                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22589 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22590                     //node.removeAttribute(n);
22591                     return true;
22592                 }
22593                 
22594                 
22595                  
22596                 
22597                 clean.push(p);
22598                 return true;
22599             });
22600             if (clean.length) { 
22601                 node.setAttribute(n, clean.join(';'));
22602             } else {
22603                 node.removeAttribute(n);
22604             }
22605             
22606         }
22607         
22608         
22609         for (var i = node.attributes.length-1; i > -1 ; i--) {
22610             var a = node.attributes[i];
22611             //console.log(a);
22612             
22613             if (a.name.toLowerCase().substr(0,2)=='on')  {
22614                 node.removeAttribute(a.name);
22615                 continue;
22616             }
22617             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22618                 node.removeAttribute(a.name);
22619                 continue;
22620             }
22621             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22622                 cleanAttr(a.name,a.value); // fixme..
22623                 continue;
22624             }
22625             if (a.name == 'style') {
22626                 cleanStyle(a.name,a.value);
22627                 continue;
22628             }
22629             /// clean up MS crap..
22630             // tecnically this should be a list of valid class'es..
22631             
22632             
22633             if (a.name == 'class') {
22634                 if (a.value.match(/^Mso/)) {
22635                     node.className = '';
22636                 }
22637                 
22638                 if (a.value.match(/^body$/)) {
22639                     node.className = '';
22640                 }
22641                 continue;
22642             }
22643             
22644             // style cleanup!?
22645             // class cleanup?
22646             
22647         }
22648         
22649         
22650         this.cleanUpChildren(node);
22651         
22652         
22653     },
22654     
22655     /**
22656      * Clean up MS wordisms...
22657      */
22658     cleanWord : function(node)
22659     {
22660         
22661         
22662         if (!node) {
22663             this.cleanWord(this.doc.body);
22664             return;
22665         }
22666         if (node.nodeName == "#text") {
22667             // clean up silly Windows -- stuff?
22668             return; 
22669         }
22670         if (node.nodeName == "#comment") {
22671             node.parentNode.removeChild(node);
22672             // clean up silly Windows -- stuff?
22673             return; 
22674         }
22675         
22676         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22677             node.parentNode.removeChild(node);
22678             return;
22679         }
22680         
22681         // remove - but keep children..
22682         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22683             while (node.childNodes.length) {
22684                 var cn = node.childNodes[0];
22685                 node.removeChild(cn);
22686                 node.parentNode.insertBefore(cn, node);
22687             }
22688             node.parentNode.removeChild(node);
22689             this.iterateChildren(node, this.cleanWord);
22690             return;
22691         }
22692         // clean styles
22693         if (node.className.length) {
22694             
22695             var cn = node.className.split(/\W+/);
22696             var cna = [];
22697             Roo.each(cn, function(cls) {
22698                 if (cls.match(/Mso[a-zA-Z]+/)) {
22699                     return;
22700                 }
22701                 cna.push(cls);
22702             });
22703             node.className = cna.length ? cna.join(' ') : '';
22704             if (!cna.length) {
22705                 node.removeAttribute("class");
22706             }
22707         }
22708         
22709         if (node.hasAttribute("lang")) {
22710             node.removeAttribute("lang");
22711         }
22712         
22713         if (node.hasAttribute("style")) {
22714             
22715             var styles = node.getAttribute("style").split(";");
22716             var nstyle = [];
22717             Roo.each(styles, function(s) {
22718                 if (!s.match(/:/)) {
22719                     return;
22720                 }
22721                 var kv = s.split(":");
22722                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22723                     return;
22724                 }
22725                 // what ever is left... we allow.
22726                 nstyle.push(s);
22727             });
22728             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22729             if (!nstyle.length) {
22730                 node.removeAttribute('style');
22731             }
22732         }
22733         this.iterateChildren(node, this.cleanWord);
22734         
22735         
22736         
22737     },
22738     /**
22739      * iterateChildren of a Node, calling fn each time, using this as the scole..
22740      * @param {DomNode} node node to iterate children of.
22741      * @param {Function} fn method of this class to call on each item.
22742      */
22743     iterateChildren : function(node, fn)
22744     {
22745         if (!node.childNodes.length) {
22746                 return;
22747         }
22748         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22749            fn.call(this, node.childNodes[i])
22750         }
22751     },
22752     
22753     
22754     /**
22755      * cleanTableWidths.
22756      *
22757      * Quite often pasting from word etc.. results in tables with column and widths.
22758      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22759      *
22760      */
22761     cleanTableWidths : function(node)
22762     {
22763          
22764          
22765         if (!node) {
22766             this.cleanTableWidths(this.doc.body);
22767             return;
22768         }
22769         
22770         // ignore list...
22771         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22772             return; 
22773         }
22774         Roo.log(node.tagName);
22775         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22776             this.iterateChildren(node, this.cleanTableWidths);
22777             return;
22778         }
22779         if (node.hasAttribute('width')) {
22780             node.removeAttribute('width');
22781         }
22782         
22783          
22784         if (node.hasAttribute("style")) {
22785             // pretty basic...
22786             
22787             var styles = node.getAttribute("style").split(";");
22788             var nstyle = [];
22789             Roo.each(styles, function(s) {
22790                 if (!s.match(/:/)) {
22791                     return;
22792                 }
22793                 var kv = s.split(":");
22794                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22795                     return;
22796                 }
22797                 // what ever is left... we allow.
22798                 nstyle.push(s);
22799             });
22800             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22801             if (!nstyle.length) {
22802                 node.removeAttribute('style');
22803             }
22804         }
22805         
22806         this.iterateChildren(node, this.cleanTableWidths);
22807         
22808         
22809     },
22810     
22811     
22812     
22813     
22814     domToHTML : function(currentElement, depth, nopadtext) {
22815         
22816         depth = depth || 0;
22817         nopadtext = nopadtext || false;
22818     
22819         if (!currentElement) {
22820             return this.domToHTML(this.doc.body);
22821         }
22822         
22823         //Roo.log(currentElement);
22824         var j;
22825         var allText = false;
22826         var nodeName = currentElement.nodeName;
22827         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22828         
22829         if  (nodeName == '#text') {
22830             
22831             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22832         }
22833         
22834         
22835         var ret = '';
22836         if (nodeName != 'BODY') {
22837              
22838             var i = 0;
22839             // Prints the node tagName, such as <A>, <IMG>, etc
22840             if (tagName) {
22841                 var attr = [];
22842                 for(i = 0; i < currentElement.attributes.length;i++) {
22843                     // quoting?
22844                     var aname = currentElement.attributes.item(i).name;
22845                     if (!currentElement.attributes.item(i).value.length) {
22846                         continue;
22847                     }
22848                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22849                 }
22850                 
22851                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22852             } 
22853             else {
22854                 
22855                 // eack
22856             }
22857         } else {
22858             tagName = false;
22859         }
22860         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22861             return ret;
22862         }
22863         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22864             nopadtext = true;
22865         }
22866         
22867         
22868         // Traverse the tree
22869         i = 0;
22870         var currentElementChild = currentElement.childNodes.item(i);
22871         var allText = true;
22872         var innerHTML  = '';
22873         lastnode = '';
22874         while (currentElementChild) {
22875             // Formatting code (indent the tree so it looks nice on the screen)
22876             var nopad = nopadtext;
22877             if (lastnode == 'SPAN') {
22878                 nopad  = true;
22879             }
22880             // text
22881             if  (currentElementChild.nodeName == '#text') {
22882                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22883                 toadd = nopadtext ? toadd : toadd.trim();
22884                 if (!nopad && toadd.length > 80) {
22885                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22886                 }
22887                 innerHTML  += toadd;
22888                 
22889                 i++;
22890                 currentElementChild = currentElement.childNodes.item(i);
22891                 lastNode = '';
22892                 continue;
22893             }
22894             allText = false;
22895             
22896             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22897                 
22898             // Recursively traverse the tree structure of the child node
22899             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22900             lastnode = currentElementChild.nodeName;
22901             i++;
22902             currentElementChild=currentElement.childNodes.item(i);
22903         }
22904         
22905         ret += innerHTML;
22906         
22907         if (!allText) {
22908                 // The remaining code is mostly for formatting the tree
22909             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22910         }
22911         
22912         
22913         if (tagName) {
22914             ret+= "</"+tagName+">";
22915         }
22916         return ret;
22917         
22918     },
22919         
22920     applyBlacklists : function()
22921     {
22922         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22923         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22924         
22925         this.white = [];
22926         this.black = [];
22927         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22928             if (b.indexOf(tag) > -1) {
22929                 return;
22930             }
22931             this.white.push(tag);
22932             
22933         }, this);
22934         
22935         Roo.each(w, function(tag) {
22936             if (b.indexOf(tag) > -1) {
22937                 return;
22938             }
22939             if (this.white.indexOf(tag) > -1) {
22940                 return;
22941             }
22942             this.white.push(tag);
22943             
22944         }, this);
22945         
22946         
22947         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22948             if (w.indexOf(tag) > -1) {
22949                 return;
22950             }
22951             this.black.push(tag);
22952             
22953         }, this);
22954         
22955         Roo.each(b, function(tag) {
22956             if (w.indexOf(tag) > -1) {
22957                 return;
22958             }
22959             if (this.black.indexOf(tag) > -1) {
22960                 return;
22961             }
22962             this.black.push(tag);
22963             
22964         }, this);
22965         
22966         
22967         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22968         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22969         
22970         this.cwhite = [];
22971         this.cblack = [];
22972         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22973             if (b.indexOf(tag) > -1) {
22974                 return;
22975             }
22976             this.cwhite.push(tag);
22977             
22978         }, this);
22979         
22980         Roo.each(w, function(tag) {
22981             if (b.indexOf(tag) > -1) {
22982                 return;
22983             }
22984             if (this.cwhite.indexOf(tag) > -1) {
22985                 return;
22986             }
22987             this.cwhite.push(tag);
22988             
22989         }, this);
22990         
22991         
22992         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22993             if (w.indexOf(tag) > -1) {
22994                 return;
22995             }
22996             this.cblack.push(tag);
22997             
22998         }, this);
22999         
23000         Roo.each(b, function(tag) {
23001             if (w.indexOf(tag) > -1) {
23002                 return;
23003             }
23004             if (this.cblack.indexOf(tag) > -1) {
23005                 return;
23006             }
23007             this.cblack.push(tag);
23008             
23009         }, this);
23010     },
23011     
23012     setStylesheets : function(stylesheets)
23013     {
23014         if(typeof(stylesheets) == 'string'){
23015             Roo.get(this.iframe.contentDocument.head).createChild({
23016                 tag : 'link',
23017                 rel : 'stylesheet',
23018                 type : 'text/css',
23019                 href : stylesheets
23020             });
23021             
23022             return;
23023         }
23024         var _this = this;
23025      
23026         Roo.each(stylesheets, function(s) {
23027             if(!s.length){
23028                 return;
23029             }
23030             
23031             Roo.get(_this.iframe.contentDocument.head).createChild({
23032                 tag : 'link',
23033                 rel : 'stylesheet',
23034                 type : 'text/css',
23035                 href : s
23036             });
23037         });
23038
23039         
23040     },
23041     
23042     removeStylesheets : function()
23043     {
23044         var _this = this;
23045         
23046         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23047             s.remove();
23048         });
23049     },
23050     
23051     setStyle : function(style)
23052     {
23053         Roo.get(this.iframe.contentDocument.head).createChild({
23054             tag : 'style',
23055             type : 'text/css',
23056             html : style
23057         });
23058
23059         return;
23060     }
23061     
23062     // hide stuff that is not compatible
23063     /**
23064      * @event blur
23065      * @hide
23066      */
23067     /**
23068      * @event change
23069      * @hide
23070      */
23071     /**
23072      * @event focus
23073      * @hide
23074      */
23075     /**
23076      * @event specialkey
23077      * @hide
23078      */
23079     /**
23080      * @cfg {String} fieldClass @hide
23081      */
23082     /**
23083      * @cfg {String} focusClass @hide
23084      */
23085     /**
23086      * @cfg {String} autoCreate @hide
23087      */
23088     /**
23089      * @cfg {String} inputType @hide
23090      */
23091     /**
23092      * @cfg {String} invalidClass @hide
23093      */
23094     /**
23095      * @cfg {String} invalidText @hide
23096      */
23097     /**
23098      * @cfg {String} msgFx @hide
23099      */
23100     /**
23101      * @cfg {String} validateOnBlur @hide
23102      */
23103 });
23104
23105 Roo.HtmlEditorCore.white = [
23106         'area', 'br', 'img', 'input', 'hr', 'wbr',
23107         
23108        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23109        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23110        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23111        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23112        'table',   'ul',         'xmp', 
23113        
23114        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23115       'thead',   'tr', 
23116      
23117       'dir', 'menu', 'ol', 'ul', 'dl',
23118        
23119       'embed',  'object'
23120 ];
23121
23122
23123 Roo.HtmlEditorCore.black = [
23124     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23125         'applet', // 
23126         'base',   'basefont', 'bgsound', 'blink',  'body', 
23127         'frame',  'frameset', 'head',    'html',   'ilayer', 
23128         'iframe', 'layer',  'link',     'meta',    'object',   
23129         'script', 'style' ,'title',  'xml' // clean later..
23130 ];
23131 Roo.HtmlEditorCore.clean = [
23132     'script', 'style', 'title', 'xml'
23133 ];
23134 Roo.HtmlEditorCore.remove = [
23135     'font'
23136 ];
23137 // attributes..
23138
23139 Roo.HtmlEditorCore.ablack = [
23140     'on'
23141 ];
23142     
23143 Roo.HtmlEditorCore.aclean = [ 
23144     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23145 ];
23146
23147 // protocols..
23148 Roo.HtmlEditorCore.pwhite= [
23149         'http',  'https',  'mailto'
23150 ];
23151
23152 // white listed style attributes.
23153 Roo.HtmlEditorCore.cwhite= [
23154       //  'text-align', /// default is to allow most things..
23155       
23156          
23157 //        'font-size'//??
23158 ];
23159
23160 // black listed style attributes.
23161 Roo.HtmlEditorCore.cblack= [
23162       //  'font-size' -- this can be set by the project 
23163 ];
23164
23165
23166 Roo.HtmlEditorCore.swapCodes   =[ 
23167     [    8211, "--" ], 
23168     [    8212, "--" ], 
23169     [    8216,  "'" ],  
23170     [    8217, "'" ],  
23171     [    8220, '"' ],  
23172     [    8221, '"' ],  
23173     [    8226, "*" ],  
23174     [    8230, "..." ]
23175 ]; 
23176
23177     /*
23178  * - LGPL
23179  *
23180  * HtmlEditor
23181  * 
23182  */
23183
23184 /**
23185  * @class Roo.bootstrap.HtmlEditor
23186  * @extends Roo.bootstrap.TextArea
23187  * Bootstrap HtmlEditor class
23188
23189  * @constructor
23190  * Create a new HtmlEditor
23191  * @param {Object} config The config object
23192  */
23193
23194 Roo.bootstrap.HtmlEditor = function(config){
23195     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23196     if (!this.toolbars) {
23197         this.toolbars = [];
23198     }
23199     
23200     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23201     this.addEvents({
23202             /**
23203              * @event initialize
23204              * Fires when the editor is fully initialized (including the iframe)
23205              * @param {HtmlEditor} this
23206              */
23207             initialize: true,
23208             /**
23209              * @event activate
23210              * Fires when the editor is first receives the focus. Any insertion must wait
23211              * until after this event.
23212              * @param {HtmlEditor} this
23213              */
23214             activate: true,
23215              /**
23216              * @event beforesync
23217              * Fires before the textarea is updated with content from the editor iframe. Return false
23218              * to cancel the sync.
23219              * @param {HtmlEditor} this
23220              * @param {String} html
23221              */
23222             beforesync: true,
23223              /**
23224              * @event beforepush
23225              * Fires before the iframe editor is updated with content from the textarea. Return false
23226              * to cancel the push.
23227              * @param {HtmlEditor} this
23228              * @param {String} html
23229              */
23230             beforepush: true,
23231              /**
23232              * @event sync
23233              * Fires when the textarea is updated with content from the editor iframe.
23234              * @param {HtmlEditor} this
23235              * @param {String} html
23236              */
23237             sync: true,
23238              /**
23239              * @event push
23240              * Fires when the iframe editor is updated with content from the textarea.
23241              * @param {HtmlEditor} this
23242              * @param {String} html
23243              */
23244             push: true,
23245              /**
23246              * @event editmodechange
23247              * Fires when the editor switches edit modes
23248              * @param {HtmlEditor} this
23249              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23250              */
23251             editmodechange: true,
23252             /**
23253              * @event editorevent
23254              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23255              * @param {HtmlEditor} this
23256              */
23257             editorevent: true,
23258             /**
23259              * @event firstfocus
23260              * Fires when on first focus - needed by toolbars..
23261              * @param {HtmlEditor} this
23262              */
23263             firstfocus: true,
23264             /**
23265              * @event autosave
23266              * Auto save the htmlEditor value as a file into Events
23267              * @param {HtmlEditor} this
23268              */
23269             autosave: true,
23270             /**
23271              * @event savedpreview
23272              * preview the saved version of htmlEditor
23273              * @param {HtmlEditor} this
23274              */
23275             savedpreview: true
23276         });
23277 };
23278
23279
23280 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23281     
23282     
23283       /**
23284      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23285      */
23286     toolbars : false,
23287     
23288      /**
23289     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23290     */
23291     btns : [],
23292    
23293      /**
23294      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23295      *                        Roo.resizable.
23296      */
23297     resizable : false,
23298      /**
23299      * @cfg {Number} height (in pixels)
23300      */   
23301     height: 300,
23302    /**
23303      * @cfg {Number} width (in pixels)
23304      */   
23305     width: false,
23306     
23307     /**
23308      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23309      * 
23310      */
23311     stylesheets: false,
23312     
23313     // id of frame..
23314     frameId: false,
23315     
23316     // private properties
23317     validationEvent : false,
23318     deferHeight: true,
23319     initialized : false,
23320     activated : false,
23321     
23322     onFocus : Roo.emptyFn,
23323     iframePad:3,
23324     hideMode:'offsets',
23325     
23326     tbContainer : false,
23327     
23328     bodyCls : '',
23329     
23330     toolbarContainer :function() {
23331         return this.wrap.select('.x-html-editor-tb',true).first();
23332     },
23333
23334     /**
23335      * Protected method that will not generally be called directly. It
23336      * is called when the editor creates its toolbar. Override this method if you need to
23337      * add custom toolbar buttons.
23338      * @param {HtmlEditor} editor
23339      */
23340     createToolbar : function(){
23341         Roo.log('renewing');
23342         Roo.log("create toolbars");
23343         
23344         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23345         this.toolbars[0].render(this.toolbarContainer());
23346         
23347         return;
23348         
23349 //        if (!editor.toolbars || !editor.toolbars.length) {
23350 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23351 //        }
23352 //        
23353 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23354 //            editor.toolbars[i] = Roo.factory(
23355 //                    typeof(editor.toolbars[i]) == 'string' ?
23356 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23357 //                Roo.bootstrap.HtmlEditor);
23358 //            editor.toolbars[i].init(editor);
23359 //        }
23360     },
23361
23362      
23363     // private
23364     onRender : function(ct, position)
23365     {
23366        // Roo.log("Call onRender: " + this.xtype);
23367         var _t = this;
23368         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23369       
23370         this.wrap = this.inputEl().wrap({
23371             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23372         });
23373         
23374         this.editorcore.onRender(ct, position);
23375          
23376         if (this.resizable) {
23377             this.resizeEl = new Roo.Resizable(this.wrap, {
23378                 pinned : true,
23379                 wrap: true,
23380                 dynamic : true,
23381                 minHeight : this.height,
23382                 height: this.height,
23383                 handles : this.resizable,
23384                 width: this.width,
23385                 listeners : {
23386                     resize : function(r, w, h) {
23387                         _t.onResize(w,h); // -something
23388                     }
23389                 }
23390             });
23391             
23392         }
23393         this.createToolbar(this);
23394        
23395         
23396         if(!this.width && this.resizable){
23397             this.setSize(this.wrap.getSize());
23398         }
23399         if (this.resizeEl) {
23400             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23401             // should trigger onReize..
23402         }
23403         
23404     },
23405
23406     // private
23407     onResize : function(w, h)
23408     {
23409         Roo.log('resize: ' +w + ',' + h );
23410         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23411         var ew = false;
23412         var eh = false;
23413         
23414         if(this.inputEl() ){
23415             if(typeof w == 'number'){
23416                 var aw = w - this.wrap.getFrameWidth('lr');
23417                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23418                 ew = aw;
23419             }
23420             if(typeof h == 'number'){
23421                  var tbh = -11;  // fixme it needs to tool bar size!
23422                 for (var i =0; i < this.toolbars.length;i++) {
23423                     // fixme - ask toolbars for heights?
23424                     tbh += this.toolbars[i].el.getHeight();
23425                     //if (this.toolbars[i].footer) {
23426                     //    tbh += this.toolbars[i].footer.el.getHeight();
23427                     //}
23428                 }
23429               
23430                 
23431                 
23432                 
23433                 
23434                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23435                 ah -= 5; // knock a few pixes off for look..
23436                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23437                 var eh = ah;
23438             }
23439         }
23440         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23441         this.editorcore.onResize(ew,eh);
23442         
23443     },
23444
23445     /**
23446      * Toggles the editor between standard and source edit mode.
23447      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23448      */
23449     toggleSourceEdit : function(sourceEditMode)
23450     {
23451         this.editorcore.toggleSourceEdit(sourceEditMode);
23452         
23453         if(this.editorcore.sourceEditMode){
23454             Roo.log('editor - showing textarea');
23455             
23456 //            Roo.log('in');
23457 //            Roo.log(this.syncValue());
23458             this.syncValue();
23459             this.inputEl().removeClass(['hide', 'x-hidden']);
23460             this.inputEl().dom.removeAttribute('tabIndex');
23461             this.inputEl().focus();
23462         }else{
23463             Roo.log('editor - hiding textarea');
23464 //            Roo.log('out')
23465 //            Roo.log(this.pushValue()); 
23466             this.pushValue();
23467             
23468             this.inputEl().addClass(['hide', 'x-hidden']);
23469             this.inputEl().dom.setAttribute('tabIndex', -1);
23470             //this.deferFocus();
23471         }
23472          
23473         if(this.resizable){
23474             this.setSize(this.wrap.getSize());
23475         }
23476         
23477         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23478     },
23479  
23480     // private (for BoxComponent)
23481     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23482
23483     // private (for BoxComponent)
23484     getResizeEl : function(){
23485         return this.wrap;
23486     },
23487
23488     // private (for BoxComponent)
23489     getPositionEl : function(){
23490         return this.wrap;
23491     },
23492
23493     // private
23494     initEvents : function(){
23495         this.originalValue = this.getValue();
23496     },
23497
23498 //    /**
23499 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23500 //     * @method
23501 //     */
23502 //    markInvalid : Roo.emptyFn,
23503 //    /**
23504 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23505 //     * @method
23506 //     */
23507 //    clearInvalid : Roo.emptyFn,
23508
23509     setValue : function(v){
23510         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23511         this.editorcore.pushValue();
23512     },
23513
23514      
23515     // private
23516     deferFocus : function(){
23517         this.focus.defer(10, this);
23518     },
23519
23520     // doc'ed in Field
23521     focus : function(){
23522         this.editorcore.focus();
23523         
23524     },
23525       
23526
23527     // private
23528     onDestroy : function(){
23529         
23530         
23531         
23532         if(this.rendered){
23533             
23534             for (var i =0; i < this.toolbars.length;i++) {
23535                 // fixme - ask toolbars for heights?
23536                 this.toolbars[i].onDestroy();
23537             }
23538             
23539             this.wrap.dom.innerHTML = '';
23540             this.wrap.remove();
23541         }
23542     },
23543
23544     // private
23545     onFirstFocus : function(){
23546         //Roo.log("onFirstFocus");
23547         this.editorcore.onFirstFocus();
23548          for (var i =0; i < this.toolbars.length;i++) {
23549             this.toolbars[i].onFirstFocus();
23550         }
23551         
23552     },
23553     
23554     // private
23555     syncValue : function()
23556     {   
23557         this.editorcore.syncValue();
23558     },
23559     
23560     pushValue : function()
23561     {   
23562         this.editorcore.pushValue();
23563     }
23564      
23565     
23566     // hide stuff that is not compatible
23567     /**
23568      * @event blur
23569      * @hide
23570      */
23571     /**
23572      * @event change
23573      * @hide
23574      */
23575     /**
23576      * @event focus
23577      * @hide
23578      */
23579     /**
23580      * @event specialkey
23581      * @hide
23582      */
23583     /**
23584      * @cfg {String} fieldClass @hide
23585      */
23586     /**
23587      * @cfg {String} focusClass @hide
23588      */
23589     /**
23590      * @cfg {String} autoCreate @hide
23591      */
23592     /**
23593      * @cfg {String} inputType @hide
23594      */
23595     /**
23596      * @cfg {String} invalidClass @hide
23597      */
23598     /**
23599      * @cfg {String} invalidText @hide
23600      */
23601     /**
23602      * @cfg {String} msgFx @hide
23603      */
23604     /**
23605      * @cfg {String} validateOnBlur @hide
23606      */
23607 });
23608  
23609     
23610    
23611    
23612    
23613       
23614 Roo.namespace('Roo.bootstrap.htmleditor');
23615 /**
23616  * @class Roo.bootstrap.HtmlEditorToolbar1
23617  * Basic Toolbar
23618  * 
23619  * Usage:
23620  *
23621  new Roo.bootstrap.HtmlEditor({
23622     ....
23623     toolbars : [
23624         new Roo.bootstrap.HtmlEditorToolbar1({
23625             disable : { fonts: 1 , format: 1, ..., ... , ...],
23626             btns : [ .... ]
23627         })
23628     }
23629      
23630  * 
23631  * @cfg {Object} disable List of elements to disable..
23632  * @cfg {Array} btns List of additional buttons.
23633  * 
23634  * 
23635  * NEEDS Extra CSS? 
23636  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23637  */
23638  
23639 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23640 {
23641     
23642     Roo.apply(this, config);
23643     
23644     // default disabled, based on 'good practice'..
23645     this.disable = this.disable || {};
23646     Roo.applyIf(this.disable, {
23647         fontSize : true,
23648         colors : true,
23649         specialElements : true
23650     });
23651     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23652     
23653     this.editor = config.editor;
23654     this.editorcore = config.editor.editorcore;
23655     
23656     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23657     
23658     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23659     // dont call parent... till later.
23660 }
23661 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23662      
23663     bar : true,
23664     
23665     editor : false,
23666     editorcore : false,
23667     
23668     
23669     formats : [
23670         "p" ,  
23671         "h1","h2","h3","h4","h5","h6", 
23672         "pre", "code", 
23673         "abbr", "acronym", "address", "cite", "samp", "var",
23674         'div','span'
23675     ],
23676     
23677     onRender : function(ct, position)
23678     {
23679        // Roo.log("Call onRender: " + this.xtype);
23680         
23681        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23682        Roo.log(this.el);
23683        this.el.dom.style.marginBottom = '0';
23684        var _this = this;
23685        var editorcore = this.editorcore;
23686        var editor= this.editor;
23687        
23688        var children = [];
23689        var btn = function(id,cmd , toggle, handler, html){
23690        
23691             var  event = toggle ? 'toggle' : 'click';
23692        
23693             var a = {
23694                 size : 'sm',
23695                 xtype: 'Button',
23696                 xns: Roo.bootstrap,
23697                 glyphicon : id,
23698                 cmd : id || cmd,
23699                 enableToggle:toggle !== false,
23700                 html : html || '',
23701                 pressed : toggle ? false : null,
23702                 listeners : {}
23703             };
23704             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23705                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23706             };
23707             children.push(a);
23708             return a;
23709        }
23710        
23711     //    var cb_box = function...
23712         
23713         var style = {
23714                 xtype: 'Button',
23715                 size : 'sm',
23716                 xns: Roo.bootstrap,
23717                 glyphicon : 'font',
23718                 //html : 'submit'
23719                 menu : {
23720                     xtype: 'Menu',
23721                     xns: Roo.bootstrap,
23722                     items:  []
23723                 }
23724         };
23725         Roo.each(this.formats, function(f) {
23726             style.menu.items.push({
23727                 xtype :'MenuItem',
23728                 xns: Roo.bootstrap,
23729                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23730                 tagname : f,
23731                 listeners : {
23732                     click : function()
23733                     {
23734                         editorcore.insertTag(this.tagname);
23735                         editor.focus();
23736                     }
23737                 }
23738                 
23739             });
23740         });
23741         children.push(style);   
23742         
23743         btn('bold',false,true);
23744         btn('italic',false,true);
23745         btn('align-left', 'justifyleft',true);
23746         btn('align-center', 'justifycenter',true);
23747         btn('align-right' , 'justifyright',true);
23748         btn('link', false, false, function(btn) {
23749             //Roo.log("create link?");
23750             var url = prompt(this.createLinkText, this.defaultLinkValue);
23751             if(url && url != 'http:/'+'/'){
23752                 this.editorcore.relayCmd('createlink', url);
23753             }
23754         }),
23755         btn('list','insertunorderedlist',true);
23756         btn('pencil', false,true, function(btn){
23757                 Roo.log(this);
23758                 this.toggleSourceEdit(btn.pressed);
23759         });
23760         
23761         if (this.editor.btns.length > 0) {
23762             for (var i = 0; i<this.editor.btns.length; i++) {
23763                 children.push(this.editor.btns[i]);
23764             }
23765         }
23766         
23767         /*
23768         var cog = {
23769                 xtype: 'Button',
23770                 size : 'sm',
23771                 xns: Roo.bootstrap,
23772                 glyphicon : 'cog',
23773                 //html : 'submit'
23774                 menu : {
23775                     xtype: 'Menu',
23776                     xns: Roo.bootstrap,
23777                     items:  []
23778                 }
23779         };
23780         
23781         cog.menu.items.push({
23782             xtype :'MenuItem',
23783             xns: Roo.bootstrap,
23784             html : Clean styles,
23785             tagname : f,
23786             listeners : {
23787                 click : function()
23788                 {
23789                     editorcore.insertTag(this.tagname);
23790                     editor.focus();
23791                 }
23792             }
23793             
23794         });
23795        */
23796         
23797          
23798        this.xtype = 'NavSimplebar';
23799         
23800         for(var i=0;i< children.length;i++) {
23801             
23802             this.buttons.add(this.addxtypeChild(children[i]));
23803             
23804         }
23805         
23806         editor.on('editorevent', this.updateToolbar, this);
23807     },
23808     onBtnClick : function(id)
23809     {
23810        this.editorcore.relayCmd(id);
23811        this.editorcore.focus();
23812     },
23813     
23814     /**
23815      * Protected method that will not generally be called directly. It triggers
23816      * a toolbar update by reading the markup state of the current selection in the editor.
23817      */
23818     updateToolbar: function(){
23819
23820         if(!this.editorcore.activated){
23821             this.editor.onFirstFocus(); // is this neeed?
23822             return;
23823         }
23824
23825         var btns = this.buttons; 
23826         var doc = this.editorcore.doc;
23827         btns.get('bold').setActive(doc.queryCommandState('bold'));
23828         btns.get('italic').setActive(doc.queryCommandState('italic'));
23829         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23830         
23831         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23832         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23833         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23834         
23835         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23836         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23837          /*
23838         
23839         var ans = this.editorcore.getAllAncestors();
23840         if (this.formatCombo) {
23841             
23842             
23843             var store = this.formatCombo.store;
23844             this.formatCombo.setValue("");
23845             for (var i =0; i < ans.length;i++) {
23846                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23847                     // select it..
23848                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23849                     break;
23850                 }
23851             }
23852         }
23853         
23854         
23855         
23856         // hides menus... - so this cant be on a menu...
23857         Roo.bootstrap.MenuMgr.hideAll();
23858         */
23859         Roo.bootstrap.MenuMgr.hideAll();
23860         //this.editorsyncValue();
23861     },
23862     onFirstFocus: function() {
23863         this.buttons.each(function(item){
23864            item.enable();
23865         });
23866     },
23867     toggleSourceEdit : function(sourceEditMode){
23868         
23869           
23870         if(sourceEditMode){
23871             Roo.log("disabling buttons");
23872            this.buttons.each( function(item){
23873                 if(item.cmd != 'pencil'){
23874                     item.disable();
23875                 }
23876             });
23877           
23878         }else{
23879             Roo.log("enabling buttons");
23880             if(this.editorcore.initialized){
23881                 this.buttons.each( function(item){
23882                     item.enable();
23883                 });
23884             }
23885             
23886         }
23887         Roo.log("calling toggole on editor");
23888         // tell the editor that it's been pressed..
23889         this.editor.toggleSourceEdit(sourceEditMode);
23890        
23891     }
23892 });
23893
23894
23895
23896
23897
23898 /**
23899  * @class Roo.bootstrap.Table.AbstractSelectionModel
23900  * @extends Roo.util.Observable
23901  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23902  * implemented by descendant classes.  This class should not be directly instantiated.
23903  * @constructor
23904  */
23905 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23906     this.locked = false;
23907     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23908 };
23909
23910
23911 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23912     /** @ignore Called by the grid automatically. Do not call directly. */
23913     init : function(grid){
23914         this.grid = grid;
23915         this.initEvents();
23916     },
23917
23918     /**
23919      * Locks the selections.
23920      */
23921     lock : function(){
23922         this.locked = true;
23923     },
23924
23925     /**
23926      * Unlocks the selections.
23927      */
23928     unlock : function(){
23929         this.locked = false;
23930     },
23931
23932     /**
23933      * Returns true if the selections are locked.
23934      * @return {Boolean}
23935      */
23936     isLocked : function(){
23937         return this.locked;
23938     }
23939 });
23940 /**
23941  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23942  * @class Roo.bootstrap.Table.RowSelectionModel
23943  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23944  * It supports multiple selections and keyboard selection/navigation. 
23945  * @constructor
23946  * @param {Object} config
23947  */
23948
23949 Roo.bootstrap.Table.RowSelectionModel = function(config){
23950     Roo.apply(this, config);
23951     this.selections = new Roo.util.MixedCollection(false, function(o){
23952         return o.id;
23953     });
23954
23955     this.last = false;
23956     this.lastActive = false;
23957
23958     this.addEvents({
23959         /**
23960              * @event selectionchange
23961              * Fires when the selection changes
23962              * @param {SelectionModel} this
23963              */
23964             "selectionchange" : true,
23965         /**
23966              * @event afterselectionchange
23967              * Fires after the selection changes (eg. by key press or clicking)
23968              * @param {SelectionModel} this
23969              */
23970             "afterselectionchange" : true,
23971         /**
23972              * @event beforerowselect
23973              * Fires when a row is selected being selected, return false to cancel.
23974              * @param {SelectionModel} this
23975              * @param {Number} rowIndex The selected index
23976              * @param {Boolean} keepExisting False if other selections will be cleared
23977              */
23978             "beforerowselect" : true,
23979         /**
23980              * @event rowselect
23981              * Fires when a row is selected.
23982              * @param {SelectionModel} this
23983              * @param {Number} rowIndex The selected index
23984              * @param {Roo.data.Record} r The record
23985              */
23986             "rowselect" : true,
23987         /**
23988              * @event rowdeselect
23989              * Fires when a row is deselected.
23990              * @param {SelectionModel} this
23991              * @param {Number} rowIndex The selected index
23992              */
23993         "rowdeselect" : true
23994     });
23995     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23996     this.locked = false;
23997  };
23998
23999 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24000     /**
24001      * @cfg {Boolean} singleSelect
24002      * True to allow selection of only one row at a time (defaults to false)
24003      */
24004     singleSelect : false,
24005
24006     // private
24007     initEvents : function()
24008     {
24009
24010         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24011         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24012         //}else{ // allow click to work like normal
24013          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24014         //}
24015         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24016         this.grid.on("rowclick", this.handleMouseDown, this);
24017         
24018         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24019             "up" : function(e){
24020                 if(!e.shiftKey){
24021                     this.selectPrevious(e.shiftKey);
24022                 }else if(this.last !== false && this.lastActive !== false){
24023                     var last = this.last;
24024                     this.selectRange(this.last,  this.lastActive-1);
24025                     this.grid.getView().focusRow(this.lastActive);
24026                     if(last !== false){
24027                         this.last = last;
24028                     }
24029                 }else{
24030                     this.selectFirstRow();
24031                 }
24032                 this.fireEvent("afterselectionchange", this);
24033             },
24034             "down" : function(e){
24035                 if(!e.shiftKey){
24036                     this.selectNext(e.shiftKey);
24037                 }else if(this.last !== false && this.lastActive !== false){
24038                     var last = this.last;
24039                     this.selectRange(this.last,  this.lastActive+1);
24040                     this.grid.getView().focusRow(this.lastActive);
24041                     if(last !== false){
24042                         this.last = last;
24043                     }
24044                 }else{
24045                     this.selectFirstRow();
24046                 }
24047                 this.fireEvent("afterselectionchange", this);
24048             },
24049             scope: this
24050         });
24051         this.grid.store.on('load', function(){
24052             this.selections.clear();
24053         },this);
24054         /*
24055         var view = this.grid.view;
24056         view.on("refresh", this.onRefresh, this);
24057         view.on("rowupdated", this.onRowUpdated, this);
24058         view.on("rowremoved", this.onRemove, this);
24059         */
24060     },
24061
24062     // private
24063     onRefresh : function()
24064     {
24065         var ds = this.grid.store, i, v = this.grid.view;
24066         var s = this.selections;
24067         s.each(function(r){
24068             if((i = ds.indexOfId(r.id)) != -1){
24069                 v.onRowSelect(i);
24070             }else{
24071                 s.remove(r);
24072             }
24073         });
24074     },
24075
24076     // private
24077     onRemove : function(v, index, r){
24078         this.selections.remove(r);
24079     },
24080
24081     // private
24082     onRowUpdated : function(v, index, r){
24083         if(this.isSelected(r)){
24084             v.onRowSelect(index);
24085         }
24086     },
24087
24088     /**
24089      * Select records.
24090      * @param {Array} records The records to select
24091      * @param {Boolean} keepExisting (optional) True to keep existing selections
24092      */
24093     selectRecords : function(records, keepExisting)
24094     {
24095         if(!keepExisting){
24096             this.clearSelections();
24097         }
24098             var ds = this.grid.store;
24099         for(var i = 0, len = records.length; i < len; i++){
24100             this.selectRow(ds.indexOf(records[i]), true);
24101         }
24102     },
24103
24104     /**
24105      * Gets the number of selected rows.
24106      * @return {Number}
24107      */
24108     getCount : function(){
24109         return this.selections.length;
24110     },
24111
24112     /**
24113      * Selects the first row in the grid.
24114      */
24115     selectFirstRow : function(){
24116         this.selectRow(0);
24117     },
24118
24119     /**
24120      * Select the last row.
24121      * @param {Boolean} keepExisting (optional) True to keep existing selections
24122      */
24123     selectLastRow : function(keepExisting){
24124         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24125         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24126     },
24127
24128     /**
24129      * Selects the row immediately following the last selected row.
24130      * @param {Boolean} keepExisting (optional) True to keep existing selections
24131      */
24132     selectNext : function(keepExisting)
24133     {
24134             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24135             this.selectRow(this.last+1, keepExisting);
24136             this.grid.getView().focusRow(this.last);
24137         }
24138     },
24139
24140     /**
24141      * Selects the row that precedes the last selected row.
24142      * @param {Boolean} keepExisting (optional) True to keep existing selections
24143      */
24144     selectPrevious : function(keepExisting){
24145         if(this.last){
24146             this.selectRow(this.last-1, keepExisting);
24147             this.grid.getView().focusRow(this.last);
24148         }
24149     },
24150
24151     /**
24152      * Returns the selected records
24153      * @return {Array} Array of selected records
24154      */
24155     getSelections : function(){
24156         return [].concat(this.selections.items);
24157     },
24158
24159     /**
24160      * Returns the first selected record.
24161      * @return {Record}
24162      */
24163     getSelected : function(){
24164         return this.selections.itemAt(0);
24165     },
24166
24167
24168     /**
24169      * Clears all selections.
24170      */
24171     clearSelections : function(fast)
24172     {
24173         if(this.locked) {
24174             return;
24175         }
24176         if(fast !== true){
24177                 var ds = this.grid.store;
24178             var s = this.selections;
24179             s.each(function(r){
24180                 this.deselectRow(ds.indexOfId(r.id));
24181             }, this);
24182             s.clear();
24183         }else{
24184             this.selections.clear();
24185         }
24186         this.last = false;
24187     },
24188
24189
24190     /**
24191      * Selects all rows.
24192      */
24193     selectAll : function(){
24194         if(this.locked) {
24195             return;
24196         }
24197         this.selections.clear();
24198         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24199             this.selectRow(i, true);
24200         }
24201     },
24202
24203     /**
24204      * Returns True if there is a selection.
24205      * @return {Boolean}
24206      */
24207     hasSelection : function(){
24208         return this.selections.length > 0;
24209     },
24210
24211     /**
24212      * Returns True if the specified row is selected.
24213      * @param {Number/Record} record The record or index of the record to check
24214      * @return {Boolean}
24215      */
24216     isSelected : function(index){
24217             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24218         return (r && this.selections.key(r.id) ? true : false);
24219     },
24220
24221     /**
24222      * Returns True if the specified record id is selected.
24223      * @param {String} id The id of record to check
24224      * @return {Boolean}
24225      */
24226     isIdSelected : function(id){
24227         return (this.selections.key(id) ? true : false);
24228     },
24229
24230
24231     // private
24232     handleMouseDBClick : function(e, t){
24233         
24234     },
24235     // private
24236     handleMouseDown : function(e, t)
24237     {
24238             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24239         if(this.isLocked() || rowIndex < 0 ){
24240             return;
24241         };
24242         if(e.shiftKey && this.last !== false){
24243             var last = this.last;
24244             this.selectRange(last, rowIndex, e.ctrlKey);
24245             this.last = last; // reset the last
24246             t.focus();
24247     
24248         }else{
24249             var isSelected = this.isSelected(rowIndex);
24250             //Roo.log("select row:" + rowIndex);
24251             if(isSelected){
24252                 this.deselectRow(rowIndex);
24253             } else {
24254                         this.selectRow(rowIndex, true);
24255             }
24256     
24257             /*
24258                 if(e.button !== 0 && isSelected){
24259                 alert('rowIndex 2: ' + rowIndex);
24260                     view.focusRow(rowIndex);
24261                 }else if(e.ctrlKey && isSelected){
24262                     this.deselectRow(rowIndex);
24263                 }else if(!isSelected){
24264                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24265                     view.focusRow(rowIndex);
24266                 }
24267             */
24268         }
24269         this.fireEvent("afterselectionchange", this);
24270     },
24271     // private
24272     handleDragableRowClick :  function(grid, rowIndex, e) 
24273     {
24274         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24275             this.selectRow(rowIndex, false);
24276             grid.view.focusRow(rowIndex);
24277              this.fireEvent("afterselectionchange", this);
24278         }
24279     },
24280     
24281     /**
24282      * Selects multiple rows.
24283      * @param {Array} rows Array of the indexes of the row to select
24284      * @param {Boolean} keepExisting (optional) True to keep existing selections
24285      */
24286     selectRows : function(rows, keepExisting){
24287         if(!keepExisting){
24288             this.clearSelections();
24289         }
24290         for(var i = 0, len = rows.length; i < len; i++){
24291             this.selectRow(rows[i], true);
24292         }
24293     },
24294
24295     /**
24296      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24297      * @param {Number} startRow The index of the first row in the range
24298      * @param {Number} endRow The index of the last row in the range
24299      * @param {Boolean} keepExisting (optional) True to retain existing selections
24300      */
24301     selectRange : function(startRow, endRow, keepExisting){
24302         if(this.locked) {
24303             return;
24304         }
24305         if(!keepExisting){
24306             this.clearSelections();
24307         }
24308         if(startRow <= endRow){
24309             for(var i = startRow; i <= endRow; i++){
24310                 this.selectRow(i, true);
24311             }
24312         }else{
24313             for(var i = startRow; i >= endRow; i--){
24314                 this.selectRow(i, true);
24315             }
24316         }
24317     },
24318
24319     /**
24320      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24321      * @param {Number} startRow The index of the first row in the range
24322      * @param {Number} endRow The index of the last row in the range
24323      */
24324     deselectRange : function(startRow, endRow, preventViewNotify){
24325         if(this.locked) {
24326             return;
24327         }
24328         for(var i = startRow; i <= endRow; i++){
24329             this.deselectRow(i, preventViewNotify);
24330         }
24331     },
24332
24333     /**
24334      * Selects a row.
24335      * @param {Number} row The index of the row to select
24336      * @param {Boolean} keepExisting (optional) True to keep existing selections
24337      */
24338     selectRow : function(index, keepExisting, preventViewNotify)
24339     {
24340             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24341             return;
24342         }
24343         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24344             if(!keepExisting || this.singleSelect){
24345                 this.clearSelections();
24346             }
24347             
24348             var r = this.grid.store.getAt(index);
24349             //console.log('selectRow - record id :' + r.id);
24350             
24351             this.selections.add(r);
24352             this.last = this.lastActive = index;
24353             if(!preventViewNotify){
24354                 var proxy = new Roo.Element(
24355                                 this.grid.getRowDom(index)
24356                 );
24357                 proxy.addClass('bg-info info');
24358             }
24359             this.fireEvent("rowselect", this, index, r);
24360             this.fireEvent("selectionchange", this);
24361         }
24362     },
24363
24364     /**
24365      * Deselects a row.
24366      * @param {Number} row The index of the row to deselect
24367      */
24368     deselectRow : function(index, preventViewNotify)
24369     {
24370         if(this.locked) {
24371             return;
24372         }
24373         if(this.last == index){
24374             this.last = false;
24375         }
24376         if(this.lastActive == index){
24377             this.lastActive = false;
24378         }
24379         
24380         var r = this.grid.store.getAt(index);
24381         if (!r) {
24382             return;
24383         }
24384         
24385         this.selections.remove(r);
24386         //.console.log('deselectRow - record id :' + r.id);
24387         if(!preventViewNotify){
24388         
24389             var proxy = new Roo.Element(
24390                 this.grid.getRowDom(index)
24391             );
24392             proxy.removeClass('bg-info info');
24393         }
24394         this.fireEvent("rowdeselect", this, index);
24395         this.fireEvent("selectionchange", this);
24396     },
24397
24398     // private
24399     restoreLast : function(){
24400         if(this._last){
24401             this.last = this._last;
24402         }
24403     },
24404
24405     // private
24406     acceptsNav : function(row, col, cm){
24407         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24408     },
24409
24410     // private
24411     onEditorKey : function(field, e){
24412         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24413         if(k == e.TAB){
24414             e.stopEvent();
24415             ed.completeEdit();
24416             if(e.shiftKey){
24417                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24418             }else{
24419                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24420             }
24421         }else if(k == e.ENTER && !e.ctrlKey){
24422             e.stopEvent();
24423             ed.completeEdit();
24424             if(e.shiftKey){
24425                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24426             }else{
24427                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24428             }
24429         }else if(k == e.ESC){
24430             ed.cancelEdit();
24431         }
24432         if(newCell){
24433             g.startEditing(newCell[0], newCell[1]);
24434         }
24435     }
24436 });
24437 /*
24438  * Based on:
24439  * Ext JS Library 1.1.1
24440  * Copyright(c) 2006-2007, Ext JS, LLC.
24441  *
24442  * Originally Released Under LGPL - original licence link has changed is not relivant.
24443  *
24444  * Fork - LGPL
24445  * <script type="text/javascript">
24446  */
24447  
24448 /**
24449  * @class Roo.bootstrap.PagingToolbar
24450  * @extends Roo.bootstrap.NavSimplebar
24451  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24452  * @constructor
24453  * Create a new PagingToolbar
24454  * @param {Object} config The config object
24455  * @param {Roo.data.Store} store
24456  */
24457 Roo.bootstrap.PagingToolbar = function(config)
24458 {
24459     // old args format still supported... - xtype is prefered..
24460         // created from xtype...
24461     
24462     this.ds = config.dataSource;
24463     
24464     if (config.store && !this.ds) {
24465         this.store= Roo.factory(config.store, Roo.data);
24466         this.ds = this.store;
24467         this.ds.xmodule = this.xmodule || false;
24468     }
24469     
24470     this.toolbarItems = [];
24471     if (config.items) {
24472         this.toolbarItems = config.items;
24473     }
24474     
24475     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24476     
24477     this.cursor = 0;
24478     
24479     if (this.ds) { 
24480         this.bind(this.ds);
24481     }
24482     
24483     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24484     
24485 };
24486
24487 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24488     /**
24489      * @cfg {Roo.data.Store} dataSource
24490      * The underlying data store providing the paged data
24491      */
24492     /**
24493      * @cfg {String/HTMLElement/Element} container
24494      * container The id or element that will contain the toolbar
24495      */
24496     /**
24497      * @cfg {Boolean} displayInfo
24498      * True to display the displayMsg (defaults to false)
24499      */
24500     /**
24501      * @cfg {Number} pageSize
24502      * The number of records to display per page (defaults to 20)
24503      */
24504     pageSize: 20,
24505     /**
24506      * @cfg {String} displayMsg
24507      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24508      */
24509     displayMsg : 'Displaying {0} - {1} of {2}',
24510     /**
24511      * @cfg {String} emptyMsg
24512      * The message to display when no records are found (defaults to "No data to display")
24513      */
24514     emptyMsg : 'No data to display',
24515     /**
24516      * Customizable piece of the default paging text (defaults to "Page")
24517      * @type String
24518      */
24519     beforePageText : "Page",
24520     /**
24521      * Customizable piece of the default paging text (defaults to "of %0")
24522      * @type String
24523      */
24524     afterPageText : "of {0}",
24525     /**
24526      * Customizable piece of the default paging text (defaults to "First Page")
24527      * @type String
24528      */
24529     firstText : "First Page",
24530     /**
24531      * Customizable piece of the default paging text (defaults to "Previous Page")
24532      * @type String
24533      */
24534     prevText : "Previous Page",
24535     /**
24536      * Customizable piece of the default paging text (defaults to "Next Page")
24537      * @type String
24538      */
24539     nextText : "Next Page",
24540     /**
24541      * Customizable piece of the default paging text (defaults to "Last Page")
24542      * @type String
24543      */
24544     lastText : "Last Page",
24545     /**
24546      * Customizable piece of the default paging text (defaults to "Refresh")
24547      * @type String
24548      */
24549     refreshText : "Refresh",
24550
24551     buttons : false,
24552     // private
24553     onRender : function(ct, position) 
24554     {
24555         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24556         this.navgroup.parentId = this.id;
24557         this.navgroup.onRender(this.el, null);
24558         // add the buttons to the navgroup
24559         
24560         if(this.displayInfo){
24561             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24562             this.displayEl = this.el.select('.x-paging-info', true).first();
24563 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24564 //            this.displayEl = navel.el.select('span',true).first();
24565         }
24566         
24567         var _this = this;
24568         
24569         if(this.buttons){
24570             Roo.each(_this.buttons, function(e){ // this might need to use render????
24571                Roo.factory(e).render(_this.el);
24572             });
24573         }
24574             
24575         Roo.each(_this.toolbarItems, function(e) {
24576             _this.navgroup.addItem(e);
24577         });
24578         
24579         
24580         this.first = this.navgroup.addItem({
24581             tooltip: this.firstText,
24582             cls: "prev",
24583             icon : 'fa fa-backward',
24584             disabled: true,
24585             preventDefault: true,
24586             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24587         });
24588         
24589         this.prev =  this.navgroup.addItem({
24590             tooltip: this.prevText,
24591             cls: "prev",
24592             icon : 'fa fa-step-backward',
24593             disabled: true,
24594             preventDefault: true,
24595             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24596         });
24597     //this.addSeparator();
24598         
24599         
24600         var field = this.navgroup.addItem( {
24601             tagtype : 'span',
24602             cls : 'x-paging-position',
24603             
24604             html : this.beforePageText  +
24605                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24606                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24607          } ); //?? escaped?
24608         
24609         this.field = field.el.select('input', true).first();
24610         this.field.on("keydown", this.onPagingKeydown, this);
24611         this.field.on("focus", function(){this.dom.select();});
24612     
24613     
24614         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24615         //this.field.setHeight(18);
24616         //this.addSeparator();
24617         this.next = this.navgroup.addItem({
24618             tooltip: this.nextText,
24619             cls: "next",
24620             html : ' <i class="fa fa-step-forward">',
24621             disabled: true,
24622             preventDefault: true,
24623             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24624         });
24625         this.last = this.navgroup.addItem({
24626             tooltip: this.lastText,
24627             icon : 'fa fa-forward',
24628             cls: "next",
24629             disabled: true,
24630             preventDefault: true,
24631             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24632         });
24633     //this.addSeparator();
24634         this.loading = this.navgroup.addItem({
24635             tooltip: this.refreshText,
24636             icon: 'fa fa-refresh',
24637             preventDefault: true,
24638             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24639         });
24640         
24641     },
24642
24643     // private
24644     updateInfo : function(){
24645         if(this.displayEl){
24646             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24647             var msg = count == 0 ?
24648                 this.emptyMsg :
24649                 String.format(
24650                     this.displayMsg,
24651                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24652                 );
24653             this.displayEl.update(msg);
24654         }
24655     },
24656
24657     // private
24658     onLoad : function(ds, r, o)
24659     {
24660         this.cursor = o.params.start ? o.params.start : 0;
24661         
24662         var d = this.getPageData(),
24663             ap = d.activePage,
24664             ps = d.pages;
24665         
24666         
24667         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24668         this.field.dom.value = ap;
24669         this.first.setDisabled(ap == 1);
24670         this.prev.setDisabled(ap == 1);
24671         this.next.setDisabled(ap == ps);
24672         this.last.setDisabled(ap == ps);
24673         this.loading.enable();
24674         this.updateInfo();
24675     },
24676
24677     // private
24678     getPageData : function(){
24679         var total = this.ds.getTotalCount();
24680         return {
24681             total : total,
24682             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24683             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24684         };
24685     },
24686
24687     // private
24688     onLoadError : function(){
24689         this.loading.enable();
24690     },
24691
24692     // private
24693     onPagingKeydown : function(e){
24694         var k = e.getKey();
24695         var d = this.getPageData();
24696         if(k == e.RETURN){
24697             var v = this.field.dom.value, pageNum;
24698             if(!v || isNaN(pageNum = parseInt(v, 10))){
24699                 this.field.dom.value = d.activePage;
24700                 return;
24701             }
24702             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24703             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24704             e.stopEvent();
24705         }
24706         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))
24707         {
24708           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24709           this.field.dom.value = pageNum;
24710           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24711           e.stopEvent();
24712         }
24713         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24714         {
24715           var v = this.field.dom.value, pageNum; 
24716           var increment = (e.shiftKey) ? 10 : 1;
24717           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24718                 increment *= -1;
24719           }
24720           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24721             this.field.dom.value = d.activePage;
24722             return;
24723           }
24724           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24725           {
24726             this.field.dom.value = parseInt(v, 10) + increment;
24727             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24728             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24729           }
24730           e.stopEvent();
24731         }
24732     },
24733
24734     // private
24735     beforeLoad : function(){
24736         if(this.loading){
24737             this.loading.disable();
24738         }
24739     },
24740
24741     // private
24742     onClick : function(which){
24743         
24744         var ds = this.ds;
24745         if (!ds) {
24746             return;
24747         }
24748         
24749         switch(which){
24750             case "first":
24751                 ds.load({params:{start: 0, limit: this.pageSize}});
24752             break;
24753             case "prev":
24754                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24755             break;
24756             case "next":
24757                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24758             break;
24759             case "last":
24760                 var total = ds.getTotalCount();
24761                 var extra = total % this.pageSize;
24762                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24763                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24764             break;
24765             case "refresh":
24766                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24767             break;
24768         }
24769     },
24770
24771     /**
24772      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24773      * @param {Roo.data.Store} store The data store to unbind
24774      */
24775     unbind : function(ds){
24776         ds.un("beforeload", this.beforeLoad, this);
24777         ds.un("load", this.onLoad, this);
24778         ds.un("loadexception", this.onLoadError, this);
24779         ds.un("remove", this.updateInfo, this);
24780         ds.un("add", this.updateInfo, this);
24781         this.ds = undefined;
24782     },
24783
24784     /**
24785      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24786      * @param {Roo.data.Store} store The data store to bind
24787      */
24788     bind : function(ds){
24789         ds.on("beforeload", this.beforeLoad, this);
24790         ds.on("load", this.onLoad, this);
24791         ds.on("loadexception", this.onLoadError, this);
24792         ds.on("remove", this.updateInfo, this);
24793         ds.on("add", this.updateInfo, this);
24794         this.ds = ds;
24795     }
24796 });/*
24797  * - LGPL
24798  *
24799  * element
24800  * 
24801  */
24802
24803 /**
24804  * @class Roo.bootstrap.MessageBar
24805  * @extends Roo.bootstrap.Component
24806  * Bootstrap MessageBar class
24807  * @cfg {String} html contents of the MessageBar
24808  * @cfg {String} weight (info | success | warning | danger) default info
24809  * @cfg {String} beforeClass insert the bar before the given class
24810  * @cfg {Boolean} closable (true | false) default false
24811  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24812  * 
24813  * @constructor
24814  * Create a new Element
24815  * @param {Object} config The config object
24816  */
24817
24818 Roo.bootstrap.MessageBar = function(config){
24819     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24820 };
24821
24822 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24823     
24824     html: '',
24825     weight: 'info',
24826     closable: false,
24827     fixed: false,
24828     beforeClass: 'bootstrap-sticky-wrap',
24829     
24830     getAutoCreate : function(){
24831         
24832         var cfg = {
24833             tag: 'div',
24834             cls: 'alert alert-dismissable alert-' + this.weight,
24835             cn: [
24836                 {
24837                     tag: 'span',
24838                     cls: 'message',
24839                     html: this.html || ''
24840                 }
24841             ]
24842         };
24843         
24844         if(this.fixed){
24845             cfg.cls += ' alert-messages-fixed';
24846         }
24847         
24848         if(this.closable){
24849             cfg.cn.push({
24850                 tag: 'button',
24851                 cls: 'close',
24852                 html: 'x'
24853             });
24854         }
24855         
24856         return cfg;
24857     },
24858     
24859     onRender : function(ct, position)
24860     {
24861         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24862         
24863         if(!this.el){
24864             var cfg = Roo.apply({},  this.getAutoCreate());
24865             cfg.id = Roo.id();
24866             
24867             if (this.cls) {
24868                 cfg.cls += ' ' + this.cls;
24869             }
24870             if (this.style) {
24871                 cfg.style = this.style;
24872             }
24873             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24874             
24875             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24876         }
24877         
24878         this.el.select('>button.close').on('click', this.hide, this);
24879         
24880     },
24881     
24882     show : function()
24883     {
24884         if (!this.rendered) {
24885             this.render();
24886         }
24887         
24888         this.el.show();
24889         
24890         this.fireEvent('show', this);
24891         
24892     },
24893     
24894     hide : function()
24895     {
24896         if (!this.rendered) {
24897             this.render();
24898         }
24899         
24900         this.el.hide();
24901         
24902         this.fireEvent('hide', this);
24903     },
24904     
24905     update : function()
24906     {
24907 //        var e = this.el.dom.firstChild;
24908 //        
24909 //        if(this.closable){
24910 //            e = e.nextSibling;
24911 //        }
24912 //        
24913 //        e.data = this.html || '';
24914
24915         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24916     }
24917    
24918 });
24919
24920  
24921
24922      /*
24923  * - LGPL
24924  *
24925  * Graph
24926  * 
24927  */
24928
24929
24930 /**
24931  * @class Roo.bootstrap.Graph
24932  * @extends Roo.bootstrap.Component
24933  * Bootstrap Graph class
24934 > Prameters
24935  -sm {number} sm 4
24936  -md {number} md 5
24937  @cfg {String} graphtype  bar | vbar | pie
24938  @cfg {number} g_x coodinator | centre x (pie)
24939  @cfg {number} g_y coodinator | centre y (pie)
24940  @cfg {number} g_r radius (pie)
24941  @cfg {number} g_height height of the chart (respected by all elements in the set)
24942  @cfg {number} g_width width of the chart (respected by all elements in the set)
24943  @cfg {Object} title The title of the chart
24944     
24945  -{Array}  values
24946  -opts (object) options for the chart 
24947      o {
24948      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24949      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24950      o vgutter (number)
24951      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.
24952      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24953      o to
24954      o stretch (boolean)
24955      o }
24956  -opts (object) options for the pie
24957      o{
24958      o cut
24959      o startAngle (number)
24960      o endAngle (number)
24961      } 
24962  *
24963  * @constructor
24964  * Create a new Input
24965  * @param {Object} config The config object
24966  */
24967
24968 Roo.bootstrap.Graph = function(config){
24969     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24970     
24971     this.addEvents({
24972         // img events
24973         /**
24974          * @event click
24975          * The img click event for the img.
24976          * @param {Roo.EventObject} e
24977          */
24978         "click" : true
24979     });
24980 };
24981
24982 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24983     
24984     sm: 4,
24985     md: 5,
24986     graphtype: 'bar',
24987     g_height: 250,
24988     g_width: 400,
24989     g_x: 50,
24990     g_y: 50,
24991     g_r: 30,
24992     opts:{
24993         //g_colors: this.colors,
24994         g_type: 'soft',
24995         g_gutter: '20%'
24996
24997     },
24998     title : false,
24999
25000     getAutoCreate : function(){
25001         
25002         var cfg = {
25003             tag: 'div',
25004             html : null
25005         };
25006         
25007         
25008         return  cfg;
25009     },
25010
25011     onRender : function(ct,position){
25012         
25013         
25014         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25015         
25016         if (typeof(Raphael) == 'undefined') {
25017             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25018             return;
25019         }
25020         
25021         this.raphael = Raphael(this.el.dom);
25022         
25023                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25024                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25025                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25026                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25027                 /*
25028                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25029                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25030                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25031                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25032                 
25033                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25034                 r.barchart(330, 10, 300, 220, data1);
25035                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25036                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25037                 */
25038                 
25039                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25040                 // r.barchart(30, 30, 560, 250,  xdata, {
25041                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25042                 //     axis : "0 0 1 1",
25043                 //     axisxlabels :  xdata
25044                 //     //yvalues : cols,
25045                    
25046                 // });
25047 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25048 //        
25049 //        this.load(null,xdata,{
25050 //                axis : "0 0 1 1",
25051 //                axisxlabels :  xdata
25052 //                });
25053
25054     },
25055
25056     load : function(graphtype,xdata,opts)
25057     {
25058         this.raphael.clear();
25059         if(!graphtype) {
25060             graphtype = this.graphtype;
25061         }
25062         if(!opts){
25063             opts = this.opts;
25064         }
25065         var r = this.raphael,
25066             fin = function () {
25067                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25068             },
25069             fout = function () {
25070                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25071             },
25072             pfin = function() {
25073                 this.sector.stop();
25074                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25075
25076                 if (this.label) {
25077                     this.label[0].stop();
25078                     this.label[0].attr({ r: 7.5 });
25079                     this.label[1].attr({ "font-weight": 800 });
25080                 }
25081             },
25082             pfout = function() {
25083                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25084
25085                 if (this.label) {
25086                     this.label[0].animate({ r: 5 }, 500, "bounce");
25087                     this.label[1].attr({ "font-weight": 400 });
25088                 }
25089             };
25090
25091         switch(graphtype){
25092             case 'bar':
25093                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25094                 break;
25095             case 'hbar':
25096                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25097                 break;
25098             case 'pie':
25099 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25100 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25101 //            
25102                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25103                 
25104                 break;
25105
25106         }
25107         
25108         if(this.title){
25109             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25110         }
25111         
25112     },
25113     
25114     setTitle: function(o)
25115     {
25116         this.title = o;
25117     },
25118     
25119     initEvents: function() {
25120         
25121         if(!this.href){
25122             this.el.on('click', this.onClick, this);
25123         }
25124     },
25125     
25126     onClick : function(e)
25127     {
25128         Roo.log('img onclick');
25129         this.fireEvent('click', this, e);
25130     }
25131    
25132 });
25133
25134  
25135 /*
25136  * - LGPL
25137  *
25138  * numberBox
25139  * 
25140  */
25141 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25142
25143 /**
25144  * @class Roo.bootstrap.dash.NumberBox
25145  * @extends Roo.bootstrap.Component
25146  * Bootstrap NumberBox class
25147  * @cfg {String} headline Box headline
25148  * @cfg {String} content Box content
25149  * @cfg {String} icon Box icon
25150  * @cfg {String} footer Footer text
25151  * @cfg {String} fhref Footer href
25152  * 
25153  * @constructor
25154  * Create a new NumberBox
25155  * @param {Object} config The config object
25156  */
25157
25158
25159 Roo.bootstrap.dash.NumberBox = function(config){
25160     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25161     
25162 };
25163
25164 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25165     
25166     headline : '',
25167     content : '',
25168     icon : '',
25169     footer : '',
25170     fhref : '',
25171     ficon : '',
25172     
25173     getAutoCreate : function(){
25174         
25175         var cfg = {
25176             tag : 'div',
25177             cls : 'small-box ',
25178             cn : [
25179                 {
25180                     tag : 'div',
25181                     cls : 'inner',
25182                     cn :[
25183                         {
25184                             tag : 'h3',
25185                             cls : 'roo-headline',
25186                             html : this.headline
25187                         },
25188                         {
25189                             tag : 'p',
25190                             cls : 'roo-content',
25191                             html : this.content
25192                         }
25193                     ]
25194                 }
25195             ]
25196         };
25197         
25198         if(this.icon){
25199             cfg.cn.push({
25200                 tag : 'div',
25201                 cls : 'icon',
25202                 cn :[
25203                     {
25204                         tag : 'i',
25205                         cls : 'ion ' + this.icon
25206                     }
25207                 ]
25208             });
25209         }
25210         
25211         if(this.footer){
25212             var footer = {
25213                 tag : 'a',
25214                 cls : 'small-box-footer',
25215                 href : this.fhref || '#',
25216                 html : this.footer
25217             };
25218             
25219             cfg.cn.push(footer);
25220             
25221         }
25222         
25223         return  cfg;
25224     },
25225
25226     onRender : function(ct,position){
25227         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25228
25229
25230        
25231                 
25232     },
25233
25234     setHeadline: function (value)
25235     {
25236         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25237     },
25238     
25239     setFooter: function (value, href)
25240     {
25241         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25242         
25243         if(href){
25244             this.el.select('a.small-box-footer',true).first().attr('href', href);
25245         }
25246         
25247     },
25248
25249     setContent: function (value)
25250     {
25251         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25252     },
25253
25254     initEvents: function() 
25255     {   
25256         
25257     }
25258     
25259 });
25260
25261  
25262 /*
25263  * - LGPL
25264  *
25265  * TabBox
25266  * 
25267  */
25268 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25269
25270 /**
25271  * @class Roo.bootstrap.dash.TabBox
25272  * @extends Roo.bootstrap.Component
25273  * Bootstrap TabBox class
25274  * @cfg {String} title Title of the TabBox
25275  * @cfg {String} icon Icon of the TabBox
25276  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25277  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25278  * 
25279  * @constructor
25280  * Create a new TabBox
25281  * @param {Object} config The config object
25282  */
25283
25284
25285 Roo.bootstrap.dash.TabBox = function(config){
25286     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25287     this.addEvents({
25288         // raw events
25289         /**
25290          * @event addpane
25291          * When a pane is added
25292          * @param {Roo.bootstrap.dash.TabPane} pane
25293          */
25294         "addpane" : true,
25295         /**
25296          * @event activatepane
25297          * When a pane is activated
25298          * @param {Roo.bootstrap.dash.TabPane} pane
25299          */
25300         "activatepane" : true
25301         
25302          
25303     });
25304     
25305     this.panes = [];
25306 };
25307
25308 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25309
25310     title : '',
25311     icon : false,
25312     showtabs : true,
25313     tabScrollable : false,
25314     
25315     getChildContainer : function()
25316     {
25317         return this.el.select('.tab-content', true).first();
25318     },
25319     
25320     getAutoCreate : function(){
25321         
25322         var header = {
25323             tag: 'li',
25324             cls: 'pull-left header',
25325             html: this.title,
25326             cn : []
25327         };
25328         
25329         if(this.icon){
25330             header.cn.push({
25331                 tag: 'i',
25332                 cls: 'fa ' + this.icon
25333             });
25334         }
25335         
25336         var h = {
25337             tag: 'ul',
25338             cls: 'nav nav-tabs pull-right',
25339             cn: [
25340                 header
25341             ]
25342         };
25343         
25344         if(this.tabScrollable){
25345             h = {
25346                 tag: 'div',
25347                 cls: 'tab-header',
25348                 cn: [
25349                     {
25350                         tag: 'ul',
25351                         cls: 'nav nav-tabs pull-right',
25352                         cn: [
25353                             header
25354                         ]
25355                     }
25356                 ]
25357             };
25358         }
25359         
25360         var cfg = {
25361             tag: 'div',
25362             cls: 'nav-tabs-custom',
25363             cn: [
25364                 h,
25365                 {
25366                     tag: 'div',
25367                     cls: 'tab-content no-padding',
25368                     cn: []
25369                 }
25370             ]
25371         };
25372
25373         return  cfg;
25374     },
25375     initEvents : function()
25376     {
25377         //Roo.log('add add pane handler');
25378         this.on('addpane', this.onAddPane, this);
25379     },
25380      /**
25381      * Updates the box title
25382      * @param {String} html to set the title to.
25383      */
25384     setTitle : function(value)
25385     {
25386         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25387     },
25388     onAddPane : function(pane)
25389     {
25390         this.panes.push(pane);
25391         //Roo.log('addpane');
25392         //Roo.log(pane);
25393         // tabs are rendere left to right..
25394         if(!this.showtabs){
25395             return;
25396         }
25397         
25398         var ctr = this.el.select('.nav-tabs', true).first();
25399          
25400          
25401         var existing = ctr.select('.nav-tab',true);
25402         var qty = existing.getCount();;
25403         
25404         
25405         var tab = ctr.createChild({
25406             tag : 'li',
25407             cls : 'nav-tab' + (qty ? '' : ' active'),
25408             cn : [
25409                 {
25410                     tag : 'a',
25411                     href:'#',
25412                     html : pane.title
25413                 }
25414             ]
25415         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25416         pane.tab = tab;
25417         
25418         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25419         if (!qty) {
25420             pane.el.addClass('active');
25421         }
25422         
25423                 
25424     },
25425     onTabClick : function(ev,un,ob,pane)
25426     {
25427         //Roo.log('tab - prev default');
25428         ev.preventDefault();
25429         
25430         
25431         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25432         pane.tab.addClass('active');
25433         //Roo.log(pane.title);
25434         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25435         // technically we should have a deactivate event.. but maybe add later.
25436         // and it should not de-activate the selected tab...
25437         this.fireEvent('activatepane', pane);
25438         pane.el.addClass('active');
25439         pane.fireEvent('activate');
25440         
25441         
25442     },
25443     
25444     getActivePane : function()
25445     {
25446         var r = false;
25447         Roo.each(this.panes, function(p) {
25448             if(p.el.hasClass('active')){
25449                 r = p;
25450                 return false;
25451             }
25452             
25453             return;
25454         });
25455         
25456         return r;
25457     }
25458     
25459     
25460 });
25461
25462  
25463 /*
25464  * - LGPL
25465  *
25466  * Tab pane
25467  * 
25468  */
25469 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25470 /**
25471  * @class Roo.bootstrap.TabPane
25472  * @extends Roo.bootstrap.Component
25473  * Bootstrap TabPane class
25474  * @cfg {Boolean} active (false | true) Default false
25475  * @cfg {String} title title of panel
25476
25477  * 
25478  * @constructor
25479  * Create a new TabPane
25480  * @param {Object} config The config object
25481  */
25482
25483 Roo.bootstrap.dash.TabPane = function(config){
25484     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25485     
25486     this.addEvents({
25487         // raw events
25488         /**
25489          * @event activate
25490          * When a pane is activated
25491          * @param {Roo.bootstrap.dash.TabPane} pane
25492          */
25493         "activate" : true
25494          
25495     });
25496 };
25497
25498 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25499     
25500     active : false,
25501     title : '',
25502     
25503     // the tabBox that this is attached to.
25504     tab : false,
25505      
25506     getAutoCreate : function() 
25507     {
25508         var cfg = {
25509             tag: 'div',
25510             cls: 'tab-pane'
25511         };
25512         
25513         if(this.active){
25514             cfg.cls += ' active';
25515         }
25516         
25517         return cfg;
25518     },
25519     initEvents  : function()
25520     {
25521         //Roo.log('trigger add pane handler');
25522         this.parent().fireEvent('addpane', this)
25523     },
25524     
25525      /**
25526      * Updates the tab title 
25527      * @param {String} html to set the title to.
25528      */
25529     setTitle: function(str)
25530     {
25531         if (!this.tab) {
25532             return;
25533         }
25534         this.title = str;
25535         this.tab.select('a', true).first().dom.innerHTML = str;
25536         
25537     }
25538     
25539     
25540     
25541 });
25542
25543  
25544
25545
25546  /*
25547  * - LGPL
25548  *
25549  * menu
25550  * 
25551  */
25552 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25553
25554 /**
25555  * @class Roo.bootstrap.menu.Menu
25556  * @extends Roo.bootstrap.Component
25557  * Bootstrap Menu class - container for Menu
25558  * @cfg {String} html Text of the menu
25559  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25560  * @cfg {String} icon Font awesome icon
25561  * @cfg {String} pos Menu align to (top | bottom) default bottom
25562  * 
25563  * 
25564  * @constructor
25565  * Create a new Menu
25566  * @param {Object} config The config object
25567  */
25568
25569
25570 Roo.bootstrap.menu.Menu = function(config){
25571     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25572     
25573     this.addEvents({
25574         /**
25575          * @event beforeshow
25576          * Fires before this menu is displayed
25577          * @param {Roo.bootstrap.menu.Menu} this
25578          */
25579         beforeshow : true,
25580         /**
25581          * @event beforehide
25582          * Fires before this menu is hidden
25583          * @param {Roo.bootstrap.menu.Menu} this
25584          */
25585         beforehide : true,
25586         /**
25587          * @event show
25588          * Fires after this menu is displayed
25589          * @param {Roo.bootstrap.menu.Menu} this
25590          */
25591         show : true,
25592         /**
25593          * @event hide
25594          * Fires after this menu is hidden
25595          * @param {Roo.bootstrap.menu.Menu} this
25596          */
25597         hide : true,
25598         /**
25599          * @event click
25600          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25601          * @param {Roo.bootstrap.menu.Menu} this
25602          * @param {Roo.EventObject} e
25603          */
25604         click : true
25605     });
25606     
25607 };
25608
25609 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25610     
25611     submenu : false,
25612     html : '',
25613     weight : 'default',
25614     icon : false,
25615     pos : 'bottom',
25616     
25617     
25618     getChildContainer : function() {
25619         if(this.isSubMenu){
25620             return this.el;
25621         }
25622         
25623         return this.el.select('ul.dropdown-menu', true).first();  
25624     },
25625     
25626     getAutoCreate : function()
25627     {
25628         var text = [
25629             {
25630                 tag : 'span',
25631                 cls : 'roo-menu-text',
25632                 html : this.html
25633             }
25634         ];
25635         
25636         if(this.icon){
25637             text.unshift({
25638                 tag : 'i',
25639                 cls : 'fa ' + this.icon
25640             })
25641         }
25642         
25643         
25644         var cfg = {
25645             tag : 'div',
25646             cls : 'btn-group',
25647             cn : [
25648                 {
25649                     tag : 'button',
25650                     cls : 'dropdown-button btn btn-' + this.weight,
25651                     cn : text
25652                 },
25653                 {
25654                     tag : 'button',
25655                     cls : 'dropdown-toggle btn btn-' + this.weight,
25656                     cn : [
25657                         {
25658                             tag : 'span',
25659                             cls : 'caret'
25660                         }
25661                     ]
25662                 },
25663                 {
25664                     tag : 'ul',
25665                     cls : 'dropdown-menu'
25666                 }
25667             ]
25668             
25669         };
25670         
25671         if(this.pos == 'top'){
25672             cfg.cls += ' dropup';
25673         }
25674         
25675         if(this.isSubMenu){
25676             cfg = {
25677                 tag : 'ul',
25678                 cls : 'dropdown-menu'
25679             }
25680         }
25681         
25682         return cfg;
25683     },
25684     
25685     onRender : function(ct, position)
25686     {
25687         this.isSubMenu = ct.hasClass('dropdown-submenu');
25688         
25689         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25690     },
25691     
25692     initEvents : function() 
25693     {
25694         if(this.isSubMenu){
25695             return;
25696         }
25697         
25698         this.hidden = true;
25699         
25700         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25701         this.triggerEl.on('click', this.onTriggerPress, this);
25702         
25703         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25704         this.buttonEl.on('click', this.onClick, this);
25705         
25706     },
25707     
25708     list : function()
25709     {
25710         if(this.isSubMenu){
25711             return this.el;
25712         }
25713         
25714         return this.el.select('ul.dropdown-menu', true).first();
25715     },
25716     
25717     onClick : function(e)
25718     {
25719         this.fireEvent("click", this, e);
25720     },
25721     
25722     onTriggerPress  : function(e)
25723     {   
25724         if (this.isVisible()) {
25725             this.hide();
25726         } else {
25727             this.show();
25728         }
25729     },
25730     
25731     isVisible : function(){
25732         return !this.hidden;
25733     },
25734     
25735     show : function()
25736     {
25737         this.fireEvent("beforeshow", this);
25738         
25739         this.hidden = false;
25740         this.el.addClass('open');
25741         
25742         Roo.get(document).on("mouseup", this.onMouseUp, this);
25743         
25744         this.fireEvent("show", this);
25745         
25746         
25747     },
25748     
25749     hide : function()
25750     {
25751         this.fireEvent("beforehide", this);
25752         
25753         this.hidden = true;
25754         this.el.removeClass('open');
25755         
25756         Roo.get(document).un("mouseup", this.onMouseUp);
25757         
25758         this.fireEvent("hide", this);
25759     },
25760     
25761     onMouseUp : function()
25762     {
25763         this.hide();
25764     }
25765     
25766 });
25767
25768  
25769  /*
25770  * - LGPL
25771  *
25772  * menu item
25773  * 
25774  */
25775 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25776
25777 /**
25778  * @class Roo.bootstrap.menu.Item
25779  * @extends Roo.bootstrap.Component
25780  * Bootstrap MenuItem class
25781  * @cfg {Boolean} submenu (true | false) default false
25782  * @cfg {String} html text of the item
25783  * @cfg {String} href the link
25784  * @cfg {Boolean} disable (true | false) default false
25785  * @cfg {Boolean} preventDefault (true | false) default true
25786  * @cfg {String} icon Font awesome icon
25787  * @cfg {String} pos Submenu align to (left | right) default right 
25788  * 
25789  * 
25790  * @constructor
25791  * Create a new Item
25792  * @param {Object} config The config object
25793  */
25794
25795
25796 Roo.bootstrap.menu.Item = function(config){
25797     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25798     this.addEvents({
25799         /**
25800          * @event mouseover
25801          * Fires when the mouse is hovering over this menu
25802          * @param {Roo.bootstrap.menu.Item} this
25803          * @param {Roo.EventObject} e
25804          */
25805         mouseover : true,
25806         /**
25807          * @event mouseout
25808          * Fires when the mouse exits this menu
25809          * @param {Roo.bootstrap.menu.Item} this
25810          * @param {Roo.EventObject} e
25811          */
25812         mouseout : true,
25813         // raw events
25814         /**
25815          * @event click
25816          * The raw click event for the entire grid.
25817          * @param {Roo.EventObject} e
25818          */
25819         click : true
25820     });
25821 };
25822
25823 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25824     
25825     submenu : false,
25826     href : '',
25827     html : '',
25828     preventDefault: true,
25829     disable : false,
25830     icon : false,
25831     pos : 'right',
25832     
25833     getAutoCreate : function()
25834     {
25835         var text = [
25836             {
25837                 tag : 'span',
25838                 cls : 'roo-menu-item-text',
25839                 html : this.html
25840             }
25841         ];
25842         
25843         if(this.icon){
25844             text.unshift({
25845                 tag : 'i',
25846                 cls : 'fa ' + this.icon
25847             })
25848         }
25849         
25850         var cfg = {
25851             tag : 'li',
25852             cn : [
25853                 {
25854                     tag : 'a',
25855                     href : this.href || '#',
25856                     cn : text
25857                 }
25858             ]
25859         };
25860         
25861         if(this.disable){
25862             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25863         }
25864         
25865         if(this.submenu){
25866             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25867             
25868             if(this.pos == 'left'){
25869                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25870             }
25871         }
25872         
25873         return cfg;
25874     },
25875     
25876     initEvents : function() 
25877     {
25878         this.el.on('mouseover', this.onMouseOver, this);
25879         this.el.on('mouseout', this.onMouseOut, this);
25880         
25881         this.el.select('a', true).first().on('click', this.onClick, this);
25882         
25883     },
25884     
25885     onClick : function(e)
25886     {
25887         if(this.preventDefault){
25888             e.preventDefault();
25889         }
25890         
25891         this.fireEvent("click", this, e);
25892     },
25893     
25894     onMouseOver : function(e)
25895     {
25896         if(this.submenu && this.pos == 'left'){
25897             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25898         }
25899         
25900         this.fireEvent("mouseover", this, e);
25901     },
25902     
25903     onMouseOut : function(e)
25904     {
25905         this.fireEvent("mouseout", this, e);
25906     }
25907 });
25908
25909  
25910
25911  /*
25912  * - LGPL
25913  *
25914  * menu separator
25915  * 
25916  */
25917 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25918
25919 /**
25920  * @class Roo.bootstrap.menu.Separator
25921  * @extends Roo.bootstrap.Component
25922  * Bootstrap Separator class
25923  * 
25924  * @constructor
25925  * Create a new Separator
25926  * @param {Object} config The config object
25927  */
25928
25929
25930 Roo.bootstrap.menu.Separator = function(config){
25931     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25932 };
25933
25934 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25935     
25936     getAutoCreate : function(){
25937         var cfg = {
25938             tag : 'li',
25939             cls: 'divider'
25940         };
25941         
25942         return cfg;
25943     }
25944    
25945 });
25946
25947  
25948
25949  /*
25950  * - LGPL
25951  *
25952  * Tooltip
25953  * 
25954  */
25955
25956 /**
25957  * @class Roo.bootstrap.Tooltip
25958  * Bootstrap Tooltip class
25959  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25960  * to determine which dom element triggers the tooltip.
25961  * 
25962  * It needs to add support for additional attributes like tooltip-position
25963  * 
25964  * @constructor
25965  * Create a new Toolti
25966  * @param {Object} config The config object
25967  */
25968
25969 Roo.bootstrap.Tooltip = function(config){
25970     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25971     
25972     this.alignment = Roo.bootstrap.Tooltip.alignment;
25973     
25974     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25975         this.alignment = config.alignment;
25976     }
25977     
25978 };
25979
25980 Roo.apply(Roo.bootstrap.Tooltip, {
25981     /**
25982      * @function init initialize tooltip monitoring.
25983      * @static
25984      */
25985     currentEl : false,
25986     currentTip : false,
25987     currentRegion : false,
25988     
25989     //  init : delay?
25990     
25991     init : function()
25992     {
25993         Roo.get(document).on('mouseover', this.enter ,this);
25994         Roo.get(document).on('mouseout', this.leave, this);
25995          
25996         
25997         this.currentTip = new Roo.bootstrap.Tooltip();
25998     },
25999     
26000     enter : function(ev)
26001     {
26002         var dom = ev.getTarget();
26003         
26004         //Roo.log(['enter',dom]);
26005         var el = Roo.fly(dom);
26006         if (this.currentEl) {
26007             //Roo.log(dom);
26008             //Roo.log(this.currentEl);
26009             //Roo.log(this.currentEl.contains(dom));
26010             if (this.currentEl == el) {
26011                 return;
26012             }
26013             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26014                 return;
26015             }
26016
26017         }
26018         
26019         if (this.currentTip.el) {
26020             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26021         }    
26022         //Roo.log(ev);
26023         
26024         if(!el || el.dom == document){
26025             return;
26026         }
26027         
26028         var bindEl = el;
26029         
26030         // you can not look for children, as if el is the body.. then everythign is the child..
26031         if (!el.attr('tooltip')) { //
26032             if (!el.select("[tooltip]").elements.length) {
26033                 return;
26034             }
26035             // is the mouse over this child...?
26036             bindEl = el.select("[tooltip]").first();
26037             var xy = ev.getXY();
26038             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26039                 //Roo.log("not in region.");
26040                 return;
26041             }
26042             //Roo.log("child element over..");
26043             
26044         }
26045         this.currentEl = bindEl;
26046         this.currentTip.bind(bindEl);
26047         this.currentRegion = Roo.lib.Region.getRegion(dom);
26048         this.currentTip.enter();
26049         
26050     },
26051     leave : function(ev)
26052     {
26053         var dom = ev.getTarget();
26054         //Roo.log(['leave',dom]);
26055         if (!this.currentEl) {
26056             return;
26057         }
26058         
26059         
26060         if (dom != this.currentEl.dom) {
26061             return;
26062         }
26063         var xy = ev.getXY();
26064         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26065             return;
26066         }
26067         // only activate leave if mouse cursor is outside... bounding box..
26068         
26069         
26070         
26071         
26072         if (this.currentTip) {
26073             this.currentTip.leave();
26074         }
26075         //Roo.log('clear currentEl');
26076         this.currentEl = false;
26077         
26078         
26079     },
26080     alignment : {
26081         'left' : ['r-l', [-2,0], 'right'],
26082         'right' : ['l-r', [2,0], 'left'],
26083         'bottom' : ['t-b', [0,2], 'top'],
26084         'top' : [ 'b-t', [0,-2], 'bottom']
26085     }
26086     
26087 });
26088
26089
26090 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26091     
26092     
26093     bindEl : false,
26094     
26095     delay : null, // can be { show : 300 , hide: 500}
26096     
26097     timeout : null,
26098     
26099     hoverState : null, //???
26100     
26101     placement : 'bottom', 
26102     
26103     alignment : false,
26104     
26105     getAutoCreate : function(){
26106     
26107         var cfg = {
26108            cls : 'tooltip',
26109            role : 'tooltip',
26110            cn : [
26111                 {
26112                     cls : 'tooltip-arrow'
26113                 },
26114                 {
26115                     cls : 'tooltip-inner'
26116                 }
26117            ]
26118         };
26119         
26120         return cfg;
26121     },
26122     bind : function(el)
26123     {
26124         this.bindEl = el;
26125     },
26126       
26127     
26128     enter : function () {
26129        
26130         if (this.timeout != null) {
26131             clearTimeout(this.timeout);
26132         }
26133         
26134         this.hoverState = 'in';
26135          //Roo.log("enter - show");
26136         if (!this.delay || !this.delay.show) {
26137             this.show();
26138             return;
26139         }
26140         var _t = this;
26141         this.timeout = setTimeout(function () {
26142             if (_t.hoverState == 'in') {
26143                 _t.show();
26144             }
26145         }, this.delay.show);
26146     },
26147     leave : function()
26148     {
26149         clearTimeout(this.timeout);
26150     
26151         this.hoverState = 'out';
26152          if (!this.delay || !this.delay.hide) {
26153             this.hide();
26154             return;
26155         }
26156        
26157         var _t = this;
26158         this.timeout = setTimeout(function () {
26159             //Roo.log("leave - timeout");
26160             
26161             if (_t.hoverState == 'out') {
26162                 _t.hide();
26163                 Roo.bootstrap.Tooltip.currentEl = false;
26164             }
26165         }, delay);
26166     },
26167     
26168     show : function (msg)
26169     {
26170         if (!this.el) {
26171             this.render(document.body);
26172         }
26173         // set content.
26174         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26175         
26176         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26177         
26178         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26179         
26180         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26181         
26182         var placement = typeof this.placement == 'function' ?
26183             this.placement.call(this, this.el, on_el) :
26184             this.placement;
26185             
26186         var autoToken = /\s?auto?\s?/i;
26187         var autoPlace = autoToken.test(placement);
26188         if (autoPlace) {
26189             placement = placement.replace(autoToken, '') || 'top';
26190         }
26191         
26192         //this.el.detach()
26193         //this.el.setXY([0,0]);
26194         this.el.show();
26195         //this.el.dom.style.display='block';
26196         
26197         //this.el.appendTo(on_el);
26198         
26199         var p = this.getPosition();
26200         var box = this.el.getBox();
26201         
26202         if (autoPlace) {
26203             // fixme..
26204         }
26205         
26206         var align = this.alignment[placement];
26207         
26208         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26209         
26210         if(placement == 'top' || placement == 'bottom'){
26211             if(xy[0] < 0){
26212                 placement = 'right';
26213             }
26214             
26215             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26216                 placement = 'left';
26217             }
26218             
26219             var scroll = Roo.select('body', true).first().getScroll();
26220             
26221             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26222                 placement = 'top';
26223             }
26224             
26225             align = this.alignment[placement];
26226         }
26227         
26228         this.el.alignTo(this.bindEl, align[0],align[1]);
26229         //var arrow = this.el.select('.arrow',true).first();
26230         //arrow.set(align[2], 
26231         
26232         this.el.addClass(placement);
26233         
26234         this.el.addClass('in fade');
26235         
26236         this.hoverState = null;
26237         
26238         if (this.el.hasClass('fade')) {
26239             // fade it?
26240         }
26241         
26242     },
26243     hide : function()
26244     {
26245          
26246         if (!this.el) {
26247             return;
26248         }
26249         //this.el.setXY([0,0]);
26250         this.el.removeClass('in');
26251         //this.el.hide();
26252         
26253     }
26254     
26255 });
26256  
26257
26258  /*
26259  * - LGPL
26260  *
26261  * Location Picker
26262  * 
26263  */
26264
26265 /**
26266  * @class Roo.bootstrap.LocationPicker
26267  * @extends Roo.bootstrap.Component
26268  * Bootstrap LocationPicker class
26269  * @cfg {Number} latitude Position when init default 0
26270  * @cfg {Number} longitude Position when init default 0
26271  * @cfg {Number} zoom default 15
26272  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26273  * @cfg {Boolean} mapTypeControl default false
26274  * @cfg {Boolean} disableDoubleClickZoom default false
26275  * @cfg {Boolean} scrollwheel default true
26276  * @cfg {Boolean} streetViewControl default false
26277  * @cfg {Number} radius default 0
26278  * @cfg {String} locationName
26279  * @cfg {Boolean} draggable default true
26280  * @cfg {Boolean} enableAutocomplete default false
26281  * @cfg {Boolean} enableReverseGeocode default true
26282  * @cfg {String} markerTitle
26283  * 
26284  * @constructor
26285  * Create a new LocationPicker
26286  * @param {Object} config The config object
26287  */
26288
26289
26290 Roo.bootstrap.LocationPicker = function(config){
26291     
26292     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26293     
26294     this.addEvents({
26295         /**
26296          * @event initial
26297          * Fires when the picker initialized.
26298          * @param {Roo.bootstrap.LocationPicker} this
26299          * @param {Google Location} location
26300          */
26301         initial : true,
26302         /**
26303          * @event positionchanged
26304          * Fires when the picker position changed.
26305          * @param {Roo.bootstrap.LocationPicker} this
26306          * @param {Google Location} location
26307          */
26308         positionchanged : true,
26309         /**
26310          * @event resize
26311          * Fires when the map resize.
26312          * @param {Roo.bootstrap.LocationPicker} this
26313          */
26314         resize : true,
26315         /**
26316          * @event show
26317          * Fires when the map show.
26318          * @param {Roo.bootstrap.LocationPicker} this
26319          */
26320         show : true,
26321         /**
26322          * @event hide
26323          * Fires when the map hide.
26324          * @param {Roo.bootstrap.LocationPicker} this
26325          */
26326         hide : true,
26327         /**
26328          * @event mapClick
26329          * Fires when click the map.
26330          * @param {Roo.bootstrap.LocationPicker} this
26331          * @param {Map event} e
26332          */
26333         mapClick : true,
26334         /**
26335          * @event mapRightClick
26336          * Fires when right click the map.
26337          * @param {Roo.bootstrap.LocationPicker} this
26338          * @param {Map event} e
26339          */
26340         mapRightClick : true,
26341         /**
26342          * @event markerClick
26343          * Fires when click the marker.
26344          * @param {Roo.bootstrap.LocationPicker} this
26345          * @param {Map event} e
26346          */
26347         markerClick : true,
26348         /**
26349          * @event markerRightClick
26350          * Fires when right click the marker.
26351          * @param {Roo.bootstrap.LocationPicker} this
26352          * @param {Map event} e
26353          */
26354         markerRightClick : true,
26355         /**
26356          * @event OverlayViewDraw
26357          * Fires when OverlayView Draw
26358          * @param {Roo.bootstrap.LocationPicker} this
26359          */
26360         OverlayViewDraw : true,
26361         /**
26362          * @event OverlayViewOnAdd
26363          * Fires when OverlayView Draw
26364          * @param {Roo.bootstrap.LocationPicker} this
26365          */
26366         OverlayViewOnAdd : true,
26367         /**
26368          * @event OverlayViewOnRemove
26369          * Fires when OverlayView Draw
26370          * @param {Roo.bootstrap.LocationPicker} this
26371          */
26372         OverlayViewOnRemove : true,
26373         /**
26374          * @event OverlayViewShow
26375          * Fires when OverlayView Draw
26376          * @param {Roo.bootstrap.LocationPicker} this
26377          * @param {Pixel} cpx
26378          */
26379         OverlayViewShow : true,
26380         /**
26381          * @event OverlayViewHide
26382          * Fires when OverlayView Draw
26383          * @param {Roo.bootstrap.LocationPicker} this
26384          */
26385         OverlayViewHide : true,
26386         /**
26387          * @event loadexception
26388          * Fires when load google lib failed.
26389          * @param {Roo.bootstrap.LocationPicker} this
26390          */
26391         loadexception : true
26392     });
26393         
26394 };
26395
26396 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26397     
26398     gMapContext: false,
26399     
26400     latitude: 0,
26401     longitude: 0,
26402     zoom: 15,
26403     mapTypeId: false,
26404     mapTypeControl: false,
26405     disableDoubleClickZoom: false,
26406     scrollwheel: true,
26407     streetViewControl: false,
26408     radius: 0,
26409     locationName: '',
26410     draggable: true,
26411     enableAutocomplete: false,
26412     enableReverseGeocode: true,
26413     markerTitle: '',
26414     
26415     getAutoCreate: function()
26416     {
26417
26418         var cfg = {
26419             tag: 'div',
26420             cls: 'roo-location-picker'
26421         };
26422         
26423         return cfg
26424     },
26425     
26426     initEvents: function(ct, position)
26427     {       
26428         if(!this.el.getWidth() || this.isApplied()){
26429             return;
26430         }
26431         
26432         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26433         
26434         this.initial();
26435     },
26436     
26437     initial: function()
26438     {
26439         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26440             this.fireEvent('loadexception', this);
26441             return;
26442         }
26443         
26444         if(!this.mapTypeId){
26445             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26446         }
26447         
26448         this.gMapContext = this.GMapContext();
26449         
26450         this.initOverlayView();
26451         
26452         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26453         
26454         var _this = this;
26455                 
26456         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26457             _this.setPosition(_this.gMapContext.marker.position);
26458         });
26459         
26460         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26461             _this.fireEvent('mapClick', this, event);
26462             
26463         });
26464
26465         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26466             _this.fireEvent('mapRightClick', this, event);
26467             
26468         });
26469         
26470         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26471             _this.fireEvent('markerClick', this, event);
26472             
26473         });
26474
26475         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26476             _this.fireEvent('markerRightClick', this, event);
26477             
26478         });
26479         
26480         this.setPosition(this.gMapContext.location);
26481         
26482         this.fireEvent('initial', this, this.gMapContext.location);
26483     },
26484     
26485     initOverlayView: function()
26486     {
26487         var _this = this;
26488         
26489         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26490             
26491             draw: function()
26492             {
26493                 _this.fireEvent('OverlayViewDraw', _this);
26494             },
26495             
26496             onAdd: function()
26497             {
26498                 _this.fireEvent('OverlayViewOnAdd', _this);
26499             },
26500             
26501             onRemove: function()
26502             {
26503                 _this.fireEvent('OverlayViewOnRemove', _this);
26504             },
26505             
26506             show: function(cpx)
26507             {
26508                 _this.fireEvent('OverlayViewShow', _this, cpx);
26509             },
26510             
26511             hide: function()
26512             {
26513                 _this.fireEvent('OverlayViewHide', _this);
26514             }
26515             
26516         });
26517     },
26518     
26519     fromLatLngToContainerPixel: function(event)
26520     {
26521         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26522     },
26523     
26524     isApplied: function() 
26525     {
26526         return this.getGmapContext() == false ? false : true;
26527     },
26528     
26529     getGmapContext: function() 
26530     {
26531         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26532     },
26533     
26534     GMapContext: function() 
26535     {
26536         var position = new google.maps.LatLng(this.latitude, this.longitude);
26537         
26538         var _map = new google.maps.Map(this.el.dom, {
26539             center: position,
26540             zoom: this.zoom,
26541             mapTypeId: this.mapTypeId,
26542             mapTypeControl: this.mapTypeControl,
26543             disableDoubleClickZoom: this.disableDoubleClickZoom,
26544             scrollwheel: this.scrollwheel,
26545             streetViewControl: this.streetViewControl,
26546             locationName: this.locationName,
26547             draggable: this.draggable,
26548             enableAutocomplete: this.enableAutocomplete,
26549             enableReverseGeocode: this.enableReverseGeocode
26550         });
26551         
26552         var _marker = new google.maps.Marker({
26553             position: position,
26554             map: _map,
26555             title: this.markerTitle,
26556             draggable: this.draggable
26557         });
26558         
26559         return {
26560             map: _map,
26561             marker: _marker,
26562             circle: null,
26563             location: position,
26564             radius: this.radius,
26565             locationName: this.locationName,
26566             addressComponents: {
26567                 formatted_address: null,
26568                 addressLine1: null,
26569                 addressLine2: null,
26570                 streetName: null,
26571                 streetNumber: null,
26572                 city: null,
26573                 district: null,
26574                 state: null,
26575                 stateOrProvince: null
26576             },
26577             settings: this,
26578             domContainer: this.el.dom,
26579             geodecoder: new google.maps.Geocoder()
26580         };
26581     },
26582     
26583     drawCircle: function(center, radius, options) 
26584     {
26585         if (this.gMapContext.circle != null) {
26586             this.gMapContext.circle.setMap(null);
26587         }
26588         if (radius > 0) {
26589             radius *= 1;
26590             options = Roo.apply({}, options, {
26591                 strokeColor: "#0000FF",
26592                 strokeOpacity: .35,
26593                 strokeWeight: 2,
26594                 fillColor: "#0000FF",
26595                 fillOpacity: .2
26596             });
26597             
26598             options.map = this.gMapContext.map;
26599             options.radius = radius;
26600             options.center = center;
26601             this.gMapContext.circle = new google.maps.Circle(options);
26602             return this.gMapContext.circle;
26603         }
26604         
26605         return null;
26606     },
26607     
26608     setPosition: function(location) 
26609     {
26610         this.gMapContext.location = location;
26611         this.gMapContext.marker.setPosition(location);
26612         this.gMapContext.map.panTo(location);
26613         this.drawCircle(location, this.gMapContext.radius, {});
26614         
26615         var _this = this;
26616         
26617         if (this.gMapContext.settings.enableReverseGeocode) {
26618             this.gMapContext.geodecoder.geocode({
26619                 latLng: this.gMapContext.location
26620             }, function(results, status) {
26621                 
26622                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26623                     _this.gMapContext.locationName = results[0].formatted_address;
26624                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26625                     
26626                     _this.fireEvent('positionchanged', this, location);
26627                 }
26628             });
26629             
26630             return;
26631         }
26632         
26633         this.fireEvent('positionchanged', this, location);
26634     },
26635     
26636     resize: function()
26637     {
26638         google.maps.event.trigger(this.gMapContext.map, "resize");
26639         
26640         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26641         
26642         this.fireEvent('resize', this);
26643     },
26644     
26645     setPositionByLatLng: function(latitude, longitude)
26646     {
26647         this.setPosition(new google.maps.LatLng(latitude, longitude));
26648     },
26649     
26650     getCurrentPosition: function() 
26651     {
26652         return {
26653             latitude: this.gMapContext.location.lat(),
26654             longitude: this.gMapContext.location.lng()
26655         };
26656     },
26657     
26658     getAddressName: function() 
26659     {
26660         return this.gMapContext.locationName;
26661     },
26662     
26663     getAddressComponents: function() 
26664     {
26665         return this.gMapContext.addressComponents;
26666     },
26667     
26668     address_component_from_google_geocode: function(address_components) 
26669     {
26670         var result = {};
26671         
26672         for (var i = 0; i < address_components.length; i++) {
26673             var component = address_components[i];
26674             if (component.types.indexOf("postal_code") >= 0) {
26675                 result.postalCode = component.short_name;
26676             } else if (component.types.indexOf("street_number") >= 0) {
26677                 result.streetNumber = component.short_name;
26678             } else if (component.types.indexOf("route") >= 0) {
26679                 result.streetName = component.short_name;
26680             } else if (component.types.indexOf("neighborhood") >= 0) {
26681                 result.city = component.short_name;
26682             } else if (component.types.indexOf("locality") >= 0) {
26683                 result.city = component.short_name;
26684             } else if (component.types.indexOf("sublocality") >= 0) {
26685                 result.district = component.short_name;
26686             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26687                 result.stateOrProvince = component.short_name;
26688             } else if (component.types.indexOf("country") >= 0) {
26689                 result.country = component.short_name;
26690             }
26691         }
26692         
26693         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26694         result.addressLine2 = "";
26695         return result;
26696     },
26697     
26698     setZoomLevel: function(zoom)
26699     {
26700         this.gMapContext.map.setZoom(zoom);
26701     },
26702     
26703     show: function()
26704     {
26705         if(!this.el){
26706             return;
26707         }
26708         
26709         this.el.show();
26710         
26711         this.resize();
26712         
26713         this.fireEvent('show', this);
26714     },
26715     
26716     hide: function()
26717     {
26718         if(!this.el){
26719             return;
26720         }
26721         
26722         this.el.hide();
26723         
26724         this.fireEvent('hide', this);
26725     }
26726     
26727 });
26728
26729 Roo.apply(Roo.bootstrap.LocationPicker, {
26730     
26731     OverlayView : function(map, options)
26732     {
26733         options = options || {};
26734         
26735         this.setMap(map);
26736     }
26737     
26738     
26739 });/*
26740  * - LGPL
26741  *
26742  * Alert
26743  * 
26744  */
26745
26746 /**
26747  * @class Roo.bootstrap.Alert
26748  * @extends Roo.bootstrap.Component
26749  * Bootstrap Alert class
26750  * @cfg {String} title The title of alert
26751  * @cfg {String} html The content of alert
26752  * @cfg {String} weight (  success | info | warning | danger )
26753  * @cfg {String} faicon font-awesomeicon
26754  * 
26755  * @constructor
26756  * Create a new alert
26757  * @param {Object} config The config object
26758  */
26759
26760
26761 Roo.bootstrap.Alert = function(config){
26762     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26763     
26764 };
26765
26766 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26767     
26768     title: '',
26769     html: '',
26770     weight: false,
26771     faicon: false,
26772     
26773     getAutoCreate : function()
26774     {
26775         
26776         var cfg = {
26777             tag : 'div',
26778             cls : 'alert',
26779             cn : [
26780                 {
26781                     tag : 'i',
26782                     cls : 'roo-alert-icon'
26783                     
26784                 },
26785                 {
26786                     tag : 'b',
26787                     cls : 'roo-alert-title',
26788                     html : this.title
26789                 },
26790                 {
26791                     tag : 'span',
26792                     cls : 'roo-alert-text',
26793                     html : this.html
26794                 }
26795             ]
26796         };
26797         
26798         if(this.faicon){
26799             cfg.cn[0].cls += ' fa ' + this.faicon;
26800         }
26801         
26802         if(this.weight){
26803             cfg.cls += ' alert-' + this.weight;
26804         }
26805         
26806         return cfg;
26807     },
26808     
26809     initEvents: function() 
26810     {
26811         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26812     },
26813     
26814     setTitle : function(str)
26815     {
26816         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26817     },
26818     
26819     setText : function(str)
26820     {
26821         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26822     },
26823     
26824     setWeight : function(weight)
26825     {
26826         if(this.weight){
26827             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26828         }
26829         
26830         this.weight = weight;
26831         
26832         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26833     },
26834     
26835     setIcon : function(icon)
26836     {
26837         if(this.faicon){
26838             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26839         }
26840         
26841         this.faicon = icon;
26842         
26843         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26844     },
26845     
26846     hide: function() 
26847     {
26848         this.el.hide();   
26849     },
26850     
26851     show: function() 
26852     {  
26853         this.el.show();   
26854     }
26855     
26856 });
26857
26858  
26859 /*
26860 * Licence: LGPL
26861 */
26862
26863 /**
26864  * @class Roo.bootstrap.UploadCropbox
26865  * @extends Roo.bootstrap.Component
26866  * Bootstrap UploadCropbox class
26867  * @cfg {String} emptyText show when image has been loaded
26868  * @cfg {String} rotateNotify show when image too small to rotate
26869  * @cfg {Number} errorTimeout default 3000
26870  * @cfg {Number} minWidth default 300
26871  * @cfg {Number} minHeight default 300
26872  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26873  * @cfg {Boolean} isDocument (true|false) default false
26874  * @cfg {String} url action url
26875  * @cfg {String} paramName default 'imageUpload'
26876  * @cfg {String} method default POST
26877  * @cfg {Boolean} loadMask (true|false) default true
26878  * @cfg {Boolean} loadingText default 'Loading...'
26879  * 
26880  * @constructor
26881  * Create a new UploadCropbox
26882  * @param {Object} config The config object
26883  */
26884
26885 Roo.bootstrap.UploadCropbox = function(config){
26886     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26887     
26888     this.addEvents({
26889         /**
26890          * @event beforeselectfile
26891          * Fire before select file
26892          * @param {Roo.bootstrap.UploadCropbox} this
26893          */
26894         "beforeselectfile" : true,
26895         /**
26896          * @event initial
26897          * Fire after initEvent
26898          * @param {Roo.bootstrap.UploadCropbox} this
26899          */
26900         "initial" : true,
26901         /**
26902          * @event crop
26903          * Fire after initEvent
26904          * @param {Roo.bootstrap.UploadCropbox} this
26905          * @param {String} data
26906          */
26907         "crop" : true,
26908         /**
26909          * @event prepare
26910          * Fire when preparing the file data
26911          * @param {Roo.bootstrap.UploadCropbox} this
26912          * @param {Object} file
26913          */
26914         "prepare" : true,
26915         /**
26916          * @event exception
26917          * Fire when get exception
26918          * @param {Roo.bootstrap.UploadCropbox} this
26919          * @param {XMLHttpRequest} xhr
26920          */
26921         "exception" : true,
26922         /**
26923          * @event beforeloadcanvas
26924          * Fire before load the canvas
26925          * @param {Roo.bootstrap.UploadCropbox} this
26926          * @param {String} src
26927          */
26928         "beforeloadcanvas" : true,
26929         /**
26930          * @event trash
26931          * Fire when trash image
26932          * @param {Roo.bootstrap.UploadCropbox} this
26933          */
26934         "trash" : true,
26935         /**
26936          * @event download
26937          * Fire when download the image
26938          * @param {Roo.bootstrap.UploadCropbox} this
26939          */
26940         "download" : true,
26941         /**
26942          * @event footerbuttonclick
26943          * Fire when footerbuttonclick
26944          * @param {Roo.bootstrap.UploadCropbox} this
26945          * @param {String} type
26946          */
26947         "footerbuttonclick" : true,
26948         /**
26949          * @event resize
26950          * Fire when resize
26951          * @param {Roo.bootstrap.UploadCropbox} this
26952          */
26953         "resize" : true,
26954         /**
26955          * @event rotate
26956          * Fire when rotate the image
26957          * @param {Roo.bootstrap.UploadCropbox} this
26958          * @param {String} pos
26959          */
26960         "rotate" : true,
26961         /**
26962          * @event inspect
26963          * Fire when inspect the file
26964          * @param {Roo.bootstrap.UploadCropbox} this
26965          * @param {Object} file
26966          */
26967         "inspect" : true,
26968         /**
26969          * @event upload
26970          * Fire when xhr upload the file
26971          * @param {Roo.bootstrap.UploadCropbox} this
26972          * @param {Object} data
26973          */
26974         "upload" : true,
26975         /**
26976          * @event arrange
26977          * Fire when arrange the file data
26978          * @param {Roo.bootstrap.UploadCropbox} this
26979          * @param {Object} formData
26980          */
26981         "arrange" : true
26982     });
26983     
26984     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26985 };
26986
26987 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26988     
26989     emptyText : 'Click to upload image',
26990     rotateNotify : 'Image is too small to rotate',
26991     errorTimeout : 3000,
26992     scale : 0,
26993     baseScale : 1,
26994     rotate : 0,
26995     dragable : false,
26996     pinching : false,
26997     mouseX : 0,
26998     mouseY : 0,
26999     cropData : false,
27000     minWidth : 300,
27001     minHeight : 300,
27002     file : false,
27003     exif : {},
27004     baseRotate : 1,
27005     cropType : 'image/jpeg',
27006     buttons : false,
27007     canvasLoaded : false,
27008     isDocument : false,
27009     method : 'POST',
27010     paramName : 'imageUpload',
27011     loadMask : true,
27012     loadingText : 'Loading...',
27013     maskEl : false,
27014     
27015     getAutoCreate : function()
27016     {
27017         var cfg = {
27018             tag : 'div',
27019             cls : 'roo-upload-cropbox',
27020             cn : [
27021                 {
27022                     tag : 'input',
27023                     cls : 'roo-upload-cropbox-selector',
27024                     type : 'file'
27025                 },
27026                 {
27027                     tag : 'div',
27028                     cls : 'roo-upload-cropbox-body',
27029                     style : 'cursor:pointer',
27030                     cn : [
27031                         {
27032                             tag : 'div',
27033                             cls : 'roo-upload-cropbox-preview'
27034                         },
27035                         {
27036                             tag : 'div',
27037                             cls : 'roo-upload-cropbox-thumb'
27038                         },
27039                         {
27040                             tag : 'div',
27041                             cls : 'roo-upload-cropbox-empty-notify',
27042                             html : this.emptyText
27043                         },
27044                         {
27045                             tag : 'div',
27046                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27047                             html : this.rotateNotify
27048                         }
27049                     ]
27050                 },
27051                 {
27052                     tag : 'div',
27053                     cls : 'roo-upload-cropbox-footer',
27054                     cn : {
27055                         tag : 'div',
27056                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27057                         cn : []
27058                     }
27059                 }
27060             ]
27061         };
27062         
27063         return cfg;
27064     },
27065     
27066     onRender : function(ct, position)
27067     {
27068         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27069         
27070         if (this.buttons.length) {
27071             
27072             Roo.each(this.buttons, function(bb) {
27073                 
27074                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27075                 
27076                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27077                 
27078             }, this);
27079         }
27080         
27081         if(this.loadMask){
27082             this.maskEl = this.el;
27083         }
27084     },
27085     
27086     initEvents : function()
27087     {
27088         this.urlAPI = (window.createObjectURL && window) || 
27089                                 (window.URL && URL.revokeObjectURL && URL) || 
27090                                 (window.webkitURL && webkitURL);
27091                         
27092         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27093         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27094         
27095         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27096         this.selectorEl.hide();
27097         
27098         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27099         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27100         
27101         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27102         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27103         this.thumbEl.hide();
27104         
27105         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27106         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27107         
27108         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27109         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27110         this.errorEl.hide();
27111         
27112         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27113         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27114         this.footerEl.hide();
27115         
27116         this.setThumbBoxSize();
27117         
27118         this.bind();
27119         
27120         this.resize();
27121         
27122         this.fireEvent('initial', this);
27123     },
27124
27125     bind : function()
27126     {
27127         var _this = this;
27128         
27129         window.addEventListener("resize", function() { _this.resize(); } );
27130         
27131         this.bodyEl.on('click', this.beforeSelectFile, this);
27132         
27133         if(Roo.isTouch){
27134             this.bodyEl.on('touchstart', this.onTouchStart, this);
27135             this.bodyEl.on('touchmove', this.onTouchMove, this);
27136             this.bodyEl.on('touchend', this.onTouchEnd, this);
27137         }
27138         
27139         if(!Roo.isTouch){
27140             this.bodyEl.on('mousedown', this.onMouseDown, this);
27141             this.bodyEl.on('mousemove', this.onMouseMove, this);
27142             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27143             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27144             Roo.get(document).on('mouseup', this.onMouseUp, this);
27145         }
27146         
27147         this.selectorEl.on('change', this.onFileSelected, this);
27148     },
27149     
27150     reset : function()
27151     {    
27152         this.scale = 0;
27153         this.baseScale = 1;
27154         this.rotate = 0;
27155         this.baseRotate = 1;
27156         this.dragable = false;
27157         this.pinching = false;
27158         this.mouseX = 0;
27159         this.mouseY = 0;
27160         this.cropData = false;
27161         this.notifyEl.dom.innerHTML = this.emptyText;
27162         
27163         this.selectorEl.dom.value = '';
27164         
27165     },
27166     
27167     resize : function()
27168     {
27169         if(this.fireEvent('resize', this) != false){
27170             this.setThumbBoxPosition();
27171             this.setCanvasPosition();
27172         }
27173     },
27174     
27175     onFooterButtonClick : function(e, el, o, type)
27176     {
27177         switch (type) {
27178             case 'rotate-left' :
27179                 this.onRotateLeft(e);
27180                 break;
27181             case 'rotate-right' :
27182                 this.onRotateRight(e);
27183                 break;
27184             case 'picture' :
27185                 this.beforeSelectFile(e);
27186                 break;
27187             case 'trash' :
27188                 this.trash(e);
27189                 break;
27190             case 'crop' :
27191                 this.crop(e);
27192                 break;
27193             case 'download' :
27194                 this.download(e);
27195                 break;
27196             default :
27197                 break;
27198         }
27199         
27200         this.fireEvent('footerbuttonclick', this, type);
27201     },
27202     
27203     beforeSelectFile : function(e)
27204     {
27205         e.preventDefault();
27206         
27207         if(this.fireEvent('beforeselectfile', this) != false){
27208             this.selectorEl.dom.click();
27209         }
27210     },
27211     
27212     onFileSelected : function(e)
27213     {
27214         e.preventDefault();
27215         
27216         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27217             return;
27218         }
27219         
27220         var file = this.selectorEl.dom.files[0];
27221         
27222         if(this.fireEvent('inspect', this, file) != false){
27223             this.prepare(file);
27224         }
27225         
27226     },
27227     
27228     trash : function(e)
27229     {
27230         this.fireEvent('trash', this);
27231     },
27232     
27233     download : function(e)
27234     {
27235         this.fireEvent('download', this);
27236     },
27237     
27238     loadCanvas : function(src)
27239     {   
27240         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27241             
27242             this.reset();
27243             
27244             this.imageEl = document.createElement('img');
27245             
27246             var _this = this;
27247             
27248             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27249             
27250             this.imageEl.src = src;
27251         }
27252     },
27253     
27254     onLoadCanvas : function()
27255     {   
27256         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27257         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27258         
27259         this.bodyEl.un('click', this.beforeSelectFile, this);
27260         
27261         this.notifyEl.hide();
27262         this.thumbEl.show();
27263         this.footerEl.show();
27264         
27265         this.baseRotateLevel();
27266         
27267         if(this.isDocument){
27268             this.setThumbBoxSize();
27269         }
27270         
27271         this.setThumbBoxPosition();
27272         
27273         this.baseScaleLevel();
27274         
27275         this.draw();
27276         
27277         this.resize();
27278         
27279         this.canvasLoaded = true;
27280         
27281         if(this.loadMask){
27282             this.maskEl.unmask();
27283         }
27284         
27285     },
27286     
27287     setCanvasPosition : function()
27288     {   
27289         if(!this.canvasEl){
27290             return;
27291         }
27292         
27293         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27294         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27295         
27296         this.previewEl.setLeft(pw);
27297         this.previewEl.setTop(ph);
27298         
27299     },
27300     
27301     onMouseDown : function(e)
27302     {   
27303         e.stopEvent();
27304         
27305         this.dragable = true;
27306         this.pinching = false;
27307         
27308         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27309             this.dragable = false;
27310             return;
27311         }
27312         
27313         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27314         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27315         
27316     },
27317     
27318     onMouseMove : function(e)
27319     {   
27320         e.stopEvent();
27321         
27322         if(!this.canvasLoaded){
27323             return;
27324         }
27325         
27326         if (!this.dragable){
27327             return;
27328         }
27329         
27330         var minX = Math.ceil(this.thumbEl.getLeft(true));
27331         var minY = Math.ceil(this.thumbEl.getTop(true));
27332         
27333         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27334         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27335         
27336         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27337         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27338         
27339         x = x - this.mouseX;
27340         y = y - this.mouseY;
27341         
27342         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27343         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27344         
27345         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27346         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27347         
27348         this.previewEl.setLeft(bgX);
27349         this.previewEl.setTop(bgY);
27350         
27351         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27352         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27353     },
27354     
27355     onMouseUp : function(e)
27356     {   
27357         e.stopEvent();
27358         
27359         this.dragable = false;
27360     },
27361     
27362     onMouseWheel : function(e)
27363     {   
27364         e.stopEvent();
27365         
27366         this.startScale = this.scale;
27367         
27368         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27369         
27370         if(!this.zoomable()){
27371             this.scale = this.startScale;
27372             return;
27373         }
27374         
27375         this.draw();
27376         
27377         return;
27378     },
27379     
27380     zoomable : function()
27381     {
27382         var minScale = this.thumbEl.getWidth() / this.minWidth;
27383         
27384         if(this.minWidth < this.minHeight){
27385             minScale = this.thumbEl.getHeight() / this.minHeight;
27386         }
27387         
27388         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27389         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27390         
27391         if(
27392                 this.isDocument &&
27393                 (this.rotate == 0 || this.rotate == 180) && 
27394                 (
27395                     width > this.imageEl.OriginWidth || 
27396                     height > this.imageEl.OriginHeight ||
27397                     (width < this.minWidth && height < this.minHeight)
27398                 )
27399         ){
27400             return false;
27401         }
27402         
27403         if(
27404                 this.isDocument &&
27405                 (this.rotate == 90 || this.rotate == 270) && 
27406                 (
27407                     width > this.imageEl.OriginWidth || 
27408                     height > this.imageEl.OriginHeight ||
27409                     (width < this.minHeight && height < this.minWidth)
27410                 )
27411         ){
27412             return false;
27413         }
27414         
27415         if(
27416                 !this.isDocument &&
27417                 (this.rotate == 0 || this.rotate == 180) && 
27418                 (
27419                     width < this.minWidth || 
27420                     width > this.imageEl.OriginWidth || 
27421                     height < this.minHeight || 
27422                     height > this.imageEl.OriginHeight
27423                 )
27424         ){
27425             return false;
27426         }
27427         
27428         if(
27429                 !this.isDocument &&
27430                 (this.rotate == 90 || this.rotate == 270) && 
27431                 (
27432                     width < this.minHeight || 
27433                     width > this.imageEl.OriginWidth || 
27434                     height < this.minWidth || 
27435                     height > this.imageEl.OriginHeight
27436                 )
27437         ){
27438             return false;
27439         }
27440         
27441         return true;
27442         
27443     },
27444     
27445     onRotateLeft : function(e)
27446     {   
27447         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27448             
27449             var minScale = this.thumbEl.getWidth() / this.minWidth;
27450             
27451             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27452             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27453             
27454             this.startScale = this.scale;
27455             
27456             while (this.getScaleLevel() < minScale){
27457             
27458                 this.scale = this.scale + 1;
27459                 
27460                 if(!this.zoomable()){
27461                     break;
27462                 }
27463                 
27464                 if(
27465                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27466                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27467                 ){
27468                     continue;
27469                 }
27470                 
27471                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27472
27473                 this.draw();
27474                 
27475                 return;
27476             }
27477             
27478             this.scale = this.startScale;
27479             
27480             this.onRotateFail();
27481             
27482             return false;
27483         }
27484         
27485         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27486
27487         if(this.isDocument){
27488             this.setThumbBoxSize();
27489             this.setThumbBoxPosition();
27490             this.setCanvasPosition();
27491         }
27492         
27493         this.draw();
27494         
27495         this.fireEvent('rotate', this, 'left');
27496         
27497     },
27498     
27499     onRotateRight : function(e)
27500     {
27501         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27502             
27503             var minScale = this.thumbEl.getWidth() / this.minWidth;
27504         
27505             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27506             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27507             
27508             this.startScale = this.scale;
27509             
27510             while (this.getScaleLevel() < minScale){
27511             
27512                 this.scale = this.scale + 1;
27513                 
27514                 if(!this.zoomable()){
27515                     break;
27516                 }
27517                 
27518                 if(
27519                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27520                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27521                 ){
27522                     continue;
27523                 }
27524                 
27525                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27526
27527                 this.draw();
27528                 
27529                 return;
27530             }
27531             
27532             this.scale = this.startScale;
27533             
27534             this.onRotateFail();
27535             
27536             return false;
27537         }
27538         
27539         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27540
27541         if(this.isDocument){
27542             this.setThumbBoxSize();
27543             this.setThumbBoxPosition();
27544             this.setCanvasPosition();
27545         }
27546         
27547         this.draw();
27548         
27549         this.fireEvent('rotate', this, 'right');
27550     },
27551     
27552     onRotateFail : function()
27553     {
27554         this.errorEl.show(true);
27555         
27556         var _this = this;
27557         
27558         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27559     },
27560     
27561     draw : function()
27562     {
27563         this.previewEl.dom.innerHTML = '';
27564         
27565         var canvasEl = document.createElement("canvas");
27566         
27567         var contextEl = canvasEl.getContext("2d");
27568         
27569         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27570         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27571         var center = this.imageEl.OriginWidth / 2;
27572         
27573         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27574             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27575             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27576             center = this.imageEl.OriginHeight / 2;
27577         }
27578         
27579         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27580         
27581         contextEl.translate(center, center);
27582         contextEl.rotate(this.rotate * Math.PI / 180);
27583
27584         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27585         
27586         this.canvasEl = document.createElement("canvas");
27587         
27588         this.contextEl = this.canvasEl.getContext("2d");
27589         
27590         switch (this.rotate) {
27591             case 0 :
27592                 
27593                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27594                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27595                 
27596                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27597                 
27598                 break;
27599             case 90 : 
27600                 
27601                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27602                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27603                 
27604                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27605                     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);
27606                     break;
27607                 }
27608                 
27609                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27610                 
27611                 break;
27612             case 180 :
27613                 
27614                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27615                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27616                 
27617                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27618                     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);
27619                     break;
27620                 }
27621                 
27622                 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);
27623                 
27624                 break;
27625             case 270 :
27626                 
27627                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27628                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27629         
27630                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27631                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27632                     break;
27633                 }
27634                 
27635                 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);
27636                 
27637                 break;
27638             default : 
27639                 break;
27640         }
27641         
27642         this.previewEl.appendChild(this.canvasEl);
27643         
27644         this.setCanvasPosition();
27645     },
27646     
27647     crop : function()
27648     {
27649         if(!this.canvasLoaded){
27650             return;
27651         }
27652         
27653         var imageCanvas = document.createElement("canvas");
27654         
27655         var imageContext = imageCanvas.getContext("2d");
27656         
27657         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27658         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27659         
27660         var center = imageCanvas.width / 2;
27661         
27662         imageContext.translate(center, center);
27663         
27664         imageContext.rotate(this.rotate * Math.PI / 180);
27665         
27666         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27667         
27668         var canvas = document.createElement("canvas");
27669         
27670         var context = canvas.getContext("2d");
27671                 
27672         canvas.width = this.minWidth;
27673         canvas.height = this.minHeight;
27674
27675         switch (this.rotate) {
27676             case 0 :
27677                 
27678                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27679                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27680                 
27681                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27682                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27683                 
27684                 var targetWidth = this.minWidth - 2 * x;
27685                 var targetHeight = this.minHeight - 2 * y;
27686                 
27687                 var scale = 1;
27688                 
27689                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27690                     scale = targetWidth / width;
27691                 }
27692                 
27693                 if(x > 0 && y == 0){
27694                     scale = targetHeight / height;
27695                 }
27696                 
27697                 if(x > 0 && y > 0){
27698                     scale = targetWidth / width;
27699                     
27700                     if(width < height){
27701                         scale = targetHeight / height;
27702                     }
27703                 }
27704                 
27705                 context.scale(scale, scale);
27706                 
27707                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27708                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27709
27710                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27711                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27712
27713                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27714                 
27715                 break;
27716             case 90 : 
27717                 
27718                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27719                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27720                 
27721                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27722                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27723                 
27724                 var targetWidth = this.minWidth - 2 * x;
27725                 var targetHeight = this.minHeight - 2 * y;
27726                 
27727                 var scale = 1;
27728                 
27729                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27730                     scale = targetWidth / width;
27731                 }
27732                 
27733                 if(x > 0 && y == 0){
27734                     scale = targetHeight / height;
27735                 }
27736                 
27737                 if(x > 0 && y > 0){
27738                     scale = targetWidth / width;
27739                     
27740                     if(width < height){
27741                         scale = targetHeight / height;
27742                     }
27743                 }
27744                 
27745                 context.scale(scale, scale);
27746                 
27747                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27748                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27749
27750                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27751                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27752                 
27753                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27754                 
27755                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27756                 
27757                 break;
27758             case 180 :
27759                 
27760                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27761                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27762                 
27763                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27764                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27765                 
27766                 var targetWidth = this.minWidth - 2 * x;
27767                 var targetHeight = this.minHeight - 2 * y;
27768                 
27769                 var scale = 1;
27770                 
27771                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27772                     scale = targetWidth / width;
27773                 }
27774                 
27775                 if(x > 0 && y == 0){
27776                     scale = targetHeight / height;
27777                 }
27778                 
27779                 if(x > 0 && y > 0){
27780                     scale = targetWidth / width;
27781                     
27782                     if(width < height){
27783                         scale = targetHeight / height;
27784                     }
27785                 }
27786                 
27787                 context.scale(scale, scale);
27788                 
27789                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27790                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27791
27792                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27793                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27794
27795                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27796                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27797                 
27798                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27799                 
27800                 break;
27801             case 270 :
27802                 
27803                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27804                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27805                 
27806                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27807                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27808                 
27809                 var targetWidth = this.minWidth - 2 * x;
27810                 var targetHeight = this.minHeight - 2 * y;
27811                 
27812                 var scale = 1;
27813                 
27814                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27815                     scale = targetWidth / width;
27816                 }
27817                 
27818                 if(x > 0 && y == 0){
27819                     scale = targetHeight / height;
27820                 }
27821                 
27822                 if(x > 0 && y > 0){
27823                     scale = targetWidth / width;
27824                     
27825                     if(width < height){
27826                         scale = targetHeight / height;
27827                     }
27828                 }
27829                 
27830                 context.scale(scale, scale);
27831                 
27832                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27833                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27834
27835                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27836                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27837                 
27838                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27839                 
27840                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27841                 
27842                 break;
27843             default : 
27844                 break;
27845         }
27846         
27847         this.cropData = canvas.toDataURL(this.cropType);
27848         
27849         if(this.fireEvent('crop', this, this.cropData) !== false){
27850             this.process(this.file, this.cropData);
27851         }
27852         
27853         return;
27854         
27855     },
27856     
27857     setThumbBoxSize : function()
27858     {
27859         var width, height;
27860         
27861         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27862             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27863             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27864             
27865             this.minWidth = width;
27866             this.minHeight = height;
27867             
27868             if(this.rotate == 90 || this.rotate == 270){
27869                 this.minWidth = height;
27870                 this.minHeight = width;
27871             }
27872         }
27873         
27874         height = 300;
27875         width = Math.ceil(this.minWidth * height / this.minHeight);
27876         
27877         if(this.minWidth > this.minHeight){
27878             width = 300;
27879             height = Math.ceil(this.minHeight * width / this.minWidth);
27880         }
27881         
27882         this.thumbEl.setStyle({
27883             width : width + 'px',
27884             height : height + 'px'
27885         });
27886
27887         return;
27888             
27889     },
27890     
27891     setThumbBoxPosition : function()
27892     {
27893         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27894         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27895         
27896         this.thumbEl.setLeft(x);
27897         this.thumbEl.setTop(y);
27898         
27899     },
27900     
27901     baseRotateLevel : function()
27902     {
27903         this.baseRotate = 1;
27904         
27905         if(
27906                 typeof(this.exif) != 'undefined' &&
27907                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27908                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27909         ){
27910             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27911         }
27912         
27913         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27914         
27915     },
27916     
27917     baseScaleLevel : function()
27918     {
27919         var width, height;
27920         
27921         if(this.isDocument){
27922             
27923             if(this.baseRotate == 6 || this.baseRotate == 8){
27924             
27925                 height = this.thumbEl.getHeight();
27926                 this.baseScale = height / this.imageEl.OriginWidth;
27927
27928                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27929                     width = this.thumbEl.getWidth();
27930                     this.baseScale = width / this.imageEl.OriginHeight;
27931                 }
27932
27933                 return;
27934             }
27935
27936             height = this.thumbEl.getHeight();
27937             this.baseScale = height / this.imageEl.OriginHeight;
27938
27939             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27940                 width = this.thumbEl.getWidth();
27941                 this.baseScale = width / this.imageEl.OriginWidth;
27942             }
27943
27944             return;
27945         }
27946         
27947         if(this.baseRotate == 6 || this.baseRotate == 8){
27948             
27949             width = this.thumbEl.getHeight();
27950             this.baseScale = width / this.imageEl.OriginHeight;
27951             
27952             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27953                 height = this.thumbEl.getWidth();
27954                 this.baseScale = height / this.imageEl.OriginHeight;
27955             }
27956             
27957             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27958                 height = this.thumbEl.getWidth();
27959                 this.baseScale = height / this.imageEl.OriginHeight;
27960                 
27961                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27962                     width = this.thumbEl.getHeight();
27963                     this.baseScale = width / this.imageEl.OriginWidth;
27964                 }
27965             }
27966             
27967             return;
27968         }
27969         
27970         width = this.thumbEl.getWidth();
27971         this.baseScale = width / this.imageEl.OriginWidth;
27972         
27973         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27974             height = this.thumbEl.getHeight();
27975             this.baseScale = height / this.imageEl.OriginHeight;
27976         }
27977         
27978         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27979             
27980             height = this.thumbEl.getHeight();
27981             this.baseScale = height / this.imageEl.OriginHeight;
27982             
27983             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27984                 width = this.thumbEl.getWidth();
27985                 this.baseScale = width / this.imageEl.OriginWidth;
27986             }
27987             
27988         }
27989         
27990         return;
27991     },
27992     
27993     getScaleLevel : function()
27994     {
27995         return this.baseScale * Math.pow(1.1, this.scale);
27996     },
27997     
27998     onTouchStart : function(e)
27999     {
28000         if(!this.canvasLoaded){
28001             this.beforeSelectFile(e);
28002             return;
28003         }
28004         
28005         var touches = e.browserEvent.touches;
28006         
28007         if(!touches){
28008             return;
28009         }
28010         
28011         if(touches.length == 1){
28012             this.onMouseDown(e);
28013             return;
28014         }
28015         
28016         if(touches.length != 2){
28017             return;
28018         }
28019         
28020         var coords = [];
28021         
28022         for(var i = 0, finger; finger = touches[i]; i++){
28023             coords.push(finger.pageX, finger.pageY);
28024         }
28025         
28026         var x = Math.pow(coords[0] - coords[2], 2);
28027         var y = Math.pow(coords[1] - coords[3], 2);
28028         
28029         this.startDistance = Math.sqrt(x + y);
28030         
28031         this.startScale = this.scale;
28032         
28033         this.pinching = true;
28034         this.dragable = false;
28035         
28036     },
28037     
28038     onTouchMove : function(e)
28039     {
28040         if(!this.pinching && !this.dragable){
28041             return;
28042         }
28043         
28044         var touches = e.browserEvent.touches;
28045         
28046         if(!touches){
28047             return;
28048         }
28049         
28050         if(this.dragable){
28051             this.onMouseMove(e);
28052             return;
28053         }
28054         
28055         var coords = [];
28056         
28057         for(var i = 0, finger; finger = touches[i]; i++){
28058             coords.push(finger.pageX, finger.pageY);
28059         }
28060         
28061         var x = Math.pow(coords[0] - coords[2], 2);
28062         var y = Math.pow(coords[1] - coords[3], 2);
28063         
28064         this.endDistance = Math.sqrt(x + y);
28065         
28066         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28067         
28068         if(!this.zoomable()){
28069             this.scale = this.startScale;
28070             return;
28071         }
28072         
28073         this.draw();
28074         
28075     },
28076     
28077     onTouchEnd : function(e)
28078     {
28079         this.pinching = false;
28080         this.dragable = false;
28081         
28082     },
28083     
28084     process : function(file, crop)
28085     {
28086         if(this.loadMask){
28087             this.maskEl.mask(this.loadingText);
28088         }
28089         
28090         this.xhr = new XMLHttpRequest();
28091         
28092         file.xhr = this.xhr;
28093
28094         this.xhr.open(this.method, this.url, true);
28095         
28096         var headers = {
28097             "Accept": "application/json",
28098             "Cache-Control": "no-cache",
28099             "X-Requested-With": "XMLHttpRequest"
28100         };
28101         
28102         for (var headerName in headers) {
28103             var headerValue = headers[headerName];
28104             if (headerValue) {
28105                 this.xhr.setRequestHeader(headerName, headerValue);
28106             }
28107         }
28108         
28109         var _this = this;
28110         
28111         this.xhr.onload = function()
28112         {
28113             _this.xhrOnLoad(_this.xhr);
28114         }
28115         
28116         this.xhr.onerror = function()
28117         {
28118             _this.xhrOnError(_this.xhr);
28119         }
28120         
28121         var formData = new FormData();
28122
28123         formData.append('returnHTML', 'NO');
28124         
28125         if(crop){
28126             formData.append('crop', crop);
28127         }
28128         
28129         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28130             formData.append(this.paramName, file, file.name);
28131         }
28132         
28133         if(typeof(file.filename) != 'undefined'){
28134             formData.append('filename', file.filename);
28135         }
28136         
28137         if(typeof(file.mimetype) != 'undefined'){
28138             formData.append('mimetype', file.mimetype);
28139         }
28140         
28141         if(this.fireEvent('arrange', this, formData) != false){
28142             this.xhr.send(formData);
28143         };
28144     },
28145     
28146     xhrOnLoad : function(xhr)
28147     {
28148         if(this.loadMask){
28149             this.maskEl.unmask();
28150         }
28151         
28152         if (xhr.readyState !== 4) {
28153             this.fireEvent('exception', this, xhr);
28154             return;
28155         }
28156
28157         var response = Roo.decode(xhr.responseText);
28158         
28159         if(!response.success){
28160             this.fireEvent('exception', this, xhr);
28161             return;
28162         }
28163         
28164         var response = Roo.decode(xhr.responseText);
28165         
28166         this.fireEvent('upload', this, response);
28167         
28168     },
28169     
28170     xhrOnError : function()
28171     {
28172         if(this.loadMask){
28173             this.maskEl.unmask();
28174         }
28175         
28176         Roo.log('xhr on error');
28177         
28178         var response = Roo.decode(xhr.responseText);
28179           
28180         Roo.log(response);
28181         
28182     },
28183     
28184     prepare : function(file)
28185     {   
28186         if(this.loadMask){
28187             this.maskEl.mask(this.loadingText);
28188         }
28189         
28190         this.file = false;
28191         this.exif = {};
28192         
28193         if(typeof(file) === 'string'){
28194             this.loadCanvas(file);
28195             return;
28196         }
28197         
28198         if(!file || !this.urlAPI){
28199             return;
28200         }
28201         
28202         this.file = file;
28203         this.cropType = file.type;
28204         
28205         var _this = this;
28206         
28207         if(this.fireEvent('prepare', this, this.file) != false){
28208             
28209             var reader = new FileReader();
28210             
28211             reader.onload = function (e) {
28212                 if (e.target.error) {
28213                     Roo.log(e.target.error);
28214                     return;
28215                 }
28216                 
28217                 var buffer = e.target.result,
28218                     dataView = new DataView(buffer),
28219                     offset = 2,
28220                     maxOffset = dataView.byteLength - 4,
28221                     markerBytes,
28222                     markerLength;
28223                 
28224                 if (dataView.getUint16(0) === 0xffd8) {
28225                     while (offset < maxOffset) {
28226                         markerBytes = dataView.getUint16(offset);
28227                         
28228                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28229                             markerLength = dataView.getUint16(offset + 2) + 2;
28230                             if (offset + markerLength > dataView.byteLength) {
28231                                 Roo.log('Invalid meta data: Invalid segment size.');
28232                                 break;
28233                             }
28234                             
28235                             if(markerBytes == 0xffe1){
28236                                 _this.parseExifData(
28237                                     dataView,
28238                                     offset,
28239                                     markerLength
28240                                 );
28241                             }
28242                             
28243                             offset += markerLength;
28244                             
28245                             continue;
28246                         }
28247                         
28248                         break;
28249                     }
28250                     
28251                 }
28252                 
28253                 var url = _this.urlAPI.createObjectURL(_this.file);
28254                 
28255                 _this.loadCanvas(url);
28256                 
28257                 return;
28258             }
28259             
28260             reader.readAsArrayBuffer(this.file);
28261             
28262         }
28263         
28264     },
28265     
28266     parseExifData : function(dataView, offset, length)
28267     {
28268         var tiffOffset = offset + 10,
28269             littleEndian,
28270             dirOffset;
28271     
28272         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28273             // No Exif data, might be XMP data instead
28274             return;
28275         }
28276         
28277         // Check for the ASCII code for "Exif" (0x45786966):
28278         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28279             // No Exif data, might be XMP data instead
28280             return;
28281         }
28282         if (tiffOffset + 8 > dataView.byteLength) {
28283             Roo.log('Invalid Exif data: Invalid segment size.');
28284             return;
28285         }
28286         // Check for the two null bytes:
28287         if (dataView.getUint16(offset + 8) !== 0x0000) {
28288             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28289             return;
28290         }
28291         // Check the byte alignment:
28292         switch (dataView.getUint16(tiffOffset)) {
28293         case 0x4949:
28294             littleEndian = true;
28295             break;
28296         case 0x4D4D:
28297             littleEndian = false;
28298             break;
28299         default:
28300             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28301             return;
28302         }
28303         // Check for the TIFF tag marker (0x002A):
28304         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28305             Roo.log('Invalid Exif data: Missing TIFF marker.');
28306             return;
28307         }
28308         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28309         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28310         
28311         this.parseExifTags(
28312             dataView,
28313             tiffOffset,
28314             tiffOffset + dirOffset,
28315             littleEndian
28316         );
28317     },
28318     
28319     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28320     {
28321         var tagsNumber,
28322             dirEndOffset,
28323             i;
28324         if (dirOffset + 6 > dataView.byteLength) {
28325             Roo.log('Invalid Exif data: Invalid directory offset.');
28326             return;
28327         }
28328         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28329         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28330         if (dirEndOffset + 4 > dataView.byteLength) {
28331             Roo.log('Invalid Exif data: Invalid directory size.');
28332             return;
28333         }
28334         for (i = 0; i < tagsNumber; i += 1) {
28335             this.parseExifTag(
28336                 dataView,
28337                 tiffOffset,
28338                 dirOffset + 2 + 12 * i, // tag offset
28339                 littleEndian
28340             );
28341         }
28342         // Return the offset to the next directory:
28343         return dataView.getUint32(dirEndOffset, littleEndian);
28344     },
28345     
28346     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28347     {
28348         var tag = dataView.getUint16(offset, littleEndian);
28349         
28350         this.exif[tag] = this.getExifValue(
28351             dataView,
28352             tiffOffset,
28353             offset,
28354             dataView.getUint16(offset + 2, littleEndian), // tag type
28355             dataView.getUint32(offset + 4, littleEndian), // tag length
28356             littleEndian
28357         );
28358     },
28359     
28360     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28361     {
28362         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28363             tagSize,
28364             dataOffset,
28365             values,
28366             i,
28367             str,
28368             c;
28369     
28370         if (!tagType) {
28371             Roo.log('Invalid Exif data: Invalid tag type.');
28372             return;
28373         }
28374         
28375         tagSize = tagType.size * length;
28376         // Determine if the value is contained in the dataOffset bytes,
28377         // or if the value at the dataOffset is a pointer to the actual data:
28378         dataOffset = tagSize > 4 ?
28379                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28380         if (dataOffset + tagSize > dataView.byteLength) {
28381             Roo.log('Invalid Exif data: Invalid data offset.');
28382             return;
28383         }
28384         if (length === 1) {
28385             return tagType.getValue(dataView, dataOffset, littleEndian);
28386         }
28387         values = [];
28388         for (i = 0; i < length; i += 1) {
28389             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28390         }
28391         
28392         if (tagType.ascii) {
28393             str = '';
28394             // Concatenate the chars:
28395             for (i = 0; i < values.length; i += 1) {
28396                 c = values[i];
28397                 // Ignore the terminating NULL byte(s):
28398                 if (c === '\u0000') {
28399                     break;
28400                 }
28401                 str += c;
28402             }
28403             return str;
28404         }
28405         return values;
28406     }
28407     
28408 });
28409
28410 Roo.apply(Roo.bootstrap.UploadCropbox, {
28411     tags : {
28412         'Orientation': 0x0112
28413     },
28414     
28415     Orientation: {
28416             1: 0, //'top-left',
28417 //            2: 'top-right',
28418             3: 180, //'bottom-right',
28419 //            4: 'bottom-left',
28420 //            5: 'left-top',
28421             6: 90, //'right-top',
28422 //            7: 'right-bottom',
28423             8: 270 //'left-bottom'
28424     },
28425     
28426     exifTagTypes : {
28427         // byte, 8-bit unsigned int:
28428         1: {
28429             getValue: function (dataView, dataOffset) {
28430                 return dataView.getUint8(dataOffset);
28431             },
28432             size: 1
28433         },
28434         // ascii, 8-bit byte:
28435         2: {
28436             getValue: function (dataView, dataOffset) {
28437                 return String.fromCharCode(dataView.getUint8(dataOffset));
28438             },
28439             size: 1,
28440             ascii: true
28441         },
28442         // short, 16 bit int:
28443         3: {
28444             getValue: function (dataView, dataOffset, littleEndian) {
28445                 return dataView.getUint16(dataOffset, littleEndian);
28446             },
28447             size: 2
28448         },
28449         // long, 32 bit int:
28450         4: {
28451             getValue: function (dataView, dataOffset, littleEndian) {
28452                 return dataView.getUint32(dataOffset, littleEndian);
28453             },
28454             size: 4
28455         },
28456         // rational = two long values, first is numerator, second is denominator:
28457         5: {
28458             getValue: function (dataView, dataOffset, littleEndian) {
28459                 return dataView.getUint32(dataOffset, littleEndian) /
28460                     dataView.getUint32(dataOffset + 4, littleEndian);
28461             },
28462             size: 8
28463         },
28464         // slong, 32 bit signed int:
28465         9: {
28466             getValue: function (dataView, dataOffset, littleEndian) {
28467                 return dataView.getInt32(dataOffset, littleEndian);
28468             },
28469             size: 4
28470         },
28471         // srational, two slongs, first is numerator, second is denominator:
28472         10: {
28473             getValue: function (dataView, dataOffset, littleEndian) {
28474                 return dataView.getInt32(dataOffset, littleEndian) /
28475                     dataView.getInt32(dataOffset + 4, littleEndian);
28476             },
28477             size: 8
28478         }
28479     },
28480     
28481     footer : {
28482         STANDARD : [
28483             {
28484                 tag : 'div',
28485                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28486                 action : 'rotate-left',
28487                 cn : [
28488                     {
28489                         tag : 'button',
28490                         cls : 'btn btn-default',
28491                         html : '<i class="fa fa-undo"></i>'
28492                     }
28493                 ]
28494             },
28495             {
28496                 tag : 'div',
28497                 cls : 'btn-group roo-upload-cropbox-picture',
28498                 action : 'picture',
28499                 cn : [
28500                     {
28501                         tag : 'button',
28502                         cls : 'btn btn-default',
28503                         html : '<i class="fa fa-picture-o"></i>'
28504                     }
28505                 ]
28506             },
28507             {
28508                 tag : 'div',
28509                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28510                 action : 'rotate-right',
28511                 cn : [
28512                     {
28513                         tag : 'button',
28514                         cls : 'btn btn-default',
28515                         html : '<i class="fa fa-repeat"></i>'
28516                     }
28517                 ]
28518             }
28519         ],
28520         DOCUMENT : [
28521             {
28522                 tag : 'div',
28523                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28524                 action : 'rotate-left',
28525                 cn : [
28526                     {
28527                         tag : 'button',
28528                         cls : 'btn btn-default',
28529                         html : '<i class="fa fa-undo"></i>'
28530                     }
28531                 ]
28532             },
28533             {
28534                 tag : 'div',
28535                 cls : 'btn-group roo-upload-cropbox-download',
28536                 action : 'download',
28537                 cn : [
28538                     {
28539                         tag : 'button',
28540                         cls : 'btn btn-default',
28541                         html : '<i class="fa fa-download"></i>'
28542                     }
28543                 ]
28544             },
28545             {
28546                 tag : 'div',
28547                 cls : 'btn-group roo-upload-cropbox-crop',
28548                 action : 'crop',
28549                 cn : [
28550                     {
28551                         tag : 'button',
28552                         cls : 'btn btn-default',
28553                         html : '<i class="fa fa-crop"></i>'
28554                     }
28555                 ]
28556             },
28557             {
28558                 tag : 'div',
28559                 cls : 'btn-group roo-upload-cropbox-trash',
28560                 action : 'trash',
28561                 cn : [
28562                     {
28563                         tag : 'button',
28564                         cls : 'btn btn-default',
28565                         html : '<i class="fa fa-trash"></i>'
28566                     }
28567                 ]
28568             },
28569             {
28570                 tag : 'div',
28571                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28572                 action : 'rotate-right',
28573                 cn : [
28574                     {
28575                         tag : 'button',
28576                         cls : 'btn btn-default',
28577                         html : '<i class="fa fa-repeat"></i>'
28578                     }
28579                 ]
28580             }
28581         ],
28582         ROTATOR : [
28583             {
28584                 tag : 'div',
28585                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28586                 action : 'rotate-left',
28587                 cn : [
28588                     {
28589                         tag : 'button',
28590                         cls : 'btn btn-default',
28591                         html : '<i class="fa fa-undo"></i>'
28592                     }
28593                 ]
28594             },
28595             {
28596                 tag : 'div',
28597                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28598                 action : 'rotate-right',
28599                 cn : [
28600                     {
28601                         tag : 'button',
28602                         cls : 'btn btn-default',
28603                         html : '<i class="fa fa-repeat"></i>'
28604                     }
28605                 ]
28606             }
28607         ]
28608     }
28609 });
28610
28611 /*
28612 * Licence: LGPL
28613 */
28614
28615 /**
28616  * @class Roo.bootstrap.DocumentManager
28617  * @extends Roo.bootstrap.Component
28618  * Bootstrap DocumentManager class
28619  * @cfg {String} paramName default 'imageUpload'
28620  * @cfg {String} toolTipName default 'filename'
28621  * @cfg {String} method default POST
28622  * @cfg {String} url action url
28623  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28624  * @cfg {Boolean} multiple multiple upload default true
28625  * @cfg {Number} thumbSize default 300
28626  * @cfg {String} fieldLabel
28627  * @cfg {Number} labelWidth default 4
28628  * @cfg {String} labelAlign (left|top) default left
28629  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28630 * @cfg {Number} labellg set the width of label (1-12)
28631  * @cfg {Number} labelmd set the width of label (1-12)
28632  * @cfg {Number} labelsm set the width of label (1-12)
28633  * @cfg {Number} labelxs set the width of label (1-12)
28634  * 
28635  * @constructor
28636  * Create a new DocumentManager
28637  * @param {Object} config The config object
28638  */
28639
28640 Roo.bootstrap.DocumentManager = function(config){
28641     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28642     
28643     this.files = [];
28644     this.delegates = [];
28645     
28646     this.addEvents({
28647         /**
28648          * @event initial
28649          * Fire when initial the DocumentManager
28650          * @param {Roo.bootstrap.DocumentManager} this
28651          */
28652         "initial" : true,
28653         /**
28654          * @event inspect
28655          * inspect selected file
28656          * @param {Roo.bootstrap.DocumentManager} this
28657          * @param {File} file
28658          */
28659         "inspect" : true,
28660         /**
28661          * @event exception
28662          * Fire when xhr load exception
28663          * @param {Roo.bootstrap.DocumentManager} this
28664          * @param {XMLHttpRequest} xhr
28665          */
28666         "exception" : true,
28667         /**
28668          * @event afterupload
28669          * Fire when xhr load exception
28670          * @param {Roo.bootstrap.DocumentManager} this
28671          * @param {XMLHttpRequest} xhr
28672          */
28673         "afterupload" : true,
28674         /**
28675          * @event prepare
28676          * prepare the form data
28677          * @param {Roo.bootstrap.DocumentManager} this
28678          * @param {Object} formData
28679          */
28680         "prepare" : true,
28681         /**
28682          * @event remove
28683          * Fire when remove the file
28684          * @param {Roo.bootstrap.DocumentManager} this
28685          * @param {Object} file
28686          */
28687         "remove" : true,
28688         /**
28689          * @event refresh
28690          * Fire after refresh the file
28691          * @param {Roo.bootstrap.DocumentManager} this
28692          */
28693         "refresh" : true,
28694         /**
28695          * @event click
28696          * Fire after click the image
28697          * @param {Roo.bootstrap.DocumentManager} this
28698          * @param {Object} file
28699          */
28700         "click" : true,
28701         /**
28702          * @event edit
28703          * Fire when upload a image and editable set to true
28704          * @param {Roo.bootstrap.DocumentManager} this
28705          * @param {Object} file
28706          */
28707         "edit" : true,
28708         /**
28709          * @event beforeselectfile
28710          * Fire before select file
28711          * @param {Roo.bootstrap.DocumentManager} this
28712          */
28713         "beforeselectfile" : true,
28714         /**
28715          * @event process
28716          * Fire before process file
28717          * @param {Roo.bootstrap.DocumentManager} this
28718          * @param {Object} file
28719          */
28720         "process" : true,
28721         /**
28722          * @event previewrendered
28723          * Fire when preview rendered
28724          * @param {Roo.bootstrap.DocumentManager} this
28725          * @param {Object} file
28726          */
28727         "previewrendered" : true,
28728         /**
28729          */
28730         "previewResize" : true
28731         
28732     });
28733 };
28734
28735 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28736     
28737     boxes : 0,
28738     inputName : '',
28739     thumbSize : 300,
28740     multiple : true,
28741     files : false,
28742     method : 'POST',
28743     url : '',
28744     paramName : 'imageUpload',
28745     toolTipName : 'filename',
28746     fieldLabel : '',
28747     labelWidth : 4,
28748     labelAlign : 'left',
28749     editable : true,
28750     delegates : false,
28751     xhr : false, 
28752     
28753     labellg : 0,
28754     labelmd : 0,
28755     labelsm : 0,
28756     labelxs : 0,
28757     
28758     getAutoCreate : function()
28759     {   
28760         var managerWidget = {
28761             tag : 'div',
28762             cls : 'roo-document-manager',
28763             cn : [
28764                 {
28765                     tag : 'input',
28766                     cls : 'roo-document-manager-selector',
28767                     type : 'file'
28768                 },
28769                 {
28770                     tag : 'div',
28771                     cls : 'roo-document-manager-uploader',
28772                     cn : [
28773                         {
28774                             tag : 'div',
28775                             cls : 'roo-document-manager-upload-btn',
28776                             html : '<i class="fa fa-plus"></i>'
28777                         }
28778                     ]
28779                     
28780                 }
28781             ]
28782         };
28783         
28784         var content = [
28785             {
28786                 tag : 'div',
28787                 cls : 'column col-md-12',
28788                 cn : managerWidget
28789             }
28790         ];
28791         
28792         if(this.fieldLabel.length){
28793             
28794             content = [
28795                 {
28796                     tag : 'div',
28797                     cls : 'column col-md-12',
28798                     html : this.fieldLabel
28799                 },
28800                 {
28801                     tag : 'div',
28802                     cls : 'column col-md-12',
28803                     cn : managerWidget
28804                 }
28805             ];
28806
28807             if(this.labelAlign == 'left'){
28808                 content = [
28809                     {
28810                         tag : 'div',
28811                         cls : 'column',
28812                         html : this.fieldLabel
28813                     },
28814                     {
28815                         tag : 'div',
28816                         cls : 'column',
28817                         cn : managerWidget
28818                     }
28819                 ];
28820                 
28821                 if(this.labelWidth > 12){
28822                     content[0].style = "width: " + this.labelWidth + 'px';
28823                 }
28824
28825                 if(this.labelWidth < 13 && this.labelmd == 0){
28826                     this.labelmd = this.labelWidth;
28827                 }
28828
28829                 if(this.labellg > 0){
28830                     content[0].cls += ' col-lg-' + this.labellg;
28831                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28832                 }
28833
28834                 if(this.labelmd > 0){
28835                     content[0].cls += ' col-md-' + this.labelmd;
28836                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28837                 }
28838
28839                 if(this.labelsm > 0){
28840                     content[0].cls += ' col-sm-' + this.labelsm;
28841                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28842                 }
28843
28844                 if(this.labelxs > 0){
28845                     content[0].cls += ' col-xs-' + this.labelxs;
28846                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28847                 }
28848                 
28849             }
28850         }
28851         
28852         var cfg = {
28853             tag : 'div',
28854             cls : 'row clearfix',
28855             cn : content
28856         };
28857         
28858         return cfg;
28859         
28860     },
28861     
28862     initEvents : function()
28863     {
28864         this.managerEl = this.el.select('.roo-document-manager', true).first();
28865         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28866         
28867         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28868         this.selectorEl.hide();
28869         
28870         if(this.multiple){
28871             this.selectorEl.attr('multiple', 'multiple');
28872         }
28873         
28874         this.selectorEl.on('change', this.onFileSelected, this);
28875         
28876         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28877         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28878         
28879         this.uploader.on('click', this.onUploaderClick, this);
28880         
28881         this.renderProgressDialog();
28882         
28883         var _this = this;
28884         
28885         window.addEventListener("resize", function() { _this.refresh(); } );
28886         
28887         this.fireEvent('initial', this);
28888     },
28889     
28890     renderProgressDialog : function()
28891     {
28892         var _this = this;
28893         
28894         this.progressDialog = new Roo.bootstrap.Modal({
28895             cls : 'roo-document-manager-progress-dialog',
28896             allow_close : false,
28897             title : '',
28898             buttons : [
28899                 {
28900                     name  :'cancel',
28901                     weight : 'danger',
28902                     html : 'Cancel'
28903                 }
28904             ], 
28905             listeners : { 
28906                 btnclick : function() {
28907                     _this.uploadCancel();
28908                     this.hide();
28909                 }
28910             }
28911         });
28912          
28913         this.progressDialog.render(Roo.get(document.body));
28914          
28915         this.progress = new Roo.bootstrap.Progress({
28916             cls : 'roo-document-manager-progress',
28917             active : true,
28918             striped : true
28919         });
28920         
28921         this.progress.render(this.progressDialog.getChildContainer());
28922         
28923         this.progressBar = new Roo.bootstrap.ProgressBar({
28924             cls : 'roo-document-manager-progress-bar',
28925             aria_valuenow : 0,
28926             aria_valuemin : 0,
28927             aria_valuemax : 12,
28928             panel : 'success'
28929         });
28930         
28931         this.progressBar.render(this.progress.getChildContainer());
28932     },
28933     
28934     onUploaderClick : function(e)
28935     {
28936         e.preventDefault();
28937      
28938         if(this.fireEvent('beforeselectfile', this) != false){
28939             this.selectorEl.dom.click();
28940         }
28941         
28942     },
28943     
28944     onFileSelected : function(e)
28945     {
28946         e.preventDefault();
28947         
28948         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28949             return;
28950         }
28951         
28952         Roo.each(this.selectorEl.dom.files, function(file){
28953             if(this.fireEvent('inspect', this, file) != false){
28954                 this.files.push(file);
28955             }
28956         }, this);
28957         
28958         this.queue();
28959         
28960     },
28961     
28962     queue : function()
28963     {
28964         this.selectorEl.dom.value = '';
28965         
28966         if(!this.files || !this.files.length){
28967             return;
28968         }
28969         
28970         if(this.boxes > 0 && this.files.length > this.boxes){
28971             this.files = this.files.slice(0, this.boxes);
28972         }
28973         
28974         this.uploader.show();
28975         
28976         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28977             this.uploader.hide();
28978         }
28979         
28980         var _this = this;
28981         
28982         var files = [];
28983         
28984         var docs = [];
28985         
28986         Roo.each(this.files, function(file){
28987             
28988             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28989                 var f = this.renderPreview(file);
28990                 files.push(f);
28991                 return;
28992             }
28993             
28994             if(file.type.indexOf('image') != -1){
28995                 this.delegates.push(
28996                     (function(){
28997                         _this.process(file);
28998                     }).createDelegate(this)
28999                 );
29000         
29001                 return;
29002             }
29003             
29004             docs.push(
29005                 (function(){
29006                     _this.process(file);
29007                 }).createDelegate(this)
29008             );
29009             
29010         }, this);
29011         
29012         this.files = files;
29013         
29014         this.delegates = this.delegates.concat(docs);
29015         
29016         if(!this.delegates.length){
29017             this.refresh();
29018             return;
29019         }
29020         
29021         this.progressBar.aria_valuemax = this.delegates.length;
29022         
29023         this.arrange();
29024         
29025         return;
29026     },
29027     
29028     arrange : function()
29029     {
29030         if(!this.delegates.length){
29031             this.progressDialog.hide();
29032             this.refresh();
29033             return;
29034         }
29035         
29036         var delegate = this.delegates.shift();
29037         
29038         this.progressDialog.show();
29039         
29040         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29041         
29042         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29043         
29044         delegate();
29045     },
29046     
29047     refresh : function()
29048     {
29049         this.uploader.show();
29050         
29051         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29052             this.uploader.hide();
29053         }
29054         
29055         Roo.isTouch ? this.closable(false) : this.closable(true);
29056         
29057         this.fireEvent('refresh', this);
29058     },
29059     
29060     onRemove : function(e, el, o)
29061     {
29062         e.preventDefault();
29063         
29064         this.fireEvent('remove', this, o);
29065         
29066     },
29067     
29068     remove : function(o)
29069     {
29070         var files = [];
29071         
29072         Roo.each(this.files, function(file){
29073             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29074                 files.push(file);
29075                 return;
29076             }
29077
29078             o.target.remove();
29079
29080         }, this);
29081         
29082         this.files = files;
29083         
29084         this.refresh();
29085     },
29086     
29087     clear : function()
29088     {
29089         Roo.each(this.files, function(file){
29090             if(!file.target){
29091                 return;
29092             }
29093             
29094             file.target.remove();
29095
29096         }, this);
29097         
29098         this.files = [];
29099         
29100         this.refresh();
29101     },
29102     
29103     onClick : function(e, el, o)
29104     {
29105         e.preventDefault();
29106         
29107         this.fireEvent('click', this, o);
29108         
29109     },
29110     
29111     closable : function(closable)
29112     {
29113         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29114             
29115             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29116             
29117             if(closable){
29118                 el.show();
29119                 return;
29120             }
29121             
29122             el.hide();
29123             
29124         }, this);
29125     },
29126     
29127     xhrOnLoad : function(xhr)
29128     {
29129         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29130             el.remove();
29131         }, this);
29132         
29133         if (xhr.readyState !== 4) {
29134             this.arrange();
29135             this.fireEvent('exception', this, xhr);
29136             return;
29137         }
29138
29139         var response = Roo.decode(xhr.responseText);
29140         
29141         if(!response.success){
29142             this.arrange();
29143             this.fireEvent('exception', this, xhr);
29144             return;
29145         }
29146         
29147         var file = this.renderPreview(response.data);
29148         
29149         this.files.push(file);
29150         
29151         this.arrange();
29152         
29153         this.fireEvent('afterupload', this, xhr);
29154         
29155     },
29156     
29157     xhrOnError : function(xhr)
29158     {
29159         Roo.log('xhr on error');
29160         
29161         var response = Roo.decode(xhr.responseText);
29162           
29163         Roo.log(response);
29164         
29165         this.arrange();
29166     },
29167     
29168     process : function(file)
29169     {
29170         if(this.fireEvent('process', this, file) !== false){
29171             if(this.editable && file.type.indexOf('image') != -1){
29172                 this.fireEvent('edit', this, file);
29173                 return;
29174             }
29175
29176             this.uploadStart(file, false);
29177
29178             return;
29179         }
29180         
29181     },
29182     
29183     uploadStart : function(file, crop)
29184     {
29185         this.xhr = new XMLHttpRequest();
29186         
29187         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29188             this.arrange();
29189             return;
29190         }
29191         
29192         file.xhr = this.xhr;
29193             
29194         this.managerEl.createChild({
29195             tag : 'div',
29196             cls : 'roo-document-manager-loading',
29197             cn : [
29198                 {
29199                     tag : 'div',
29200                     tooltip : file.name,
29201                     cls : 'roo-document-manager-thumb',
29202                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29203                 }
29204             ]
29205
29206         });
29207
29208         this.xhr.open(this.method, this.url, true);
29209         
29210         var headers = {
29211             "Accept": "application/json",
29212             "Cache-Control": "no-cache",
29213             "X-Requested-With": "XMLHttpRequest"
29214         };
29215         
29216         for (var headerName in headers) {
29217             var headerValue = headers[headerName];
29218             if (headerValue) {
29219                 this.xhr.setRequestHeader(headerName, headerValue);
29220             }
29221         }
29222         
29223         var _this = this;
29224         
29225         this.xhr.onload = function()
29226         {
29227             _this.xhrOnLoad(_this.xhr);
29228         }
29229         
29230         this.xhr.onerror = function()
29231         {
29232             _this.xhrOnError(_this.xhr);
29233         }
29234         
29235         var formData = new FormData();
29236
29237         formData.append('returnHTML', 'NO');
29238         
29239         if(crop){
29240             formData.append('crop', crop);
29241         }
29242         
29243         formData.append(this.paramName, file, file.name);
29244         
29245         var options = {
29246             file : file, 
29247             manually : false
29248         };
29249         
29250         if(this.fireEvent('prepare', this, formData, options) != false){
29251             
29252             if(options.manually){
29253                 return;
29254             }
29255             
29256             this.xhr.send(formData);
29257             return;
29258         };
29259         
29260         this.uploadCancel();
29261     },
29262     
29263     uploadCancel : function()
29264     {
29265         if (this.xhr) {
29266             this.xhr.abort();
29267         }
29268         
29269         this.delegates = [];
29270         
29271         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29272             el.remove();
29273         }, this);
29274         
29275         this.arrange();
29276     },
29277     
29278     renderPreview : function(file)
29279     {
29280         if(typeof(file.target) != 'undefined' && file.target){
29281             return file;
29282         }
29283         
29284         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29285         
29286         var previewEl = this.managerEl.createChild({
29287             tag : 'div',
29288             cls : 'roo-document-manager-preview',
29289             cn : [
29290                 {
29291                     tag : 'div',
29292                     tooltip : file[this.toolTipName],
29293                     cls : 'roo-document-manager-thumb',
29294                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29295                 },
29296                 {
29297                     tag : 'button',
29298                     cls : 'close',
29299                     html : '<i class="fa fa-times-circle"></i>'
29300                 }
29301             ]
29302         });
29303
29304         var close = previewEl.select('button.close', true).first();
29305
29306         close.on('click', this.onRemove, this, file);
29307
29308         file.target = previewEl;
29309
29310         var image = previewEl.select('img', true).first();
29311         
29312         var _this = this;
29313         
29314         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29315         
29316         image.on('click', this.onClick, this, file);
29317         
29318         this.fireEvent('previewrendered', this, file);
29319         
29320         return file;
29321         
29322     },
29323     
29324     onPreviewLoad : function(file, image)
29325     {
29326         if(typeof(file.target) == 'undefined' || !file.target){
29327             return;
29328         }
29329         
29330         var width = image.dom.naturalWidth || image.dom.width;
29331         var height = image.dom.naturalHeight || image.dom.height;
29332         
29333         if(!this.previewResize) {
29334             return;
29335         }
29336         
29337         if(width > height){
29338             file.target.addClass('wide');
29339             return;
29340         }
29341         
29342         file.target.addClass('tall');
29343         return;
29344         
29345     },
29346     
29347     uploadFromSource : function(file, crop)
29348     {
29349         this.xhr = new XMLHttpRequest();
29350         
29351         this.managerEl.createChild({
29352             tag : 'div',
29353             cls : 'roo-document-manager-loading',
29354             cn : [
29355                 {
29356                     tag : 'div',
29357                     tooltip : file.name,
29358                     cls : 'roo-document-manager-thumb',
29359                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29360                 }
29361             ]
29362
29363         });
29364
29365         this.xhr.open(this.method, this.url, true);
29366         
29367         var headers = {
29368             "Accept": "application/json",
29369             "Cache-Control": "no-cache",
29370             "X-Requested-With": "XMLHttpRequest"
29371         };
29372         
29373         for (var headerName in headers) {
29374             var headerValue = headers[headerName];
29375             if (headerValue) {
29376                 this.xhr.setRequestHeader(headerName, headerValue);
29377             }
29378         }
29379         
29380         var _this = this;
29381         
29382         this.xhr.onload = function()
29383         {
29384             _this.xhrOnLoad(_this.xhr);
29385         }
29386         
29387         this.xhr.onerror = function()
29388         {
29389             _this.xhrOnError(_this.xhr);
29390         }
29391         
29392         var formData = new FormData();
29393
29394         formData.append('returnHTML', 'NO');
29395         
29396         formData.append('crop', crop);
29397         
29398         if(typeof(file.filename) != 'undefined'){
29399             formData.append('filename', file.filename);
29400         }
29401         
29402         if(typeof(file.mimetype) != 'undefined'){
29403             formData.append('mimetype', file.mimetype);
29404         }
29405         
29406         Roo.log(formData);
29407         
29408         if(this.fireEvent('prepare', this, formData) != false){
29409             this.xhr.send(formData);
29410         };
29411     }
29412 });
29413
29414 /*
29415 * Licence: LGPL
29416 */
29417
29418 /**
29419  * @class Roo.bootstrap.DocumentViewer
29420  * @extends Roo.bootstrap.Component
29421  * Bootstrap DocumentViewer class
29422  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29423  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29424  * 
29425  * @constructor
29426  * Create a new DocumentViewer
29427  * @param {Object} config The config object
29428  */
29429
29430 Roo.bootstrap.DocumentViewer = function(config){
29431     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29432     
29433     this.addEvents({
29434         /**
29435          * @event initial
29436          * Fire after initEvent
29437          * @param {Roo.bootstrap.DocumentViewer} this
29438          */
29439         "initial" : true,
29440         /**
29441          * @event click
29442          * Fire after click
29443          * @param {Roo.bootstrap.DocumentViewer} this
29444          */
29445         "click" : true,
29446         /**
29447          * @event download
29448          * Fire after download button
29449          * @param {Roo.bootstrap.DocumentViewer} this
29450          */
29451         "download" : true,
29452         /**
29453          * @event trash
29454          * Fire after trash button
29455          * @param {Roo.bootstrap.DocumentViewer} this
29456          */
29457         "trash" : true
29458         
29459     });
29460 };
29461
29462 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29463     
29464     showDownload : true,
29465     
29466     showTrash : true,
29467     
29468     getAutoCreate : function()
29469     {
29470         var cfg = {
29471             tag : 'div',
29472             cls : 'roo-document-viewer',
29473             cn : [
29474                 {
29475                     tag : 'div',
29476                     cls : 'roo-document-viewer-body',
29477                     cn : [
29478                         {
29479                             tag : 'div',
29480                             cls : 'roo-document-viewer-thumb',
29481                             cn : [
29482                                 {
29483                                     tag : 'img',
29484                                     cls : 'roo-document-viewer-image'
29485                                 }
29486                             ]
29487                         }
29488                     ]
29489                 },
29490                 {
29491                     tag : 'div',
29492                     cls : 'roo-document-viewer-footer',
29493                     cn : {
29494                         tag : 'div',
29495                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29496                         cn : [
29497                             {
29498                                 tag : 'div',
29499                                 cls : 'btn-group roo-document-viewer-download',
29500                                 cn : [
29501                                     {
29502                                         tag : 'button',
29503                                         cls : 'btn btn-default',
29504                                         html : '<i class="fa fa-download"></i>'
29505                                     }
29506                                 ]
29507                             },
29508                             {
29509                                 tag : 'div',
29510                                 cls : 'btn-group roo-document-viewer-trash',
29511                                 cn : [
29512                                     {
29513                                         tag : 'button',
29514                                         cls : 'btn btn-default',
29515                                         html : '<i class="fa fa-trash"></i>'
29516                                     }
29517                                 ]
29518                             }
29519                         ]
29520                     }
29521                 }
29522             ]
29523         };
29524         
29525         return cfg;
29526     },
29527     
29528     initEvents : function()
29529     {
29530         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29531         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29532         
29533         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29534         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29535         
29536         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29537         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29538         
29539         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29540         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29541         
29542         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29543         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29544         
29545         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29546         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29547         
29548         this.bodyEl.on('click', this.onClick, this);
29549         this.downloadBtn.on('click', this.onDownload, this);
29550         this.trashBtn.on('click', this.onTrash, this);
29551         
29552         this.downloadBtn.hide();
29553         this.trashBtn.hide();
29554         
29555         if(this.showDownload){
29556             this.downloadBtn.show();
29557         }
29558         
29559         if(this.showTrash){
29560             this.trashBtn.show();
29561         }
29562         
29563         if(!this.showDownload && !this.showTrash) {
29564             this.footerEl.hide();
29565         }
29566         
29567     },
29568     
29569     initial : function()
29570     {
29571         this.fireEvent('initial', this);
29572         
29573     },
29574     
29575     onClick : function(e)
29576     {
29577         e.preventDefault();
29578         
29579         this.fireEvent('click', this);
29580     },
29581     
29582     onDownload : function(e)
29583     {
29584         e.preventDefault();
29585         
29586         this.fireEvent('download', this);
29587     },
29588     
29589     onTrash : function(e)
29590     {
29591         e.preventDefault();
29592         
29593         this.fireEvent('trash', this);
29594     }
29595     
29596 });
29597 /*
29598  * - LGPL
29599  *
29600  * nav progress bar
29601  * 
29602  */
29603
29604 /**
29605  * @class Roo.bootstrap.NavProgressBar
29606  * @extends Roo.bootstrap.Component
29607  * Bootstrap NavProgressBar class
29608  * 
29609  * @constructor
29610  * Create a new nav progress bar
29611  * @param {Object} config The config object
29612  */
29613
29614 Roo.bootstrap.NavProgressBar = function(config){
29615     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29616
29617     this.bullets = this.bullets || [];
29618    
29619 //    Roo.bootstrap.NavProgressBar.register(this);
29620      this.addEvents({
29621         /**
29622              * @event changed
29623              * Fires when the active item changes
29624              * @param {Roo.bootstrap.NavProgressBar} this
29625              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29626              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29627          */
29628         'changed': true
29629      });
29630     
29631 };
29632
29633 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29634     
29635     bullets : [],
29636     barItems : [],
29637     
29638     getAutoCreate : function()
29639     {
29640         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29641         
29642         cfg = {
29643             tag : 'div',
29644             cls : 'roo-navigation-bar-group',
29645             cn : [
29646                 {
29647                     tag : 'div',
29648                     cls : 'roo-navigation-top-bar'
29649                 },
29650                 {
29651                     tag : 'div',
29652                     cls : 'roo-navigation-bullets-bar',
29653                     cn : [
29654                         {
29655                             tag : 'ul',
29656                             cls : 'roo-navigation-bar'
29657                         }
29658                     ]
29659                 },
29660                 
29661                 {
29662                     tag : 'div',
29663                     cls : 'roo-navigation-bottom-bar'
29664                 }
29665             ]
29666             
29667         };
29668         
29669         return cfg;
29670         
29671     },
29672     
29673     initEvents: function() 
29674     {
29675         
29676     },
29677     
29678     onRender : function(ct, position) 
29679     {
29680         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29681         
29682         if(this.bullets.length){
29683             Roo.each(this.bullets, function(b){
29684                this.addItem(b);
29685             }, this);
29686         }
29687         
29688         this.format();
29689         
29690     },
29691     
29692     addItem : function(cfg)
29693     {
29694         var item = new Roo.bootstrap.NavProgressItem(cfg);
29695         
29696         item.parentId = this.id;
29697         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29698         
29699         if(cfg.html){
29700             var top = new Roo.bootstrap.Element({
29701                 tag : 'div',
29702                 cls : 'roo-navigation-bar-text'
29703             });
29704             
29705             var bottom = new Roo.bootstrap.Element({
29706                 tag : 'div',
29707                 cls : 'roo-navigation-bar-text'
29708             });
29709             
29710             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29711             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29712             
29713             var topText = new Roo.bootstrap.Element({
29714                 tag : 'span',
29715                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29716             });
29717             
29718             var bottomText = new Roo.bootstrap.Element({
29719                 tag : 'span',
29720                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29721             });
29722             
29723             topText.onRender(top.el, null);
29724             bottomText.onRender(bottom.el, null);
29725             
29726             item.topEl = top;
29727             item.bottomEl = bottom;
29728         }
29729         
29730         this.barItems.push(item);
29731         
29732         return item;
29733     },
29734     
29735     getActive : function()
29736     {
29737         var active = false;
29738         
29739         Roo.each(this.barItems, function(v){
29740             
29741             if (!v.isActive()) {
29742                 return;
29743             }
29744             
29745             active = v;
29746             return false;
29747             
29748         });
29749         
29750         return active;
29751     },
29752     
29753     setActiveItem : function(item)
29754     {
29755         var prev = false;
29756         
29757         Roo.each(this.barItems, function(v){
29758             if (v.rid == item.rid) {
29759                 return ;
29760             }
29761             
29762             if (v.isActive()) {
29763                 v.setActive(false);
29764                 prev = v;
29765             }
29766         });
29767
29768         item.setActive(true);
29769         
29770         this.fireEvent('changed', this, item, prev);
29771     },
29772     
29773     getBarItem: function(rid)
29774     {
29775         var ret = false;
29776         
29777         Roo.each(this.barItems, function(e) {
29778             if (e.rid != rid) {
29779                 return;
29780             }
29781             
29782             ret =  e;
29783             return false;
29784         });
29785         
29786         return ret;
29787     },
29788     
29789     indexOfItem : function(item)
29790     {
29791         var index = false;
29792         
29793         Roo.each(this.barItems, function(v, i){
29794             
29795             if (v.rid != item.rid) {
29796                 return;
29797             }
29798             
29799             index = i;
29800             return false
29801         });
29802         
29803         return index;
29804     },
29805     
29806     setActiveNext : function()
29807     {
29808         var i = this.indexOfItem(this.getActive());
29809         
29810         if (i > this.barItems.length) {
29811             return;
29812         }
29813         
29814         this.setActiveItem(this.barItems[i+1]);
29815     },
29816     
29817     setActivePrev : function()
29818     {
29819         var i = this.indexOfItem(this.getActive());
29820         
29821         if (i  < 1) {
29822             return;
29823         }
29824         
29825         this.setActiveItem(this.barItems[i-1]);
29826     },
29827     
29828     format : function()
29829     {
29830         if(!this.barItems.length){
29831             return;
29832         }
29833      
29834         var width = 100 / this.barItems.length;
29835         
29836         Roo.each(this.barItems, function(i){
29837             i.el.setStyle('width', width + '%');
29838             i.topEl.el.setStyle('width', width + '%');
29839             i.bottomEl.el.setStyle('width', width + '%');
29840         }, this);
29841         
29842     }
29843     
29844 });
29845 /*
29846  * - LGPL
29847  *
29848  * Nav Progress Item
29849  * 
29850  */
29851
29852 /**
29853  * @class Roo.bootstrap.NavProgressItem
29854  * @extends Roo.bootstrap.Component
29855  * Bootstrap NavProgressItem class
29856  * @cfg {String} rid the reference id
29857  * @cfg {Boolean} active (true|false) Is item active default false
29858  * @cfg {Boolean} disabled (true|false) Is item active default false
29859  * @cfg {String} html
29860  * @cfg {String} position (top|bottom) text position default bottom
29861  * @cfg {String} icon show icon instead of number
29862  * 
29863  * @constructor
29864  * Create a new NavProgressItem
29865  * @param {Object} config The config object
29866  */
29867 Roo.bootstrap.NavProgressItem = function(config){
29868     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29869     this.addEvents({
29870         // raw events
29871         /**
29872          * @event click
29873          * The raw click event for the entire grid.
29874          * @param {Roo.bootstrap.NavProgressItem} this
29875          * @param {Roo.EventObject} e
29876          */
29877         "click" : true
29878     });
29879    
29880 };
29881
29882 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29883     
29884     rid : '',
29885     active : false,
29886     disabled : false,
29887     html : '',
29888     position : 'bottom',
29889     icon : false,
29890     
29891     getAutoCreate : function()
29892     {
29893         var iconCls = 'roo-navigation-bar-item-icon';
29894         
29895         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29896         
29897         var cfg = {
29898             tag: 'li',
29899             cls: 'roo-navigation-bar-item',
29900             cn : [
29901                 {
29902                     tag : 'i',
29903                     cls : iconCls
29904                 }
29905             ]
29906         };
29907         
29908         if(this.active){
29909             cfg.cls += ' active';
29910         }
29911         if(this.disabled){
29912             cfg.cls += ' disabled';
29913         }
29914         
29915         return cfg;
29916     },
29917     
29918     disable : function()
29919     {
29920         this.setDisabled(true);
29921     },
29922     
29923     enable : function()
29924     {
29925         this.setDisabled(false);
29926     },
29927     
29928     initEvents: function() 
29929     {
29930         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29931         
29932         this.iconEl.on('click', this.onClick, this);
29933     },
29934     
29935     onClick : function(e)
29936     {
29937         e.preventDefault();
29938         
29939         if(this.disabled){
29940             return;
29941         }
29942         
29943         if(this.fireEvent('click', this, e) === false){
29944             return;
29945         };
29946         
29947         this.parent().setActiveItem(this);
29948     },
29949     
29950     isActive: function () 
29951     {
29952         return this.active;
29953     },
29954     
29955     setActive : function(state)
29956     {
29957         if(this.active == state){
29958             return;
29959         }
29960         
29961         this.active = state;
29962         
29963         if (state) {
29964             this.el.addClass('active');
29965             return;
29966         }
29967         
29968         this.el.removeClass('active');
29969         
29970         return;
29971     },
29972     
29973     setDisabled : function(state)
29974     {
29975         if(this.disabled == state){
29976             return;
29977         }
29978         
29979         this.disabled = state;
29980         
29981         if (state) {
29982             this.el.addClass('disabled');
29983             return;
29984         }
29985         
29986         this.el.removeClass('disabled');
29987     },
29988     
29989     tooltipEl : function()
29990     {
29991         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29992     }
29993 });
29994  
29995
29996  /*
29997  * - LGPL
29998  *
29999  * FieldLabel
30000  * 
30001  */
30002
30003 /**
30004  * @class Roo.bootstrap.FieldLabel
30005  * @extends Roo.bootstrap.Component
30006  * Bootstrap FieldLabel class
30007  * @cfg {String} html contents of the element
30008  * @cfg {String} tag tag of the element default label
30009  * @cfg {String} cls class of the element
30010  * @cfg {String} target label target 
30011  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30012  * @cfg {String} invalidClass default "text-warning"
30013  * @cfg {String} validClass default "text-success"
30014  * @cfg {String} iconTooltip default "This field is required"
30015  * @cfg {String} indicatorpos (left|right) default left
30016  * 
30017  * @constructor
30018  * Create a new FieldLabel
30019  * @param {Object} config The config object
30020  */
30021
30022 Roo.bootstrap.FieldLabel = function(config){
30023     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30024     
30025     this.addEvents({
30026             /**
30027              * @event invalid
30028              * Fires after the field has been marked as invalid.
30029              * @param {Roo.form.FieldLabel} this
30030              * @param {String} msg The validation message
30031              */
30032             invalid : true,
30033             /**
30034              * @event valid
30035              * Fires after the field has been validated with no errors.
30036              * @param {Roo.form.FieldLabel} this
30037              */
30038             valid : true
30039         });
30040 };
30041
30042 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30043     
30044     tag: 'label',
30045     cls: '',
30046     html: '',
30047     target: '',
30048     allowBlank : true,
30049     invalidClass : 'has-warning',
30050     validClass : 'has-success',
30051     iconTooltip : 'This field is required',
30052     indicatorpos : 'left',
30053     
30054     getAutoCreate : function(){
30055         
30056         var cls = "";
30057         if (!this.allowBlank) {
30058             cls  = "visible";
30059         }
30060         
30061         var cfg = {
30062             tag : this.tag,
30063             cls : 'roo-bootstrap-field-label ' + this.cls,
30064             for : this.target,
30065             cn : [
30066                 {
30067                     tag : 'i',
30068                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30069                     tooltip : this.iconTooltip
30070                 },
30071                 {
30072                     tag : 'span',
30073                     html : this.html
30074                 }
30075             ] 
30076         };
30077         
30078         if(this.indicatorpos == 'right'){
30079             var cfg = {
30080                 tag : this.tag,
30081                 cls : 'roo-bootstrap-field-label ' + this.cls,
30082                 for : this.target,
30083                 cn : [
30084                     {
30085                         tag : 'span',
30086                         html : this.html
30087                     },
30088                     {
30089                         tag : 'i',
30090                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30091                         tooltip : this.iconTooltip
30092                     }
30093                 ] 
30094             };
30095         }
30096         
30097         return cfg;
30098     },
30099     
30100     initEvents: function() 
30101     {
30102         Roo.bootstrap.Element.superclass.initEvents.call(this);
30103         
30104         this.indicator = this.indicatorEl();
30105         
30106         if(this.indicator){
30107             this.indicator.removeClass('visible');
30108             this.indicator.addClass('invisible');
30109         }
30110         
30111         Roo.bootstrap.FieldLabel.register(this);
30112     },
30113     
30114     indicatorEl : function()
30115     {
30116         var indicator = this.el.select('i.roo-required-indicator',true).first();
30117         
30118         if(!indicator){
30119             return false;
30120         }
30121         
30122         return indicator;
30123         
30124     },
30125     
30126     /**
30127      * Mark this field as valid
30128      */
30129     markValid : function()
30130     {
30131         if(this.indicator){
30132             this.indicator.removeClass('visible');
30133             this.indicator.addClass('invisible');
30134         }
30135         
30136         this.el.removeClass(this.invalidClass);
30137         
30138         this.el.addClass(this.validClass);
30139         
30140         this.fireEvent('valid', this);
30141     },
30142     
30143     /**
30144      * Mark this field as invalid
30145      * @param {String} msg The validation message
30146      */
30147     markInvalid : function(msg)
30148     {
30149         if(this.indicator){
30150             this.indicator.removeClass('invisible');
30151             this.indicator.addClass('visible');
30152         }
30153         
30154         this.el.removeClass(this.validClass);
30155         
30156         this.el.addClass(this.invalidClass);
30157         
30158         this.fireEvent('invalid', this, msg);
30159     }
30160     
30161    
30162 });
30163
30164 Roo.apply(Roo.bootstrap.FieldLabel, {
30165     
30166     groups: {},
30167     
30168      /**
30169     * register a FieldLabel Group
30170     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30171     */
30172     register : function(label)
30173     {
30174         if(this.groups.hasOwnProperty(label.target)){
30175             return;
30176         }
30177      
30178         this.groups[label.target] = label;
30179         
30180     },
30181     /**
30182     * fetch a FieldLabel Group based on the target
30183     * @param {string} target
30184     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30185     */
30186     get: function(target) {
30187         if (typeof(this.groups[target]) == 'undefined') {
30188             return false;
30189         }
30190         
30191         return this.groups[target] ;
30192     }
30193 });
30194
30195  
30196
30197  /*
30198  * - LGPL
30199  *
30200  * page DateSplitField.
30201  * 
30202  */
30203
30204
30205 /**
30206  * @class Roo.bootstrap.DateSplitField
30207  * @extends Roo.bootstrap.Component
30208  * Bootstrap DateSplitField class
30209  * @cfg {string} fieldLabel - the label associated
30210  * @cfg {Number} labelWidth set the width of label (0-12)
30211  * @cfg {String} labelAlign (top|left)
30212  * @cfg {Boolean} dayAllowBlank (true|false) default false
30213  * @cfg {Boolean} monthAllowBlank (true|false) default false
30214  * @cfg {Boolean} yearAllowBlank (true|false) default false
30215  * @cfg {string} dayPlaceholder 
30216  * @cfg {string} monthPlaceholder
30217  * @cfg {string} yearPlaceholder
30218  * @cfg {string} dayFormat default 'd'
30219  * @cfg {string} monthFormat default 'm'
30220  * @cfg {string} yearFormat default 'Y'
30221  * @cfg {Number} labellg set the width of label (1-12)
30222  * @cfg {Number} labelmd set the width of label (1-12)
30223  * @cfg {Number} labelsm set the width of label (1-12)
30224  * @cfg {Number} labelxs set the width of label (1-12)
30225
30226  *     
30227  * @constructor
30228  * Create a new DateSplitField
30229  * @param {Object} config The config object
30230  */
30231
30232 Roo.bootstrap.DateSplitField = function(config){
30233     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30234     
30235     this.addEvents({
30236         // raw events
30237          /**
30238          * @event years
30239          * getting the data of years
30240          * @param {Roo.bootstrap.DateSplitField} this
30241          * @param {Object} years
30242          */
30243         "years" : true,
30244         /**
30245          * @event days
30246          * getting the data of days
30247          * @param {Roo.bootstrap.DateSplitField} this
30248          * @param {Object} days
30249          */
30250         "days" : true,
30251         /**
30252          * @event invalid
30253          * Fires after the field has been marked as invalid.
30254          * @param {Roo.form.Field} this
30255          * @param {String} msg The validation message
30256          */
30257         invalid : true,
30258        /**
30259          * @event valid
30260          * Fires after the field has been validated with no errors.
30261          * @param {Roo.form.Field} this
30262          */
30263         valid : true
30264     });
30265 };
30266
30267 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30268     
30269     fieldLabel : '',
30270     labelAlign : 'top',
30271     labelWidth : 3,
30272     dayAllowBlank : false,
30273     monthAllowBlank : false,
30274     yearAllowBlank : false,
30275     dayPlaceholder : '',
30276     monthPlaceholder : '',
30277     yearPlaceholder : '',
30278     dayFormat : 'd',
30279     monthFormat : 'm',
30280     yearFormat : 'Y',
30281     isFormField : true,
30282     labellg : 0,
30283     labelmd : 0,
30284     labelsm : 0,
30285     labelxs : 0,
30286     
30287     getAutoCreate : function()
30288     {
30289         var cfg = {
30290             tag : 'div',
30291             cls : 'row roo-date-split-field-group',
30292             cn : [
30293                 {
30294                     tag : 'input',
30295                     type : 'hidden',
30296                     cls : 'form-hidden-field roo-date-split-field-group-value',
30297                     name : this.name
30298                 }
30299             ]
30300         };
30301         
30302         var labelCls = 'col-md-12';
30303         var contentCls = 'col-md-4';
30304         
30305         if(this.fieldLabel){
30306             
30307             var label = {
30308                 tag : 'div',
30309                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30310                 cn : [
30311                     {
30312                         tag : 'label',
30313                         html : this.fieldLabel
30314                     }
30315                 ]
30316             };
30317             
30318             if(this.labelAlign == 'left'){
30319             
30320                 if(this.labelWidth > 12){
30321                     label.style = "width: " + this.labelWidth + 'px';
30322                 }
30323
30324                 if(this.labelWidth < 13 && this.labelmd == 0){
30325                     this.labelmd = this.labelWidth;
30326                 }
30327
30328                 if(this.labellg > 0){
30329                     labelCls = ' col-lg-' + this.labellg;
30330                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30331                 }
30332
30333                 if(this.labelmd > 0){
30334                     labelCls = ' col-md-' + this.labelmd;
30335                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30336                 }
30337
30338                 if(this.labelsm > 0){
30339                     labelCls = ' col-sm-' + this.labelsm;
30340                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30341                 }
30342
30343                 if(this.labelxs > 0){
30344                     labelCls = ' col-xs-' + this.labelxs;
30345                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30346                 }
30347             }
30348             
30349             label.cls += ' ' + labelCls;
30350             
30351             cfg.cn.push(label);
30352         }
30353         
30354         Roo.each(['day', 'month', 'year'], function(t){
30355             cfg.cn.push({
30356                 tag : 'div',
30357                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30358             });
30359         }, this);
30360         
30361         return cfg;
30362     },
30363     
30364     inputEl: function ()
30365     {
30366         return this.el.select('.roo-date-split-field-group-value', true).first();
30367     },
30368     
30369     onRender : function(ct, position) 
30370     {
30371         var _this = this;
30372         
30373         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30374         
30375         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30376         
30377         this.dayField = new Roo.bootstrap.ComboBox({
30378             allowBlank : this.dayAllowBlank,
30379             alwaysQuery : true,
30380             displayField : 'value',
30381             editable : false,
30382             fieldLabel : '',
30383             forceSelection : true,
30384             mode : 'local',
30385             placeholder : this.dayPlaceholder,
30386             selectOnFocus : true,
30387             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30388             triggerAction : 'all',
30389             typeAhead : true,
30390             valueField : 'value',
30391             store : new Roo.data.SimpleStore({
30392                 data : (function() {    
30393                     var days = [];
30394                     _this.fireEvent('days', _this, days);
30395                     return days;
30396                 })(),
30397                 fields : [ 'value' ]
30398             }),
30399             listeners : {
30400                 select : function (_self, record, index)
30401                 {
30402                     _this.setValue(_this.getValue());
30403                 }
30404             }
30405         });
30406
30407         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30408         
30409         this.monthField = new Roo.bootstrap.MonthField({
30410             after : '<i class=\"fa fa-calendar\"></i>',
30411             allowBlank : this.monthAllowBlank,
30412             placeholder : this.monthPlaceholder,
30413             readOnly : true,
30414             listeners : {
30415                 render : function (_self)
30416                 {
30417                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30418                         e.preventDefault();
30419                         _self.focus();
30420                     });
30421                 },
30422                 select : function (_self, oldvalue, newvalue)
30423                 {
30424                     _this.setValue(_this.getValue());
30425                 }
30426             }
30427         });
30428         
30429         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30430         
30431         this.yearField = new Roo.bootstrap.ComboBox({
30432             allowBlank : this.yearAllowBlank,
30433             alwaysQuery : true,
30434             displayField : 'value',
30435             editable : false,
30436             fieldLabel : '',
30437             forceSelection : true,
30438             mode : 'local',
30439             placeholder : this.yearPlaceholder,
30440             selectOnFocus : true,
30441             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30442             triggerAction : 'all',
30443             typeAhead : true,
30444             valueField : 'value',
30445             store : new Roo.data.SimpleStore({
30446                 data : (function() {
30447                     var years = [];
30448                     _this.fireEvent('years', _this, years);
30449                     return years;
30450                 })(),
30451                 fields : [ 'value' ]
30452             }),
30453             listeners : {
30454                 select : function (_self, record, index)
30455                 {
30456                     _this.setValue(_this.getValue());
30457                 }
30458             }
30459         });
30460
30461         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30462     },
30463     
30464     setValue : function(v, format)
30465     {
30466         this.inputEl.dom.value = v;
30467         
30468         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30469         
30470         var d = Date.parseDate(v, f);
30471         
30472         if(!d){
30473             this.validate();
30474             return;
30475         }
30476         
30477         this.setDay(d.format(this.dayFormat));
30478         this.setMonth(d.format(this.monthFormat));
30479         this.setYear(d.format(this.yearFormat));
30480         
30481         this.validate();
30482         
30483         return;
30484     },
30485     
30486     setDay : function(v)
30487     {
30488         this.dayField.setValue(v);
30489         this.inputEl.dom.value = this.getValue();
30490         this.validate();
30491         return;
30492     },
30493     
30494     setMonth : function(v)
30495     {
30496         this.monthField.setValue(v, true);
30497         this.inputEl.dom.value = this.getValue();
30498         this.validate();
30499         return;
30500     },
30501     
30502     setYear : function(v)
30503     {
30504         this.yearField.setValue(v);
30505         this.inputEl.dom.value = this.getValue();
30506         this.validate();
30507         return;
30508     },
30509     
30510     getDay : function()
30511     {
30512         return this.dayField.getValue();
30513     },
30514     
30515     getMonth : function()
30516     {
30517         return this.monthField.getValue();
30518     },
30519     
30520     getYear : function()
30521     {
30522         return this.yearField.getValue();
30523     },
30524     
30525     getValue : function()
30526     {
30527         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30528         
30529         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30530         
30531         return date;
30532     },
30533     
30534     reset : function()
30535     {
30536         this.setDay('');
30537         this.setMonth('');
30538         this.setYear('');
30539         this.inputEl.dom.value = '';
30540         this.validate();
30541         return;
30542     },
30543     
30544     validate : function()
30545     {
30546         var d = this.dayField.validate();
30547         var m = this.monthField.validate();
30548         var y = this.yearField.validate();
30549         
30550         var valid = true;
30551         
30552         if(
30553                 (!this.dayAllowBlank && !d) ||
30554                 (!this.monthAllowBlank && !m) ||
30555                 (!this.yearAllowBlank && !y)
30556         ){
30557             valid = false;
30558         }
30559         
30560         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30561             return valid;
30562         }
30563         
30564         if(valid){
30565             this.markValid();
30566             return valid;
30567         }
30568         
30569         this.markInvalid();
30570         
30571         return valid;
30572     },
30573     
30574     markValid : function()
30575     {
30576         
30577         var label = this.el.select('label', true).first();
30578         var icon = this.el.select('i.fa-star', true).first();
30579
30580         if(label && icon){
30581             icon.remove();
30582         }
30583         
30584         this.fireEvent('valid', this);
30585     },
30586     
30587      /**
30588      * Mark this field as invalid
30589      * @param {String} msg The validation message
30590      */
30591     markInvalid : function(msg)
30592     {
30593         
30594         var label = this.el.select('label', true).first();
30595         var icon = this.el.select('i.fa-star', true).first();
30596
30597         if(label && !icon){
30598             this.el.select('.roo-date-split-field-label', true).createChild({
30599                 tag : 'i',
30600                 cls : 'text-danger fa fa-lg fa-star',
30601                 tooltip : 'This field is required',
30602                 style : 'margin-right:5px;'
30603             }, label, true);
30604         }
30605         
30606         this.fireEvent('invalid', this, msg);
30607     },
30608     
30609     clearInvalid : function()
30610     {
30611         var label = this.el.select('label', true).first();
30612         var icon = this.el.select('i.fa-star', true).first();
30613
30614         if(label && icon){
30615             icon.remove();
30616         }
30617         
30618         this.fireEvent('valid', this);
30619     },
30620     
30621     getName: function()
30622     {
30623         return this.name;
30624     }
30625     
30626 });
30627
30628  /**
30629  *
30630  * This is based on 
30631  * http://masonry.desandro.com
30632  *
30633  * The idea is to render all the bricks based on vertical width...
30634  *
30635  * The original code extends 'outlayer' - we might need to use that....
30636  * 
30637  */
30638
30639
30640 /**
30641  * @class Roo.bootstrap.LayoutMasonry
30642  * @extends Roo.bootstrap.Component
30643  * Bootstrap Layout Masonry class
30644  * 
30645  * @constructor
30646  * Create a new Element
30647  * @param {Object} config The config object
30648  */
30649
30650 Roo.bootstrap.LayoutMasonry = function(config){
30651     
30652     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30653     
30654     this.bricks = [];
30655     
30656     Roo.bootstrap.LayoutMasonry.register(this);
30657     
30658     this.addEvents({
30659         // raw events
30660         /**
30661          * @event layout
30662          * Fire after layout the items
30663          * @param {Roo.bootstrap.LayoutMasonry} this
30664          * @param {Roo.EventObject} e
30665          */
30666         "layout" : true
30667     });
30668     
30669 };
30670
30671 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30672     
30673     /**
30674      * @cfg {Boolean} isLayoutInstant = no animation?
30675      */   
30676     isLayoutInstant : false, // needed?
30677    
30678     /**
30679      * @cfg {Number} boxWidth  width of the columns
30680      */   
30681     boxWidth : 450,
30682     
30683       /**
30684      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30685      */   
30686     boxHeight : 0,
30687     
30688     /**
30689      * @cfg {Number} padWidth padding below box..
30690      */   
30691     padWidth : 10, 
30692     
30693     /**
30694      * @cfg {Number} gutter gutter width..
30695      */   
30696     gutter : 10,
30697     
30698      /**
30699      * @cfg {Number} maxCols maximum number of columns
30700      */   
30701     
30702     maxCols: 0,
30703     
30704     /**
30705      * @cfg {Boolean} isAutoInitial defalut true
30706      */   
30707     isAutoInitial : true, 
30708     
30709     containerWidth: 0,
30710     
30711     /**
30712      * @cfg {Boolean} isHorizontal defalut false
30713      */   
30714     isHorizontal : false, 
30715
30716     currentSize : null,
30717     
30718     tag: 'div',
30719     
30720     cls: '',
30721     
30722     bricks: null, //CompositeElement
30723     
30724     cols : 1,
30725     
30726     _isLayoutInited : false,
30727     
30728 //    isAlternative : false, // only use for vertical layout...
30729     
30730     /**
30731      * @cfg {Number} alternativePadWidth padding below box..
30732      */   
30733     alternativePadWidth : 50,
30734     
30735     selectedBrick : [],
30736     
30737     getAutoCreate : function(){
30738         
30739         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30740         
30741         var cfg = {
30742             tag: this.tag,
30743             cls: 'blog-masonary-wrapper ' + this.cls,
30744             cn : {
30745                 cls : 'mas-boxes masonary'
30746             }
30747         };
30748         
30749         return cfg;
30750     },
30751     
30752     getChildContainer: function( )
30753     {
30754         if (this.boxesEl) {
30755             return this.boxesEl;
30756         }
30757         
30758         this.boxesEl = this.el.select('.mas-boxes').first();
30759         
30760         return this.boxesEl;
30761     },
30762     
30763     
30764     initEvents : function()
30765     {
30766         var _this = this;
30767         
30768         if(this.isAutoInitial){
30769             Roo.log('hook children rendered');
30770             this.on('childrenrendered', function() {
30771                 Roo.log('children rendered');
30772                 _this.initial();
30773             } ,this);
30774         }
30775     },
30776     
30777     initial : function()
30778     {
30779         this.selectedBrick = [];
30780         
30781         this.currentSize = this.el.getBox(true);
30782         
30783         Roo.EventManager.onWindowResize(this.resize, this); 
30784
30785         if(!this.isAutoInitial){
30786             this.layout();
30787             return;
30788         }
30789         
30790         this.layout();
30791         
30792         return;
30793         //this.layout.defer(500,this);
30794         
30795     },
30796     
30797     resize : function()
30798     {
30799         var cs = this.el.getBox(true);
30800         
30801         if (
30802                 this.currentSize.width == cs.width && 
30803                 this.currentSize.x == cs.x && 
30804                 this.currentSize.height == cs.height && 
30805                 this.currentSize.y == cs.y 
30806         ) {
30807             Roo.log("no change in with or X or Y");
30808             return;
30809         }
30810         
30811         this.currentSize = cs;
30812         
30813         this.layout();
30814         
30815     },
30816     
30817     layout : function()
30818     {   
30819         this._resetLayout();
30820         
30821         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30822         
30823         this.layoutItems( isInstant );
30824       
30825         this._isLayoutInited = true;
30826         
30827         this.fireEvent('layout', this);
30828         
30829     },
30830     
30831     _resetLayout : function()
30832     {
30833         if(this.isHorizontal){
30834             this.horizontalMeasureColumns();
30835             return;
30836         }
30837         
30838         this.verticalMeasureColumns();
30839         
30840     },
30841     
30842     verticalMeasureColumns : function()
30843     {
30844         this.getContainerWidth();
30845         
30846 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30847 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30848 //            return;
30849 //        }
30850         
30851         var boxWidth = this.boxWidth + this.padWidth;
30852         
30853         if(this.containerWidth < this.boxWidth){
30854             boxWidth = this.containerWidth
30855         }
30856         
30857         var containerWidth = this.containerWidth;
30858         
30859         var cols = Math.floor(containerWidth / boxWidth);
30860         
30861         this.cols = Math.max( cols, 1 );
30862         
30863         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30864         
30865         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30866         
30867         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30868         
30869         this.colWidth = boxWidth + avail - this.padWidth;
30870         
30871         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30872         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30873     },
30874     
30875     horizontalMeasureColumns : function()
30876     {
30877         this.getContainerWidth();
30878         
30879         var boxWidth = this.boxWidth;
30880         
30881         if(this.containerWidth < boxWidth){
30882             boxWidth = this.containerWidth;
30883         }
30884         
30885         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30886         
30887         this.el.setHeight(boxWidth);
30888         
30889     },
30890     
30891     getContainerWidth : function()
30892     {
30893         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30894     },
30895     
30896     layoutItems : function( isInstant )
30897     {
30898         Roo.log(this.bricks);
30899         
30900         var items = Roo.apply([], this.bricks);
30901         
30902         if(this.isHorizontal){
30903             this._horizontalLayoutItems( items , isInstant );
30904             return;
30905         }
30906         
30907 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30908 //            this._verticalAlternativeLayoutItems( items , isInstant );
30909 //            return;
30910 //        }
30911         
30912         this._verticalLayoutItems( items , isInstant );
30913         
30914     },
30915     
30916     _verticalLayoutItems : function ( items , isInstant)
30917     {
30918         if ( !items || !items.length ) {
30919             return;
30920         }
30921         
30922         var standard = [
30923             ['xs', 'xs', 'xs', 'tall'],
30924             ['xs', 'xs', 'tall'],
30925             ['xs', 'xs', 'sm'],
30926             ['xs', 'xs', 'xs'],
30927             ['xs', 'tall'],
30928             ['xs', 'sm'],
30929             ['xs', 'xs'],
30930             ['xs'],
30931             
30932             ['sm', 'xs', 'xs'],
30933             ['sm', 'xs'],
30934             ['sm'],
30935             
30936             ['tall', 'xs', 'xs', 'xs'],
30937             ['tall', 'xs', 'xs'],
30938             ['tall', 'xs'],
30939             ['tall']
30940             
30941         ];
30942         
30943         var queue = [];
30944         
30945         var boxes = [];
30946         
30947         var box = [];
30948         
30949         Roo.each(items, function(item, k){
30950             
30951             switch (item.size) {
30952                 // these layouts take up a full box,
30953                 case 'md' :
30954                 case 'md-left' :
30955                 case 'md-right' :
30956                 case 'wide' :
30957                     
30958                     if(box.length){
30959                         boxes.push(box);
30960                         box = [];
30961                     }
30962                     
30963                     boxes.push([item]);
30964                     
30965                     break;
30966                     
30967                 case 'xs' :
30968                 case 'sm' :
30969                 case 'tall' :
30970                     
30971                     box.push(item);
30972                     
30973                     break;
30974                 default :
30975                     break;
30976                     
30977             }
30978             
30979         }, this);
30980         
30981         if(box.length){
30982             boxes.push(box);
30983             box = [];
30984         }
30985         
30986         var filterPattern = function(box, length)
30987         {
30988             if(!box.length){
30989                 return;
30990             }
30991             
30992             var match = false;
30993             
30994             var pattern = box.slice(0, length);
30995             
30996             var format = [];
30997             
30998             Roo.each(pattern, function(i){
30999                 format.push(i.size);
31000             }, this);
31001             
31002             Roo.each(standard, function(s){
31003                 
31004                 if(String(s) != String(format)){
31005                     return;
31006                 }
31007                 
31008                 match = true;
31009                 return false;
31010                 
31011             }, this);
31012             
31013             if(!match && length == 1){
31014                 return;
31015             }
31016             
31017             if(!match){
31018                 filterPattern(box, length - 1);
31019                 return;
31020             }
31021                 
31022             queue.push(pattern);
31023
31024             box = box.slice(length, box.length);
31025
31026             filterPattern(box, 4);
31027
31028             return;
31029             
31030         }
31031         
31032         Roo.each(boxes, function(box, k){
31033             
31034             if(!box.length){
31035                 return;
31036             }
31037             
31038             if(box.length == 1){
31039                 queue.push(box);
31040                 return;
31041             }
31042             
31043             filterPattern(box, 4);
31044             
31045         }, this);
31046         
31047         this._processVerticalLayoutQueue( queue, isInstant );
31048         
31049     },
31050     
31051 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31052 //    {
31053 //        if ( !items || !items.length ) {
31054 //            return;
31055 //        }
31056 //
31057 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31058 //        
31059 //    },
31060     
31061     _horizontalLayoutItems : function ( items , isInstant)
31062     {
31063         if ( !items || !items.length || items.length < 3) {
31064             return;
31065         }
31066         
31067         items.reverse();
31068         
31069         var eItems = items.slice(0, 3);
31070         
31071         items = items.slice(3, items.length);
31072         
31073         var standard = [
31074             ['xs', 'xs', 'xs', 'wide'],
31075             ['xs', 'xs', 'wide'],
31076             ['xs', 'xs', 'sm'],
31077             ['xs', 'xs', 'xs'],
31078             ['xs', 'wide'],
31079             ['xs', 'sm'],
31080             ['xs', 'xs'],
31081             ['xs'],
31082             
31083             ['sm', 'xs', 'xs'],
31084             ['sm', 'xs'],
31085             ['sm'],
31086             
31087             ['wide', 'xs', 'xs', 'xs'],
31088             ['wide', 'xs', 'xs'],
31089             ['wide', 'xs'],
31090             ['wide'],
31091             
31092             ['wide-thin']
31093         ];
31094         
31095         var queue = [];
31096         
31097         var boxes = [];
31098         
31099         var box = [];
31100         
31101         Roo.each(items, function(item, k){
31102             
31103             switch (item.size) {
31104                 case 'md' :
31105                 case 'md-left' :
31106                 case 'md-right' :
31107                 case 'tall' :
31108                     
31109                     if(box.length){
31110                         boxes.push(box);
31111                         box = [];
31112                     }
31113                     
31114                     boxes.push([item]);
31115                     
31116                     break;
31117                     
31118                 case 'xs' :
31119                 case 'sm' :
31120                 case 'wide' :
31121                 case 'wide-thin' :
31122                     
31123                     box.push(item);
31124                     
31125                     break;
31126                 default :
31127                     break;
31128                     
31129             }
31130             
31131         }, this);
31132         
31133         if(box.length){
31134             boxes.push(box);
31135             box = [];
31136         }
31137         
31138         var filterPattern = function(box, length)
31139         {
31140             if(!box.length){
31141                 return;
31142             }
31143             
31144             var match = false;
31145             
31146             var pattern = box.slice(0, length);
31147             
31148             var format = [];
31149             
31150             Roo.each(pattern, function(i){
31151                 format.push(i.size);
31152             }, this);
31153             
31154             Roo.each(standard, function(s){
31155                 
31156                 if(String(s) != String(format)){
31157                     return;
31158                 }
31159                 
31160                 match = true;
31161                 return false;
31162                 
31163             }, this);
31164             
31165             if(!match && length == 1){
31166                 return;
31167             }
31168             
31169             if(!match){
31170                 filterPattern(box, length - 1);
31171                 return;
31172             }
31173                 
31174             queue.push(pattern);
31175
31176             box = box.slice(length, box.length);
31177
31178             filterPattern(box, 4);
31179
31180             return;
31181             
31182         }
31183         
31184         Roo.each(boxes, function(box, k){
31185             
31186             if(!box.length){
31187                 return;
31188             }
31189             
31190             if(box.length == 1){
31191                 queue.push(box);
31192                 return;
31193             }
31194             
31195             filterPattern(box, 4);
31196             
31197         }, this);
31198         
31199         
31200         var prune = [];
31201         
31202         var pos = this.el.getBox(true);
31203         
31204         var minX = pos.x;
31205         
31206         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31207         
31208         var hit_end = false;
31209         
31210         Roo.each(queue, function(box){
31211             
31212             if(hit_end){
31213                 
31214                 Roo.each(box, function(b){
31215                 
31216                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31217                     b.el.hide();
31218
31219                 }, this);
31220
31221                 return;
31222             }
31223             
31224             var mx = 0;
31225             
31226             Roo.each(box, function(b){
31227                 
31228                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31229                 b.el.show();
31230
31231                 mx = Math.max(mx, b.x);
31232                 
31233             }, this);
31234             
31235             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31236             
31237             if(maxX < minX){
31238                 
31239                 Roo.each(box, function(b){
31240                 
31241                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31242                     b.el.hide();
31243                     
31244                 }, this);
31245                 
31246                 hit_end = true;
31247                 
31248                 return;
31249             }
31250             
31251             prune.push(box);
31252             
31253         }, this);
31254         
31255         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31256     },
31257     
31258     /** Sets position of item in DOM
31259     * @param {Element} item
31260     * @param {Number} x - horizontal position
31261     * @param {Number} y - vertical position
31262     * @param {Boolean} isInstant - disables transitions
31263     */
31264     _processVerticalLayoutQueue : function( queue, isInstant )
31265     {
31266         var pos = this.el.getBox(true);
31267         var x = pos.x;
31268         var y = pos.y;
31269         var maxY = [];
31270         
31271         for (var i = 0; i < this.cols; i++){
31272             maxY[i] = pos.y;
31273         }
31274         
31275         Roo.each(queue, function(box, k){
31276             
31277             var col = k % this.cols;
31278             
31279             Roo.each(box, function(b,kk){
31280                 
31281                 b.el.position('absolute');
31282                 
31283                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31284                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31285                 
31286                 if(b.size == 'md-left' || b.size == 'md-right'){
31287                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31288                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31289                 }
31290                 
31291                 b.el.setWidth(width);
31292                 b.el.setHeight(height);
31293                 // iframe?
31294                 b.el.select('iframe',true).setSize(width,height);
31295                 
31296             }, this);
31297             
31298             for (var i = 0; i < this.cols; i++){
31299                 
31300                 if(maxY[i] < maxY[col]){
31301                     col = i;
31302                     continue;
31303                 }
31304                 
31305                 col = Math.min(col, i);
31306                 
31307             }
31308             
31309             x = pos.x + col * (this.colWidth + this.padWidth);
31310             
31311             y = maxY[col];
31312             
31313             var positions = [];
31314             
31315             switch (box.length){
31316                 case 1 :
31317                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31318                     break;
31319                 case 2 :
31320                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31321                     break;
31322                 case 3 :
31323                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31324                     break;
31325                 case 4 :
31326                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31327                     break;
31328                 default :
31329                     break;
31330             }
31331             
31332             Roo.each(box, function(b,kk){
31333                 
31334                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31335                 
31336                 var sz = b.el.getSize();
31337                 
31338                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31339                 
31340             }, this);
31341             
31342         }, this);
31343         
31344         var mY = 0;
31345         
31346         for (var i = 0; i < this.cols; i++){
31347             mY = Math.max(mY, maxY[i]);
31348         }
31349         
31350         this.el.setHeight(mY - pos.y);
31351         
31352     },
31353     
31354 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31355 //    {
31356 //        var pos = this.el.getBox(true);
31357 //        var x = pos.x;
31358 //        var y = pos.y;
31359 //        var maxX = pos.right;
31360 //        
31361 //        var maxHeight = 0;
31362 //        
31363 //        Roo.each(items, function(item, k){
31364 //            
31365 //            var c = k % 2;
31366 //            
31367 //            item.el.position('absolute');
31368 //                
31369 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31370 //
31371 //            item.el.setWidth(width);
31372 //
31373 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31374 //
31375 //            item.el.setHeight(height);
31376 //            
31377 //            if(c == 0){
31378 //                item.el.setXY([x, y], isInstant ? false : true);
31379 //            } else {
31380 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31381 //            }
31382 //            
31383 //            y = y + height + this.alternativePadWidth;
31384 //            
31385 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31386 //            
31387 //        }, this);
31388 //        
31389 //        this.el.setHeight(maxHeight);
31390 //        
31391 //    },
31392     
31393     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31394     {
31395         var pos = this.el.getBox(true);
31396         
31397         var minX = pos.x;
31398         var minY = pos.y;
31399         
31400         var maxX = pos.right;
31401         
31402         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31403         
31404         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31405         
31406         Roo.each(queue, function(box, k){
31407             
31408             Roo.each(box, function(b, kk){
31409                 
31410                 b.el.position('absolute');
31411                 
31412                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31413                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31414                 
31415                 if(b.size == 'md-left' || b.size == 'md-right'){
31416                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31417                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31418                 }
31419                 
31420                 b.el.setWidth(width);
31421                 b.el.setHeight(height);
31422                 
31423             }, this);
31424             
31425             if(!box.length){
31426                 return;
31427             }
31428             
31429             var positions = [];
31430             
31431             switch (box.length){
31432                 case 1 :
31433                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31434                     break;
31435                 case 2 :
31436                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31437                     break;
31438                 case 3 :
31439                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31440                     break;
31441                 case 4 :
31442                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31443                     break;
31444                 default :
31445                     break;
31446             }
31447             
31448             Roo.each(box, function(b,kk){
31449                 
31450                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31451                 
31452                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31453                 
31454             }, this);
31455             
31456         }, this);
31457         
31458     },
31459     
31460     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31461     {
31462         Roo.each(eItems, function(b,k){
31463             
31464             b.size = (k == 0) ? 'sm' : 'xs';
31465             b.x = (k == 0) ? 2 : 1;
31466             b.y = (k == 0) ? 2 : 1;
31467             
31468             b.el.position('absolute');
31469             
31470             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31471                 
31472             b.el.setWidth(width);
31473             
31474             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31475             
31476             b.el.setHeight(height);
31477             
31478         }, this);
31479
31480         var positions = [];
31481         
31482         positions.push({
31483             x : maxX - this.unitWidth * 2 - this.gutter,
31484             y : minY
31485         });
31486         
31487         positions.push({
31488             x : maxX - this.unitWidth,
31489             y : minY + (this.unitWidth + this.gutter) * 2
31490         });
31491         
31492         positions.push({
31493             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31494             y : minY
31495         });
31496         
31497         Roo.each(eItems, function(b,k){
31498             
31499             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31500
31501         }, this);
31502         
31503     },
31504     
31505     getVerticalOneBoxColPositions : function(x, y, box)
31506     {
31507         var pos = [];
31508         
31509         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31510         
31511         if(box[0].size == 'md-left'){
31512             rand = 0;
31513         }
31514         
31515         if(box[0].size == 'md-right'){
31516             rand = 1;
31517         }
31518         
31519         pos.push({
31520             x : x + (this.unitWidth + this.gutter) * rand,
31521             y : y
31522         });
31523         
31524         return pos;
31525     },
31526     
31527     getVerticalTwoBoxColPositions : function(x, y, box)
31528     {
31529         var pos = [];
31530         
31531         if(box[0].size == 'xs'){
31532             
31533             pos.push({
31534                 x : x,
31535                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31536             });
31537
31538             pos.push({
31539                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31540                 y : y
31541             });
31542             
31543             return pos;
31544             
31545         }
31546         
31547         pos.push({
31548             x : x,
31549             y : y
31550         });
31551
31552         pos.push({
31553             x : x + (this.unitWidth + this.gutter) * 2,
31554             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31555         });
31556         
31557         return pos;
31558         
31559     },
31560     
31561     getVerticalThreeBoxColPositions : function(x, y, box)
31562     {
31563         var pos = [];
31564         
31565         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31566             
31567             pos.push({
31568                 x : x,
31569                 y : y
31570             });
31571
31572             pos.push({
31573                 x : x + (this.unitWidth + this.gutter) * 1,
31574                 y : y
31575             });
31576             
31577             pos.push({
31578                 x : x + (this.unitWidth + this.gutter) * 2,
31579                 y : y
31580             });
31581             
31582             return pos;
31583             
31584         }
31585         
31586         if(box[0].size == 'xs' && box[1].size == 'xs'){
31587             
31588             pos.push({
31589                 x : x,
31590                 y : y
31591             });
31592
31593             pos.push({
31594                 x : x,
31595                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31596             });
31597             
31598             pos.push({
31599                 x : x + (this.unitWidth + this.gutter) * 1,
31600                 y : y
31601             });
31602             
31603             return pos;
31604             
31605         }
31606         
31607         pos.push({
31608             x : x,
31609             y : y
31610         });
31611
31612         pos.push({
31613             x : x + (this.unitWidth + this.gutter) * 2,
31614             y : y
31615         });
31616
31617         pos.push({
31618             x : x + (this.unitWidth + this.gutter) * 2,
31619             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31620         });
31621             
31622         return pos;
31623         
31624     },
31625     
31626     getVerticalFourBoxColPositions : function(x, y, box)
31627     {
31628         var pos = [];
31629         
31630         if(box[0].size == 'xs'){
31631             
31632             pos.push({
31633                 x : x,
31634                 y : y
31635             });
31636
31637             pos.push({
31638                 x : x,
31639                 y : y + (this.unitHeight + this.gutter) * 1
31640             });
31641             
31642             pos.push({
31643                 x : x,
31644                 y : y + (this.unitHeight + this.gutter) * 2
31645             });
31646             
31647             pos.push({
31648                 x : x + (this.unitWidth + this.gutter) * 1,
31649                 y : y
31650             });
31651             
31652             return pos;
31653             
31654         }
31655         
31656         pos.push({
31657             x : x,
31658             y : y
31659         });
31660
31661         pos.push({
31662             x : x + (this.unitWidth + this.gutter) * 2,
31663             y : y
31664         });
31665
31666         pos.push({
31667             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31668             y : y + (this.unitHeight + this.gutter) * 1
31669         });
31670
31671         pos.push({
31672             x : x + (this.unitWidth + this.gutter) * 2,
31673             y : y + (this.unitWidth + this.gutter) * 2
31674         });
31675
31676         return pos;
31677         
31678     },
31679     
31680     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31681     {
31682         var pos = [];
31683         
31684         if(box[0].size == 'md-left'){
31685             pos.push({
31686                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31687                 y : minY
31688             });
31689             
31690             return pos;
31691         }
31692         
31693         if(box[0].size == 'md-right'){
31694             pos.push({
31695                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31696                 y : minY + (this.unitWidth + this.gutter) * 1
31697             });
31698             
31699             return pos;
31700         }
31701         
31702         var rand = Math.floor(Math.random() * (4 - box[0].y));
31703         
31704         pos.push({
31705             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31706             y : minY + (this.unitWidth + this.gutter) * rand
31707         });
31708         
31709         return pos;
31710         
31711     },
31712     
31713     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31714     {
31715         var pos = [];
31716         
31717         if(box[0].size == 'xs'){
31718             
31719             pos.push({
31720                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31721                 y : minY
31722             });
31723
31724             pos.push({
31725                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31726                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31727             });
31728             
31729             return pos;
31730             
31731         }
31732         
31733         pos.push({
31734             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31735             y : minY
31736         });
31737
31738         pos.push({
31739             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31740             y : minY + (this.unitWidth + this.gutter) * 2
31741         });
31742         
31743         return pos;
31744         
31745     },
31746     
31747     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31748     {
31749         var pos = [];
31750         
31751         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31752             
31753             pos.push({
31754                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31755                 y : minY
31756             });
31757
31758             pos.push({
31759                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31760                 y : minY + (this.unitWidth + this.gutter) * 1
31761             });
31762             
31763             pos.push({
31764                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31765                 y : minY + (this.unitWidth + this.gutter) * 2
31766             });
31767             
31768             return pos;
31769             
31770         }
31771         
31772         if(box[0].size == 'xs' && box[1].size == 'xs'){
31773             
31774             pos.push({
31775                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31776                 y : minY
31777             });
31778
31779             pos.push({
31780                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31781                 y : minY
31782             });
31783             
31784             pos.push({
31785                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31786                 y : minY + (this.unitWidth + this.gutter) * 1
31787             });
31788             
31789             return pos;
31790             
31791         }
31792         
31793         pos.push({
31794             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31795             y : minY
31796         });
31797
31798         pos.push({
31799             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31800             y : minY + (this.unitWidth + this.gutter) * 2
31801         });
31802
31803         pos.push({
31804             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31805             y : minY + (this.unitWidth + this.gutter) * 2
31806         });
31807             
31808         return pos;
31809         
31810     },
31811     
31812     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31813     {
31814         var pos = [];
31815         
31816         if(box[0].size == 'xs'){
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[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31825                 y : minY
31826             });
31827             
31828             pos.push({
31829                 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),
31830                 y : minY
31831             });
31832             
31833             pos.push({
31834                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31835                 y : minY + (this.unitWidth + this.gutter) * 1
31836             });
31837             
31838             return pos;
31839             
31840         }
31841         
31842         pos.push({
31843             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31844             y : minY
31845         });
31846         
31847         pos.push({
31848             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31849             y : minY + (this.unitWidth + this.gutter) * 2
31850         });
31851         
31852         pos.push({
31853             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31854             y : minY + (this.unitWidth + this.gutter) * 2
31855         });
31856         
31857         pos.push({
31858             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),
31859             y : minY + (this.unitWidth + this.gutter) * 2
31860         });
31861
31862         return pos;
31863         
31864     },
31865     
31866     /**
31867     * remove a Masonry Brick
31868     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31869     */
31870     removeBrick : function(brick_id)
31871     {
31872         if (!brick_id) {
31873             return;
31874         }
31875         
31876         for (var i = 0; i<this.bricks.length; i++) {
31877             if (this.bricks[i].id == brick_id) {
31878                 this.bricks.splice(i,1);
31879                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31880                 this.initial();
31881             }
31882         }
31883     },
31884     
31885     /**
31886     * adds a Masonry Brick
31887     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31888     */
31889     addBrick : function(cfg)
31890     {
31891         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31892         //this.register(cn);
31893         cn.parentId = this.id;
31894         cn.onRender(this.el, null);
31895         return cn;
31896     },
31897     
31898     /**
31899     * register a Masonry Brick
31900     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31901     */
31902     
31903     register : function(brick)
31904     {
31905         this.bricks.push(brick);
31906         brick.masonryId = this.id;
31907     },
31908     
31909     /**
31910     * clear all the Masonry Brick
31911     */
31912     clearAll : function()
31913     {
31914         this.bricks = [];
31915         //this.getChildContainer().dom.innerHTML = "";
31916         this.el.dom.innerHTML = '';
31917     },
31918     
31919     getSelected : function()
31920     {
31921         if (!this.selectedBrick) {
31922             return false;
31923         }
31924         
31925         return this.selectedBrick;
31926     }
31927 });
31928
31929 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31930     
31931     groups: {},
31932      /**
31933     * register a Masonry Layout
31934     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31935     */
31936     
31937     register : function(layout)
31938     {
31939         this.groups[layout.id] = layout;
31940     },
31941     /**
31942     * fetch a  Masonry Layout based on the masonry layout ID
31943     * @param {string} the masonry layout to add
31944     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31945     */
31946     
31947     get: function(layout_id) {
31948         if (typeof(this.groups[layout_id]) == 'undefined') {
31949             return false;
31950         }
31951         return this.groups[layout_id] ;
31952     }
31953     
31954     
31955     
31956 });
31957
31958  
31959
31960  /**
31961  *
31962  * This is based on 
31963  * http://masonry.desandro.com
31964  *
31965  * The idea is to render all the bricks based on vertical width...
31966  *
31967  * The original code extends 'outlayer' - we might need to use that....
31968  * 
31969  */
31970
31971
31972 /**
31973  * @class Roo.bootstrap.LayoutMasonryAuto
31974  * @extends Roo.bootstrap.Component
31975  * Bootstrap Layout Masonry class
31976  * 
31977  * @constructor
31978  * Create a new Element
31979  * @param {Object} config The config object
31980  */
31981
31982 Roo.bootstrap.LayoutMasonryAuto = function(config){
31983     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31984 };
31985
31986 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31987     
31988       /**
31989      * @cfg {Boolean} isFitWidth  - resize the width..
31990      */   
31991     isFitWidth : false,  // options..
31992     /**
31993      * @cfg {Boolean} isOriginLeft = left align?
31994      */   
31995     isOriginLeft : true,
31996     /**
31997      * @cfg {Boolean} isOriginTop = top align?
31998      */   
31999     isOriginTop : false,
32000     /**
32001      * @cfg {Boolean} isLayoutInstant = no animation?
32002      */   
32003     isLayoutInstant : false, // needed?
32004     /**
32005      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32006      */   
32007     isResizingContainer : true,
32008     /**
32009      * @cfg {Number} columnWidth  width of the columns 
32010      */   
32011     
32012     columnWidth : 0,
32013     
32014     /**
32015      * @cfg {Number} maxCols maximum number of columns
32016      */   
32017     
32018     maxCols: 0,
32019     /**
32020      * @cfg {Number} padHeight padding below box..
32021      */   
32022     
32023     padHeight : 10, 
32024     
32025     /**
32026      * @cfg {Boolean} isAutoInitial defalut true
32027      */   
32028     
32029     isAutoInitial : true, 
32030     
32031     // private?
32032     gutter : 0,
32033     
32034     containerWidth: 0,
32035     initialColumnWidth : 0,
32036     currentSize : null,
32037     
32038     colYs : null, // array.
32039     maxY : 0,
32040     padWidth: 10,
32041     
32042     
32043     tag: 'div',
32044     cls: '',
32045     bricks: null, //CompositeElement
32046     cols : 0, // array?
32047     // element : null, // wrapped now this.el
32048     _isLayoutInited : null, 
32049     
32050     
32051     getAutoCreate : function(){
32052         
32053         var cfg = {
32054             tag: this.tag,
32055             cls: 'blog-masonary-wrapper ' + this.cls,
32056             cn : {
32057                 cls : 'mas-boxes masonary'
32058             }
32059         };
32060         
32061         return cfg;
32062     },
32063     
32064     getChildContainer: function( )
32065     {
32066         if (this.boxesEl) {
32067             return this.boxesEl;
32068         }
32069         
32070         this.boxesEl = this.el.select('.mas-boxes').first();
32071         
32072         return this.boxesEl;
32073     },
32074     
32075     
32076     initEvents : function()
32077     {
32078         var _this = this;
32079         
32080         if(this.isAutoInitial){
32081             Roo.log('hook children rendered');
32082             this.on('childrenrendered', function() {
32083                 Roo.log('children rendered');
32084                 _this.initial();
32085             } ,this);
32086         }
32087         
32088     },
32089     
32090     initial : function()
32091     {
32092         this.reloadItems();
32093
32094         this.currentSize = this.el.getBox(true);
32095
32096         /// was window resize... - let's see if this works..
32097         Roo.EventManager.onWindowResize(this.resize, this); 
32098
32099         if(!this.isAutoInitial){
32100             this.layout();
32101             return;
32102         }
32103         
32104         this.layout.defer(500,this);
32105     },
32106     
32107     reloadItems: function()
32108     {
32109         this.bricks = this.el.select('.masonry-brick', true);
32110         
32111         this.bricks.each(function(b) {
32112             //Roo.log(b.getSize());
32113             if (!b.attr('originalwidth')) {
32114                 b.attr('originalwidth',  b.getSize().width);
32115             }
32116             
32117         });
32118         
32119         Roo.log(this.bricks.elements.length);
32120     },
32121     
32122     resize : function()
32123     {
32124         Roo.log('resize');
32125         var cs = this.el.getBox(true);
32126         
32127         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32128             Roo.log("no change in with or X");
32129             return;
32130         }
32131         this.currentSize = cs;
32132         this.layout();
32133     },
32134     
32135     layout : function()
32136     {
32137          Roo.log('layout');
32138         this._resetLayout();
32139         //this._manageStamps();
32140       
32141         // don't animate first layout
32142         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32143         this.layoutItems( isInstant );
32144       
32145         // flag for initalized
32146         this._isLayoutInited = true;
32147     },
32148     
32149     layoutItems : function( isInstant )
32150     {
32151         //var items = this._getItemsForLayout( this.items );
32152         // original code supports filtering layout items.. we just ignore it..
32153         
32154         this._layoutItems( this.bricks , isInstant );
32155       
32156         this._postLayout();
32157     },
32158     _layoutItems : function ( items , isInstant)
32159     {
32160        //this.fireEvent( 'layout', this, items );
32161     
32162
32163         if ( !items || !items.elements.length ) {
32164           // no items, emit event with empty array
32165             return;
32166         }
32167
32168         var queue = [];
32169         items.each(function(item) {
32170             Roo.log("layout item");
32171             Roo.log(item);
32172             // get x/y object from method
32173             var position = this._getItemLayoutPosition( item );
32174             // enqueue
32175             position.item = item;
32176             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32177             queue.push( position );
32178         }, this);
32179       
32180         this._processLayoutQueue( queue );
32181     },
32182     /** Sets position of item in DOM
32183     * @param {Element} item
32184     * @param {Number} x - horizontal position
32185     * @param {Number} y - vertical position
32186     * @param {Boolean} isInstant - disables transitions
32187     */
32188     _processLayoutQueue : function( queue )
32189     {
32190         for ( var i=0, len = queue.length; i < len; i++ ) {
32191             var obj = queue[i];
32192             obj.item.position('absolute');
32193             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32194         }
32195     },
32196       
32197     
32198     /**
32199     * Any logic you want to do after each layout,
32200     * i.e. size the container
32201     */
32202     _postLayout : function()
32203     {
32204         this.resizeContainer();
32205     },
32206     
32207     resizeContainer : function()
32208     {
32209         if ( !this.isResizingContainer ) {
32210             return;
32211         }
32212         var size = this._getContainerSize();
32213         if ( size ) {
32214             this.el.setSize(size.width,size.height);
32215             this.boxesEl.setSize(size.width,size.height);
32216         }
32217     },
32218     
32219     
32220     
32221     _resetLayout : function()
32222     {
32223         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32224         this.colWidth = this.el.getWidth();
32225         //this.gutter = this.el.getWidth(); 
32226         
32227         this.measureColumns();
32228
32229         // reset column Y
32230         var i = this.cols;
32231         this.colYs = [];
32232         while (i--) {
32233             this.colYs.push( 0 );
32234         }
32235     
32236         this.maxY = 0;
32237     },
32238
32239     measureColumns : function()
32240     {
32241         this.getContainerWidth();
32242       // if columnWidth is 0, default to outerWidth of first item
32243         if ( !this.columnWidth ) {
32244             var firstItem = this.bricks.first();
32245             Roo.log(firstItem);
32246             this.columnWidth  = this.containerWidth;
32247             if (firstItem && firstItem.attr('originalwidth') ) {
32248                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32249             }
32250             // columnWidth fall back to item of first element
32251             Roo.log("set column width?");
32252                         this.initialColumnWidth = this.columnWidth  ;
32253
32254             // if first elem has no width, default to size of container
32255             
32256         }
32257         
32258         
32259         if (this.initialColumnWidth) {
32260             this.columnWidth = this.initialColumnWidth;
32261         }
32262         
32263         
32264             
32265         // column width is fixed at the top - however if container width get's smaller we should
32266         // reduce it...
32267         
32268         // this bit calcs how man columns..
32269             
32270         var columnWidth = this.columnWidth += this.gutter;
32271       
32272         // calculate columns
32273         var containerWidth = this.containerWidth + this.gutter;
32274         
32275         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32276         // fix rounding errors, typically with gutters
32277         var excess = columnWidth - containerWidth % columnWidth;
32278         
32279         
32280         // if overshoot is less than a pixel, round up, otherwise floor it
32281         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32282         cols = Math[ mathMethod ]( cols );
32283         this.cols = Math.max( cols, 1 );
32284         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32285         
32286          // padding positioning..
32287         var totalColWidth = this.cols * this.columnWidth;
32288         var padavail = this.containerWidth - totalColWidth;
32289         // so for 2 columns - we need 3 'pads'
32290         
32291         var padNeeded = (1+this.cols) * this.padWidth;
32292         
32293         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32294         
32295         this.columnWidth += padExtra
32296         //this.padWidth = Math.floor(padavail /  ( this.cols));
32297         
32298         // adjust colum width so that padding is fixed??
32299         
32300         // we have 3 columns ... total = width * 3
32301         // we have X left over... that should be used by 
32302         
32303         //if (this.expandC) {
32304             
32305         //}
32306         
32307         
32308         
32309     },
32310     
32311     getContainerWidth : function()
32312     {
32313        /* // container is parent if fit width
32314         var container = this.isFitWidth ? this.element.parentNode : this.element;
32315         // check that this.size and size are there
32316         // IE8 triggers resize on body size change, so they might not be
32317         
32318         var size = getSize( container );  //FIXME
32319         this.containerWidth = size && size.innerWidth; //FIXME
32320         */
32321          
32322         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32323         
32324     },
32325     
32326     _getItemLayoutPosition : function( item )  // what is item?
32327     {
32328         // we resize the item to our columnWidth..
32329       
32330         item.setWidth(this.columnWidth);
32331         item.autoBoxAdjust  = false;
32332         
32333         var sz = item.getSize();
32334  
32335         // how many columns does this brick span
32336         var remainder = this.containerWidth % this.columnWidth;
32337         
32338         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32339         // round if off by 1 pixel, otherwise use ceil
32340         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32341         colSpan = Math.min( colSpan, this.cols );
32342         
32343         // normally this should be '1' as we dont' currently allow multi width columns..
32344         
32345         var colGroup = this._getColGroup( colSpan );
32346         // get the minimum Y value from the columns
32347         var minimumY = Math.min.apply( Math, colGroup );
32348         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32349         
32350         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32351          
32352         // position the brick
32353         var position = {
32354             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32355             y: this.currentSize.y + minimumY + this.padHeight
32356         };
32357         
32358         Roo.log(position);
32359         // apply setHeight to necessary columns
32360         var setHeight = minimumY + sz.height + this.padHeight;
32361         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32362         
32363         var setSpan = this.cols + 1 - colGroup.length;
32364         for ( var i = 0; i < setSpan; i++ ) {
32365           this.colYs[ shortColIndex + i ] = setHeight ;
32366         }
32367       
32368         return position;
32369     },
32370     
32371     /**
32372      * @param {Number} colSpan - number of columns the element spans
32373      * @returns {Array} colGroup
32374      */
32375     _getColGroup : function( colSpan )
32376     {
32377         if ( colSpan < 2 ) {
32378           // if brick spans only one column, use all the column Ys
32379           return this.colYs;
32380         }
32381       
32382         var colGroup = [];
32383         // how many different places could this brick fit horizontally
32384         var groupCount = this.cols + 1 - colSpan;
32385         // for each group potential horizontal position
32386         for ( var i = 0; i < groupCount; i++ ) {
32387           // make an array of colY values for that one group
32388           var groupColYs = this.colYs.slice( i, i + colSpan );
32389           // and get the max value of the array
32390           colGroup[i] = Math.max.apply( Math, groupColYs );
32391         }
32392         return colGroup;
32393     },
32394     /*
32395     _manageStamp : function( stamp )
32396     {
32397         var stampSize =  stamp.getSize();
32398         var offset = stamp.getBox();
32399         // get the columns that this stamp affects
32400         var firstX = this.isOriginLeft ? offset.x : offset.right;
32401         var lastX = firstX + stampSize.width;
32402         var firstCol = Math.floor( firstX / this.columnWidth );
32403         firstCol = Math.max( 0, firstCol );
32404         
32405         var lastCol = Math.floor( lastX / this.columnWidth );
32406         // lastCol should not go over if multiple of columnWidth #425
32407         lastCol -= lastX % this.columnWidth ? 0 : 1;
32408         lastCol = Math.min( this.cols - 1, lastCol );
32409         
32410         // set colYs to bottom of the stamp
32411         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32412             stampSize.height;
32413             
32414         for ( var i = firstCol; i <= lastCol; i++ ) {
32415           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32416         }
32417     },
32418     */
32419     
32420     _getContainerSize : function()
32421     {
32422         this.maxY = Math.max.apply( Math, this.colYs );
32423         var size = {
32424             height: this.maxY
32425         };
32426       
32427         if ( this.isFitWidth ) {
32428             size.width = this._getContainerFitWidth();
32429         }
32430       
32431         return size;
32432     },
32433     
32434     _getContainerFitWidth : function()
32435     {
32436         var unusedCols = 0;
32437         // count unused columns
32438         var i = this.cols;
32439         while ( --i ) {
32440           if ( this.colYs[i] !== 0 ) {
32441             break;
32442           }
32443           unusedCols++;
32444         }
32445         // fit container to columns that have been used
32446         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32447     },
32448     
32449     needsResizeLayout : function()
32450     {
32451         var previousWidth = this.containerWidth;
32452         this.getContainerWidth();
32453         return previousWidth !== this.containerWidth;
32454     }
32455  
32456 });
32457
32458  
32459
32460  /*
32461  * - LGPL
32462  *
32463  * element
32464  * 
32465  */
32466
32467 /**
32468  * @class Roo.bootstrap.MasonryBrick
32469  * @extends Roo.bootstrap.Component
32470  * Bootstrap MasonryBrick class
32471  * 
32472  * @constructor
32473  * Create a new MasonryBrick
32474  * @param {Object} config The config object
32475  */
32476
32477 Roo.bootstrap.MasonryBrick = function(config){
32478     
32479     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32480     
32481     Roo.bootstrap.MasonryBrick.register(this);
32482     
32483     this.addEvents({
32484         // raw events
32485         /**
32486          * @event click
32487          * When a MasonryBrick is clcik
32488          * @param {Roo.bootstrap.MasonryBrick} this
32489          * @param {Roo.EventObject} e
32490          */
32491         "click" : true
32492     });
32493 };
32494
32495 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32496     
32497     /**
32498      * @cfg {String} title
32499      */   
32500     title : '',
32501     /**
32502      * @cfg {String} html
32503      */   
32504     html : '',
32505     /**
32506      * @cfg {String} bgimage
32507      */   
32508     bgimage : '',
32509     /**
32510      * @cfg {String} videourl
32511      */   
32512     videourl : '',
32513     /**
32514      * @cfg {String} cls
32515      */   
32516     cls : '',
32517     /**
32518      * @cfg {String} href
32519      */   
32520     href : '',
32521     /**
32522      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32523      */   
32524     size : 'xs',
32525     
32526     /**
32527      * @cfg {String} placetitle (center|bottom)
32528      */   
32529     placetitle : '',
32530     
32531     /**
32532      * @cfg {Boolean} isFitContainer defalut true
32533      */   
32534     isFitContainer : true, 
32535     
32536     /**
32537      * @cfg {Boolean} preventDefault defalut false
32538      */   
32539     preventDefault : false, 
32540     
32541     /**
32542      * @cfg {Boolean} inverse defalut false
32543      */   
32544     maskInverse : false, 
32545     
32546     getAutoCreate : function()
32547     {
32548         if(!this.isFitContainer){
32549             return this.getSplitAutoCreate();
32550         }
32551         
32552         var cls = 'masonry-brick masonry-brick-full';
32553         
32554         if(this.href.length){
32555             cls += ' masonry-brick-link';
32556         }
32557         
32558         if(this.bgimage.length){
32559             cls += ' masonry-brick-image';
32560         }
32561         
32562         if(this.maskInverse){
32563             cls += ' mask-inverse';
32564         }
32565         
32566         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32567             cls += ' enable-mask';
32568         }
32569         
32570         if(this.size){
32571             cls += ' masonry-' + this.size + '-brick';
32572         }
32573         
32574         if(this.placetitle.length){
32575             
32576             switch (this.placetitle) {
32577                 case 'center' :
32578                     cls += ' masonry-center-title';
32579                     break;
32580                 case 'bottom' :
32581                     cls += ' masonry-bottom-title';
32582                     break;
32583                 default:
32584                     break;
32585             }
32586             
32587         } else {
32588             if(!this.html.length && !this.bgimage.length){
32589                 cls += ' masonry-center-title';
32590             }
32591
32592             if(!this.html.length && this.bgimage.length){
32593                 cls += ' masonry-bottom-title';
32594             }
32595         }
32596         
32597         if(this.cls){
32598             cls += ' ' + this.cls;
32599         }
32600         
32601         var cfg = {
32602             tag: (this.href.length) ? 'a' : 'div',
32603             cls: cls,
32604             cn: [
32605                 {
32606                     tag: 'div',
32607                     cls: 'masonry-brick-mask'
32608                 },
32609                 {
32610                     tag: 'div',
32611                     cls: 'masonry-brick-paragraph',
32612                     cn: []
32613                 }
32614             ]
32615         };
32616         
32617         if(this.href.length){
32618             cfg.href = this.href;
32619         }
32620         
32621         var cn = cfg.cn[1].cn;
32622         
32623         if(this.title.length){
32624             cn.push({
32625                 tag: 'h4',
32626                 cls: 'masonry-brick-title',
32627                 html: this.title
32628             });
32629         }
32630         
32631         if(this.html.length){
32632             cn.push({
32633                 tag: 'p',
32634                 cls: 'masonry-brick-text',
32635                 html: this.html
32636             });
32637         }
32638         
32639         if (!this.title.length && !this.html.length) {
32640             cfg.cn[1].cls += ' hide';
32641         }
32642         
32643         if(this.bgimage.length){
32644             cfg.cn.push({
32645                 tag: 'img',
32646                 cls: 'masonry-brick-image-view',
32647                 src: this.bgimage
32648             });
32649         }
32650         
32651         if(this.videourl.length){
32652             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32653             // youtube support only?
32654             cfg.cn.push({
32655                 tag: 'iframe',
32656                 cls: 'masonry-brick-image-view',
32657                 src: vurl,
32658                 frameborder : 0,
32659                 allowfullscreen : true
32660             });
32661         }
32662         
32663         return cfg;
32664         
32665     },
32666     
32667     getSplitAutoCreate : function()
32668     {
32669         var cls = 'masonry-brick masonry-brick-split';
32670         
32671         if(this.href.length){
32672             cls += ' masonry-brick-link';
32673         }
32674         
32675         if(this.bgimage.length){
32676             cls += ' masonry-brick-image';
32677         }
32678         
32679         if(this.size){
32680             cls += ' masonry-' + this.size + '-brick';
32681         }
32682         
32683         switch (this.placetitle) {
32684             case 'center' :
32685                 cls += ' masonry-center-title';
32686                 break;
32687             case 'bottom' :
32688                 cls += ' masonry-bottom-title';
32689                 break;
32690             default:
32691                 if(!this.bgimage.length){
32692                     cls += ' masonry-center-title';
32693                 }
32694
32695                 if(this.bgimage.length){
32696                     cls += ' masonry-bottom-title';
32697                 }
32698                 break;
32699         }
32700         
32701         if(this.cls){
32702             cls += ' ' + this.cls;
32703         }
32704         
32705         var cfg = {
32706             tag: (this.href.length) ? 'a' : 'div',
32707             cls: cls,
32708             cn: [
32709                 {
32710                     tag: 'div',
32711                     cls: 'masonry-brick-split-head',
32712                     cn: [
32713                         {
32714                             tag: 'div',
32715                             cls: 'masonry-brick-paragraph',
32716                             cn: []
32717                         }
32718                     ]
32719                 },
32720                 {
32721                     tag: 'div',
32722                     cls: 'masonry-brick-split-body',
32723                     cn: []
32724                 }
32725             ]
32726         };
32727         
32728         if(this.href.length){
32729             cfg.href = this.href;
32730         }
32731         
32732         if(this.title.length){
32733             cfg.cn[0].cn[0].cn.push({
32734                 tag: 'h4',
32735                 cls: 'masonry-brick-title',
32736                 html: this.title
32737             });
32738         }
32739         
32740         if(this.html.length){
32741             cfg.cn[1].cn.push({
32742                 tag: 'p',
32743                 cls: 'masonry-brick-text',
32744                 html: this.html
32745             });
32746         }
32747
32748         if(this.bgimage.length){
32749             cfg.cn[0].cn.push({
32750                 tag: 'img',
32751                 cls: 'masonry-brick-image-view',
32752                 src: this.bgimage
32753             });
32754         }
32755         
32756         if(this.videourl.length){
32757             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32758             // youtube support only?
32759             cfg.cn[0].cn.cn.push({
32760                 tag: 'iframe',
32761                 cls: 'masonry-brick-image-view',
32762                 src: vurl,
32763                 frameborder : 0,
32764                 allowfullscreen : true
32765             });
32766         }
32767         
32768         return cfg;
32769     },
32770     
32771     initEvents: function() 
32772     {
32773         switch (this.size) {
32774             case 'xs' :
32775                 this.x = 1;
32776                 this.y = 1;
32777                 break;
32778             case 'sm' :
32779                 this.x = 2;
32780                 this.y = 2;
32781                 break;
32782             case 'md' :
32783             case 'md-left' :
32784             case 'md-right' :
32785                 this.x = 3;
32786                 this.y = 3;
32787                 break;
32788             case 'tall' :
32789                 this.x = 2;
32790                 this.y = 3;
32791                 break;
32792             case 'wide' :
32793                 this.x = 3;
32794                 this.y = 2;
32795                 break;
32796             case 'wide-thin' :
32797                 this.x = 3;
32798                 this.y = 1;
32799                 break;
32800                         
32801             default :
32802                 break;
32803         }
32804         
32805         if(Roo.isTouch){
32806             this.el.on('touchstart', this.onTouchStart, this);
32807             this.el.on('touchmove', this.onTouchMove, this);
32808             this.el.on('touchend', this.onTouchEnd, this);
32809             this.el.on('contextmenu', this.onContextMenu, this);
32810         } else {
32811             this.el.on('mouseenter'  ,this.enter, this);
32812             this.el.on('mouseleave', this.leave, this);
32813             this.el.on('click', this.onClick, this);
32814         }
32815         
32816         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32817             this.parent().bricks.push(this);   
32818         }
32819         
32820     },
32821     
32822     onClick: function(e, el)
32823     {
32824         var time = this.endTimer - this.startTimer;
32825         // Roo.log(e.preventDefault());
32826         if(Roo.isTouch){
32827             if(time > 1000){
32828                 e.preventDefault();
32829                 return;
32830             }
32831         }
32832         
32833         if(!this.preventDefault){
32834             return;
32835         }
32836         
32837         e.preventDefault();
32838         
32839         if (this.activeClass != '') {
32840             this.selectBrick();
32841         }
32842         
32843         this.fireEvent('click', this, e);
32844     },
32845     
32846     enter: function(e, el)
32847     {
32848         e.preventDefault();
32849         
32850         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32851             return;
32852         }
32853         
32854         if(this.bgimage.length && this.html.length){
32855             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32856         }
32857     },
32858     
32859     leave: function(e, el)
32860     {
32861         e.preventDefault();
32862         
32863         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32864             return;
32865         }
32866         
32867         if(this.bgimage.length && this.html.length){
32868             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32869         }
32870     },
32871     
32872     onTouchStart: function(e, el)
32873     {
32874 //        e.preventDefault();
32875         
32876         this.touchmoved = false;
32877         
32878         if(!this.isFitContainer){
32879             return;
32880         }
32881         
32882         if(!this.bgimage.length || !this.html.length){
32883             return;
32884         }
32885         
32886         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32887         
32888         this.timer = new Date().getTime();
32889         
32890     },
32891     
32892     onTouchMove: function(e, el)
32893     {
32894         this.touchmoved = true;
32895     },
32896     
32897     onContextMenu : function(e,el)
32898     {
32899         e.preventDefault();
32900         e.stopPropagation();
32901         return false;
32902     },
32903     
32904     onTouchEnd: function(e, el)
32905     {
32906 //        e.preventDefault();
32907         
32908         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32909         
32910             this.leave(e,el);
32911             
32912             return;
32913         }
32914         
32915         if(!this.bgimage.length || !this.html.length){
32916             
32917             if(this.href.length){
32918                 window.location.href = this.href;
32919             }
32920             
32921             return;
32922         }
32923         
32924         if(!this.isFitContainer){
32925             return;
32926         }
32927         
32928         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32929         
32930         window.location.href = this.href;
32931     },
32932     
32933     //selection on single brick only
32934     selectBrick : function() {
32935         
32936         if (!this.parentId) {
32937             return;
32938         }
32939         
32940         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32941         var index = m.selectedBrick.indexOf(this.id);
32942         
32943         if ( index > -1) {
32944             m.selectedBrick.splice(index,1);
32945             this.el.removeClass(this.activeClass);
32946             return;
32947         }
32948         
32949         for(var i = 0; i < m.selectedBrick.length; i++) {
32950             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32951             b.el.removeClass(b.activeClass);
32952         }
32953         
32954         m.selectedBrick = [];
32955         
32956         m.selectedBrick.push(this.id);
32957         this.el.addClass(this.activeClass);
32958         return;
32959     },
32960     
32961     isSelected : function(){
32962         return this.el.hasClass(this.activeClass);
32963         
32964     }
32965 });
32966
32967 Roo.apply(Roo.bootstrap.MasonryBrick, {
32968     
32969     //groups: {},
32970     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32971      /**
32972     * register a Masonry Brick
32973     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32974     */
32975     
32976     register : function(brick)
32977     {
32978         //this.groups[brick.id] = brick;
32979         this.groups.add(brick.id, brick);
32980     },
32981     /**
32982     * fetch a  masonry brick based on the masonry brick ID
32983     * @param {string} the masonry brick to add
32984     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32985     */
32986     
32987     get: function(brick_id) 
32988     {
32989         // if (typeof(this.groups[brick_id]) == 'undefined') {
32990         //     return false;
32991         // }
32992         // return this.groups[brick_id] ;
32993         
32994         if(this.groups.key(brick_id)) {
32995             return this.groups.key(brick_id);
32996         }
32997         
32998         return false;
32999     }
33000     
33001     
33002     
33003 });
33004
33005  /*
33006  * - LGPL
33007  *
33008  * element
33009  * 
33010  */
33011
33012 /**
33013  * @class Roo.bootstrap.Brick
33014  * @extends Roo.bootstrap.Component
33015  * Bootstrap Brick class
33016  * 
33017  * @constructor
33018  * Create a new Brick
33019  * @param {Object} config The config object
33020  */
33021
33022 Roo.bootstrap.Brick = function(config){
33023     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33024     
33025     this.addEvents({
33026         // raw events
33027         /**
33028          * @event click
33029          * When a Brick is click
33030          * @param {Roo.bootstrap.Brick} this
33031          * @param {Roo.EventObject} e
33032          */
33033         "click" : true
33034     });
33035 };
33036
33037 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33038     
33039     /**
33040      * @cfg {String} title
33041      */   
33042     title : '',
33043     /**
33044      * @cfg {String} html
33045      */   
33046     html : '',
33047     /**
33048      * @cfg {String} bgimage
33049      */   
33050     bgimage : '',
33051     /**
33052      * @cfg {String} cls
33053      */   
33054     cls : '',
33055     /**
33056      * @cfg {String} href
33057      */   
33058     href : '',
33059     /**
33060      * @cfg {String} video
33061      */   
33062     video : '',
33063     /**
33064      * @cfg {Boolean} square
33065      */   
33066     square : true,
33067     
33068     getAutoCreate : function()
33069     {
33070         var cls = 'roo-brick';
33071         
33072         if(this.href.length){
33073             cls += ' roo-brick-link';
33074         }
33075         
33076         if(this.bgimage.length){
33077             cls += ' roo-brick-image';
33078         }
33079         
33080         if(!this.html.length && !this.bgimage.length){
33081             cls += ' roo-brick-center-title';
33082         }
33083         
33084         if(!this.html.length && this.bgimage.length){
33085             cls += ' roo-brick-bottom-title';
33086         }
33087         
33088         if(this.cls){
33089             cls += ' ' + this.cls;
33090         }
33091         
33092         var cfg = {
33093             tag: (this.href.length) ? 'a' : 'div',
33094             cls: cls,
33095             cn: [
33096                 {
33097                     tag: 'div',
33098                     cls: 'roo-brick-paragraph',
33099                     cn: []
33100                 }
33101             ]
33102         };
33103         
33104         if(this.href.length){
33105             cfg.href = this.href;
33106         }
33107         
33108         var cn = cfg.cn[0].cn;
33109         
33110         if(this.title.length){
33111             cn.push({
33112                 tag: 'h4',
33113                 cls: 'roo-brick-title',
33114                 html: this.title
33115             });
33116         }
33117         
33118         if(this.html.length){
33119             cn.push({
33120                 tag: 'p',
33121                 cls: 'roo-brick-text',
33122                 html: this.html
33123             });
33124         } else {
33125             cn.cls += ' hide';
33126         }
33127         
33128         if(this.bgimage.length){
33129             cfg.cn.push({
33130                 tag: 'img',
33131                 cls: 'roo-brick-image-view',
33132                 src: this.bgimage
33133             });
33134         }
33135         
33136         return cfg;
33137     },
33138     
33139     initEvents: function() 
33140     {
33141         if(this.title.length || this.html.length){
33142             this.el.on('mouseenter'  ,this.enter, this);
33143             this.el.on('mouseleave', this.leave, this);
33144         }
33145         
33146         Roo.EventManager.onWindowResize(this.resize, this); 
33147         
33148         if(this.bgimage.length){
33149             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33150             this.imageEl.on('load', this.onImageLoad, this);
33151             return;
33152         }
33153         
33154         this.resize();
33155     },
33156     
33157     onImageLoad : function()
33158     {
33159         this.resize();
33160     },
33161     
33162     resize : function()
33163     {
33164         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33165         
33166         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33167         
33168         if(this.bgimage.length){
33169             var image = this.el.select('.roo-brick-image-view', true).first();
33170             
33171             image.setWidth(paragraph.getWidth());
33172             
33173             if(this.square){
33174                 image.setHeight(paragraph.getWidth());
33175             }
33176             
33177             this.el.setHeight(image.getHeight());
33178             paragraph.setHeight(image.getHeight());
33179             
33180         }
33181         
33182     },
33183     
33184     enter: function(e, el)
33185     {
33186         e.preventDefault();
33187         
33188         if(this.bgimage.length){
33189             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33190             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33191         }
33192     },
33193     
33194     leave: function(e, el)
33195     {
33196         e.preventDefault();
33197         
33198         if(this.bgimage.length){
33199             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33200             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33201         }
33202     }
33203     
33204 });
33205
33206  
33207
33208  /*
33209  * - LGPL
33210  *
33211  * Number field 
33212  */
33213
33214 /**
33215  * @class Roo.bootstrap.NumberField
33216  * @extends Roo.bootstrap.Input
33217  * Bootstrap NumberField class
33218  * 
33219  * 
33220  * 
33221  * 
33222  * @constructor
33223  * Create a new NumberField
33224  * @param {Object} config The config object
33225  */
33226
33227 Roo.bootstrap.NumberField = function(config){
33228     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33229 };
33230
33231 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33232     
33233     /**
33234      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33235      */
33236     allowDecimals : true,
33237     /**
33238      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33239      */
33240     decimalSeparator : ".",
33241     /**
33242      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33243      */
33244     decimalPrecision : 2,
33245     /**
33246      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33247      */
33248     allowNegative : true,
33249     
33250     /**
33251      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33252      */
33253     allowZero: true,
33254     /**
33255      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33256      */
33257     minValue : Number.NEGATIVE_INFINITY,
33258     /**
33259      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33260      */
33261     maxValue : Number.MAX_VALUE,
33262     /**
33263      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33264      */
33265     minText : "The minimum value for this field is {0}",
33266     /**
33267      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33268      */
33269     maxText : "The maximum value for this field is {0}",
33270     /**
33271      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33272      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33273      */
33274     nanText : "{0} is not a valid number",
33275     /**
33276      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33277      */
33278     thousandsDelimiter : false,
33279     /**
33280      * @cfg {String} valueAlign alignment of value
33281      */
33282     valueAlign : "left",
33283
33284     getAutoCreate : function()
33285     {
33286         var hiddenInput = {
33287             tag: 'input',
33288             type: 'hidden',
33289             id: Roo.id(),
33290             cls: 'hidden-number-input'
33291         };
33292         
33293         if (this.name) {
33294             hiddenInput.name = this.name;
33295         }
33296         
33297         this.name = '';
33298         
33299         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33300         
33301         this.name = hiddenInput.name;
33302         
33303         if(cfg.cn.length > 0) {
33304             cfg.cn.push(hiddenInput);
33305         }
33306         
33307         return cfg;
33308     },
33309
33310     // private
33311     initEvents : function()
33312     {   
33313         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33314         
33315         var allowed = "0123456789";
33316         
33317         if(this.allowDecimals){
33318             allowed += this.decimalSeparator;
33319         }
33320         
33321         if(this.allowNegative){
33322             allowed += "-";
33323         }
33324         
33325         if(this.thousandsDelimiter) {
33326             allowed += ",";
33327         }
33328         
33329         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33330         
33331         var keyPress = function(e){
33332             
33333             var k = e.getKey();
33334             
33335             var c = e.getCharCode();
33336             
33337             if(
33338                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33339                     allowed.indexOf(String.fromCharCode(c)) === -1
33340             ){
33341                 e.stopEvent();
33342                 return;
33343             }
33344             
33345             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33346                 return;
33347             }
33348             
33349             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33350                 e.stopEvent();
33351             }
33352         };
33353         
33354         this.el.on("keypress", keyPress, this);
33355     },
33356     
33357     validateValue : function(value)
33358     {
33359         
33360         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33361             return false;
33362         }
33363         
33364         var num = this.parseValue(value);
33365         
33366         if(isNaN(num)){
33367             this.markInvalid(String.format(this.nanText, value));
33368             return false;
33369         }
33370         
33371         if(num < this.minValue){
33372             this.markInvalid(String.format(this.minText, this.minValue));
33373             return false;
33374         }
33375         
33376         if(num > this.maxValue){
33377             this.markInvalid(String.format(this.maxText, this.maxValue));
33378             return false;
33379         }
33380         
33381         return true;
33382     },
33383
33384     getValue : function()
33385     {
33386         var v = this.hiddenEl().getValue();
33387         
33388         return this.fixPrecision(this.parseValue(v));
33389     },
33390
33391     parseValue : function(value)
33392     {
33393         if(this.thousandsDelimiter) {
33394             value += "";
33395             r = new RegExp(",", "g");
33396             value = value.replace(r, "");
33397         }
33398         
33399         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33400         return isNaN(value) ? '' : value;
33401     },
33402
33403     fixPrecision : function(value)
33404     {
33405         if(this.thousandsDelimiter) {
33406             value += "";
33407             r = new RegExp(",", "g");
33408             value = value.replace(r, "");
33409         }
33410         
33411         var nan = isNaN(value);
33412         
33413         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33414             return nan ? '' : value;
33415         }
33416         return parseFloat(value).toFixed(this.decimalPrecision);
33417     },
33418
33419     setValue : function(v)
33420     {
33421         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33422         
33423         this.value = v;
33424         
33425         if(this.rendered){
33426             
33427             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33428             
33429             this.inputEl().dom.value = (v == '') ? '' :
33430                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33431             
33432             if(!this.allowZero && v === '0') {
33433                 this.hiddenEl().dom.value = '';
33434                 this.inputEl().dom.value = '';
33435             }
33436             
33437             this.validate();
33438         }
33439     },
33440
33441     decimalPrecisionFcn : function(v)
33442     {
33443         return Math.floor(v);
33444     },
33445
33446     beforeBlur : function()
33447     {
33448         var v = this.parseValue(this.getRawValue());
33449         
33450         if(v || v === 0 || v === ''){
33451             this.setValue(v);
33452         }
33453     },
33454     
33455     hiddenEl : function()
33456     {
33457         return this.el.select('input.hidden-number-input',true).first();
33458     }
33459     
33460 });
33461
33462  
33463
33464 /*
33465 * Licence: LGPL
33466 */
33467
33468 /**
33469  * @class Roo.bootstrap.DocumentSlider
33470  * @extends Roo.bootstrap.Component
33471  * Bootstrap DocumentSlider class
33472  * 
33473  * @constructor
33474  * Create a new DocumentViewer
33475  * @param {Object} config The config object
33476  */
33477
33478 Roo.bootstrap.DocumentSlider = function(config){
33479     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33480     
33481     this.files = [];
33482     
33483     this.addEvents({
33484         /**
33485          * @event initial
33486          * Fire after initEvent
33487          * @param {Roo.bootstrap.DocumentSlider} this
33488          */
33489         "initial" : true,
33490         /**
33491          * @event update
33492          * Fire after update
33493          * @param {Roo.bootstrap.DocumentSlider} this
33494          */
33495         "update" : true,
33496         /**
33497          * @event click
33498          * Fire after click
33499          * @param {Roo.bootstrap.DocumentSlider} this
33500          */
33501         "click" : true
33502     });
33503 };
33504
33505 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33506     
33507     files : false,
33508     
33509     indicator : 0,
33510     
33511     getAutoCreate : function()
33512     {
33513         var cfg = {
33514             tag : 'div',
33515             cls : 'roo-document-slider',
33516             cn : [
33517                 {
33518                     tag : 'div',
33519                     cls : 'roo-document-slider-header',
33520                     cn : [
33521                         {
33522                             tag : 'div',
33523                             cls : 'roo-document-slider-header-title'
33524                         }
33525                     ]
33526                 },
33527                 {
33528                     tag : 'div',
33529                     cls : 'roo-document-slider-body',
33530                     cn : [
33531                         {
33532                             tag : 'div',
33533                             cls : 'roo-document-slider-prev',
33534                             cn : [
33535                                 {
33536                                     tag : 'i',
33537                                     cls : 'fa fa-chevron-left'
33538                                 }
33539                             ]
33540                         },
33541                         {
33542                             tag : 'div',
33543                             cls : 'roo-document-slider-thumb',
33544                             cn : [
33545                                 {
33546                                     tag : 'img',
33547                                     cls : 'roo-document-slider-image'
33548                                 }
33549                             ]
33550                         },
33551                         {
33552                             tag : 'div',
33553                             cls : 'roo-document-slider-next',
33554                             cn : [
33555                                 {
33556                                     tag : 'i',
33557                                     cls : 'fa fa-chevron-right'
33558                                 }
33559                             ]
33560                         }
33561                     ]
33562                 }
33563             ]
33564         };
33565         
33566         return cfg;
33567     },
33568     
33569     initEvents : function()
33570     {
33571         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33572         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33573         
33574         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33575         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33576         
33577         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33578         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33579         
33580         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33581         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33582         
33583         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33584         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33585         
33586         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33587         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33588         
33589         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33590         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33591         
33592         this.thumbEl.on('click', this.onClick, this);
33593         
33594         this.prevIndicator.on('click', this.prev, this);
33595         
33596         this.nextIndicator.on('click', this.next, this);
33597         
33598     },
33599     
33600     initial : function()
33601     {
33602         if(this.files.length){
33603             this.indicator = 1;
33604             this.update()
33605         }
33606         
33607         this.fireEvent('initial', this);
33608     },
33609     
33610     update : function()
33611     {
33612         this.imageEl.attr('src', this.files[this.indicator - 1]);
33613         
33614         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33615         
33616         this.prevIndicator.show();
33617         
33618         if(this.indicator == 1){
33619             this.prevIndicator.hide();
33620         }
33621         
33622         this.nextIndicator.show();
33623         
33624         if(this.indicator == this.files.length){
33625             this.nextIndicator.hide();
33626         }
33627         
33628         this.thumbEl.scrollTo('top');
33629         
33630         this.fireEvent('update', this);
33631     },
33632     
33633     onClick : function(e)
33634     {
33635         e.preventDefault();
33636         
33637         this.fireEvent('click', this);
33638     },
33639     
33640     prev : function(e)
33641     {
33642         e.preventDefault();
33643         
33644         this.indicator = Math.max(1, this.indicator - 1);
33645         
33646         this.update();
33647     },
33648     
33649     next : function(e)
33650     {
33651         e.preventDefault();
33652         
33653         this.indicator = Math.min(this.files.length, this.indicator + 1);
33654         
33655         this.update();
33656     }
33657 });
33658 /*
33659  * - LGPL
33660  *
33661  * RadioSet
33662  *
33663  *
33664  */
33665
33666 /**
33667  * @class Roo.bootstrap.RadioSet
33668  * @extends Roo.bootstrap.Input
33669  * Bootstrap RadioSet class
33670  * @cfg {String} indicatorpos (left|right) default left
33671  * @cfg {Boolean} inline (true|false) inline the element (default true)
33672  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33673  * @constructor
33674  * Create a new RadioSet
33675  * @param {Object} config The config object
33676  */
33677
33678 Roo.bootstrap.RadioSet = function(config){
33679     
33680     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33681     
33682     this.radioes = [];
33683     
33684     Roo.bootstrap.RadioSet.register(this);
33685     
33686     this.addEvents({
33687         /**
33688         * @event check
33689         * Fires when the element is checked or unchecked.
33690         * @param {Roo.bootstrap.RadioSet} this This radio
33691         * @param {Roo.bootstrap.Radio} item The checked item
33692         */
33693        check : true,
33694        /**
33695         * @event click
33696         * Fires when the element is click.
33697         * @param {Roo.bootstrap.RadioSet} this This radio set
33698         * @param {Roo.bootstrap.Radio} item The checked item
33699         * @param {Roo.EventObject} e The event object
33700         */
33701        click : true
33702     });
33703     
33704 };
33705
33706 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33707
33708     radioes : false,
33709     
33710     inline : true,
33711     
33712     weight : '',
33713     
33714     indicatorpos : 'left',
33715     
33716     getAutoCreate : function()
33717     {
33718         var label = {
33719             tag : 'label',
33720             cls : 'roo-radio-set-label',
33721             cn : [
33722                 {
33723                     tag : 'span',
33724                     html : this.fieldLabel
33725                 }
33726             ]
33727         };
33728         
33729         if(this.indicatorpos == 'left'){
33730             label.cn.unshift({
33731                 tag : 'i',
33732                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33733                 tooltip : 'This field is required'
33734             });
33735         } else {
33736             label.cn.push({
33737                 tag : 'i',
33738                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33739                 tooltip : 'This field is required'
33740             });
33741         }
33742         
33743         var items = {
33744             tag : 'div',
33745             cls : 'roo-radio-set-items'
33746         };
33747         
33748         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33749         
33750         if (align === 'left' && this.fieldLabel.length) {
33751             
33752             items = {
33753                 cls : "roo-radio-set-right", 
33754                 cn: [
33755                     items
33756                 ]
33757             };
33758             
33759             if(this.labelWidth > 12){
33760                 label.style = "width: " + this.labelWidth + 'px';
33761             }
33762             
33763             if(this.labelWidth < 13 && this.labelmd == 0){
33764                 this.labelmd = this.labelWidth;
33765             }
33766             
33767             if(this.labellg > 0){
33768                 label.cls += ' col-lg-' + this.labellg;
33769                 items.cls += ' col-lg-' + (12 - this.labellg);
33770             }
33771             
33772             if(this.labelmd > 0){
33773                 label.cls += ' col-md-' + this.labelmd;
33774                 items.cls += ' col-md-' + (12 - this.labelmd);
33775             }
33776             
33777             if(this.labelsm > 0){
33778                 label.cls += ' col-sm-' + this.labelsm;
33779                 items.cls += ' col-sm-' + (12 - this.labelsm);
33780             }
33781             
33782             if(this.labelxs > 0){
33783                 label.cls += ' col-xs-' + this.labelxs;
33784                 items.cls += ' col-xs-' + (12 - this.labelxs);
33785             }
33786         }
33787         
33788         var cfg = {
33789             tag : 'div',
33790             cls : 'roo-radio-set',
33791             cn : [
33792                 {
33793                     tag : 'input',
33794                     cls : 'roo-radio-set-input',
33795                     type : 'hidden',
33796                     name : this.name,
33797                     value : this.value ? this.value :  ''
33798                 },
33799                 label,
33800                 items
33801             ]
33802         };
33803         
33804         if(this.weight.length){
33805             cfg.cls += ' roo-radio-' + this.weight;
33806         }
33807         
33808         if(this.inline) {
33809             cfg.cls += ' roo-radio-set-inline';
33810         }
33811         
33812         var settings=this;
33813         ['xs','sm','md','lg'].map(function(size){
33814             if (settings[size]) {
33815                 cfg.cls += ' col-' + size + '-' + settings[size];
33816             }
33817         });
33818         
33819         return cfg;
33820         
33821     },
33822
33823     initEvents : function()
33824     {
33825         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33826         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33827         
33828         if(!this.fieldLabel.length){
33829             this.labelEl.hide();
33830         }
33831         
33832         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33833         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33834         
33835         this.indicator = this.indicatorEl();
33836         
33837         if(this.indicator){
33838             this.indicator.addClass('invisible');
33839         }
33840         
33841         this.originalValue = this.getValue();
33842         
33843     },
33844     
33845     inputEl: function ()
33846     {
33847         return this.el.select('.roo-radio-set-input', true).first();
33848     },
33849     
33850     getChildContainer : function()
33851     {
33852         return this.itemsEl;
33853     },
33854     
33855     register : function(item)
33856     {
33857         this.radioes.push(item);
33858         
33859     },
33860     
33861     validate : function()
33862     {   
33863         if(this.getVisibilityEl().hasClass('hidden')){
33864             return true;
33865         }
33866         
33867         var valid = false;
33868         
33869         Roo.each(this.radioes, function(i){
33870             if(!i.checked){
33871                 return;
33872             }
33873             
33874             valid = true;
33875             return false;
33876         });
33877         
33878         if(this.allowBlank) {
33879             return true;
33880         }
33881         
33882         if(this.disabled || valid){
33883             this.markValid();
33884             return true;
33885         }
33886         
33887         this.markInvalid();
33888         return false;
33889         
33890     },
33891     
33892     markValid : function()
33893     {
33894         if(this.labelEl.isVisible(true)){
33895             this.indicatorEl().removeClass('visible');
33896             this.indicatorEl().addClass('invisible');
33897         }
33898         
33899         this.el.removeClass([this.invalidClass, this.validClass]);
33900         this.el.addClass(this.validClass);
33901         
33902         this.fireEvent('valid', this);
33903     },
33904     
33905     markInvalid : function(msg)
33906     {
33907         if(this.allowBlank || this.disabled){
33908             return;
33909         }
33910         
33911         if(this.labelEl.isVisible(true)){
33912             this.indicatorEl().removeClass('invisible');
33913             this.indicatorEl().addClass('visible');
33914         }
33915         
33916         this.el.removeClass([this.invalidClass, this.validClass]);
33917         this.el.addClass(this.invalidClass);
33918         
33919         this.fireEvent('invalid', this, msg);
33920         
33921     },
33922     
33923     setValue : function(v, suppressEvent)
33924     {   
33925         if(this.value === v){
33926             return;
33927         }
33928         
33929         this.value = v;
33930         
33931         if(this.rendered){
33932             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33933         }
33934         
33935         Roo.each(this.radioes, function(i){
33936             i.checked = false;
33937             i.el.removeClass('checked');
33938         });
33939         
33940         Roo.each(this.radioes, function(i){
33941             
33942             if(i.value === v || i.value.toString() === v.toString()){
33943                 i.checked = true;
33944                 i.el.addClass('checked');
33945                 
33946                 if(suppressEvent !== true){
33947                     this.fireEvent('check', this, i);
33948                 }
33949                 
33950                 return false;
33951             }
33952             
33953         }, this);
33954         
33955         this.validate();
33956     },
33957     
33958     clearInvalid : function(){
33959         
33960         if(!this.el || this.preventMark){
33961             return;
33962         }
33963         
33964         this.el.removeClass([this.invalidClass]);
33965         
33966         this.fireEvent('valid', this);
33967     }
33968     
33969 });
33970
33971 Roo.apply(Roo.bootstrap.RadioSet, {
33972     
33973     groups: {},
33974     
33975     register : function(set)
33976     {
33977         this.groups[set.name] = set;
33978     },
33979     
33980     get: function(name) 
33981     {
33982         if (typeof(this.groups[name]) == 'undefined') {
33983             return false;
33984         }
33985         
33986         return this.groups[name] ;
33987     }
33988     
33989 });
33990 /*
33991  * Based on:
33992  * Ext JS Library 1.1.1
33993  * Copyright(c) 2006-2007, Ext JS, LLC.
33994  *
33995  * Originally Released Under LGPL - original licence link has changed is not relivant.
33996  *
33997  * Fork - LGPL
33998  * <script type="text/javascript">
33999  */
34000
34001
34002 /**
34003  * @class Roo.bootstrap.SplitBar
34004  * @extends Roo.util.Observable
34005  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34006  * <br><br>
34007  * Usage:
34008  * <pre><code>
34009 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34010                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34011 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34012 split.minSize = 100;
34013 split.maxSize = 600;
34014 split.animate = true;
34015 split.on('moved', splitterMoved);
34016 </code></pre>
34017  * @constructor
34018  * Create a new SplitBar
34019  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34020  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34021  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34022  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34023                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34024                         position of the SplitBar).
34025  */
34026 Roo.bootstrap.SplitBar = function(cfg){
34027     
34028     /** @private */
34029     
34030     //{
34031     //  dragElement : elm
34032     //  resizingElement: el,
34033         // optional..
34034     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34035     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34036         // existingProxy ???
34037     //}
34038     
34039     this.el = Roo.get(cfg.dragElement, true);
34040     this.el.dom.unselectable = "on";
34041     /** @private */
34042     this.resizingEl = Roo.get(cfg.resizingElement, true);
34043
34044     /**
34045      * @private
34046      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34047      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34048      * @type Number
34049      */
34050     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34051     
34052     /**
34053      * The minimum size of the resizing element. (Defaults to 0)
34054      * @type Number
34055      */
34056     this.minSize = 0;
34057     
34058     /**
34059      * The maximum size of the resizing element. (Defaults to 2000)
34060      * @type Number
34061      */
34062     this.maxSize = 2000;
34063     
34064     /**
34065      * Whether to animate the transition to the new size
34066      * @type Boolean
34067      */
34068     this.animate = false;
34069     
34070     /**
34071      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34072      * @type Boolean
34073      */
34074     this.useShim = false;
34075     
34076     /** @private */
34077     this.shim = null;
34078     
34079     if(!cfg.existingProxy){
34080         /** @private */
34081         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34082     }else{
34083         this.proxy = Roo.get(cfg.existingProxy).dom;
34084     }
34085     /** @private */
34086     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34087     
34088     /** @private */
34089     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34090     
34091     /** @private */
34092     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34093     
34094     /** @private */
34095     this.dragSpecs = {};
34096     
34097     /**
34098      * @private The adapter to use to positon and resize elements
34099      */
34100     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34101     this.adapter.init(this);
34102     
34103     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34104         /** @private */
34105         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34106         this.el.addClass("roo-splitbar-h");
34107     }else{
34108         /** @private */
34109         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34110         this.el.addClass("roo-splitbar-v");
34111     }
34112     
34113     this.addEvents({
34114         /**
34115          * @event resize
34116          * Fires when the splitter is moved (alias for {@link #event-moved})
34117          * @param {Roo.bootstrap.SplitBar} this
34118          * @param {Number} newSize the new width or height
34119          */
34120         "resize" : true,
34121         /**
34122          * @event moved
34123          * Fires when the splitter is moved
34124          * @param {Roo.bootstrap.SplitBar} this
34125          * @param {Number} newSize the new width or height
34126          */
34127         "moved" : true,
34128         /**
34129          * @event beforeresize
34130          * Fires before the splitter is dragged
34131          * @param {Roo.bootstrap.SplitBar} this
34132          */
34133         "beforeresize" : true,
34134
34135         "beforeapply" : true
34136     });
34137
34138     Roo.util.Observable.call(this);
34139 };
34140
34141 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34142     onStartProxyDrag : function(x, y){
34143         this.fireEvent("beforeresize", this);
34144         if(!this.overlay){
34145             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34146             o.unselectable();
34147             o.enableDisplayMode("block");
34148             // all splitbars share the same overlay
34149             Roo.bootstrap.SplitBar.prototype.overlay = o;
34150         }
34151         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34152         this.overlay.show();
34153         Roo.get(this.proxy).setDisplayed("block");
34154         var size = this.adapter.getElementSize(this);
34155         this.activeMinSize = this.getMinimumSize();;
34156         this.activeMaxSize = this.getMaximumSize();;
34157         var c1 = size - this.activeMinSize;
34158         var c2 = Math.max(this.activeMaxSize - size, 0);
34159         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34160             this.dd.resetConstraints();
34161             this.dd.setXConstraint(
34162                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34163                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34164             );
34165             this.dd.setYConstraint(0, 0);
34166         }else{
34167             this.dd.resetConstraints();
34168             this.dd.setXConstraint(0, 0);
34169             this.dd.setYConstraint(
34170                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34171                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34172             );
34173          }
34174         this.dragSpecs.startSize = size;
34175         this.dragSpecs.startPoint = [x, y];
34176         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34177     },
34178     
34179     /** 
34180      * @private Called after the drag operation by the DDProxy
34181      */
34182     onEndProxyDrag : function(e){
34183         Roo.get(this.proxy).setDisplayed(false);
34184         var endPoint = Roo.lib.Event.getXY(e);
34185         if(this.overlay){
34186             this.overlay.hide();
34187         }
34188         var newSize;
34189         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34190             newSize = this.dragSpecs.startSize + 
34191                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34192                     endPoint[0] - this.dragSpecs.startPoint[0] :
34193                     this.dragSpecs.startPoint[0] - endPoint[0]
34194                 );
34195         }else{
34196             newSize = this.dragSpecs.startSize + 
34197                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34198                     endPoint[1] - this.dragSpecs.startPoint[1] :
34199                     this.dragSpecs.startPoint[1] - endPoint[1]
34200                 );
34201         }
34202         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34203         if(newSize != this.dragSpecs.startSize){
34204             if(this.fireEvent('beforeapply', this, newSize) !== false){
34205                 this.adapter.setElementSize(this, newSize);
34206                 this.fireEvent("moved", this, newSize);
34207                 this.fireEvent("resize", this, newSize);
34208             }
34209         }
34210     },
34211     
34212     /**
34213      * Get the adapter this SplitBar uses
34214      * @return The adapter object
34215      */
34216     getAdapter : function(){
34217         return this.adapter;
34218     },
34219     
34220     /**
34221      * Set the adapter this SplitBar uses
34222      * @param {Object} adapter A SplitBar adapter object
34223      */
34224     setAdapter : function(adapter){
34225         this.adapter = adapter;
34226         this.adapter.init(this);
34227     },
34228     
34229     /**
34230      * Gets the minimum size for the resizing element
34231      * @return {Number} The minimum size
34232      */
34233     getMinimumSize : function(){
34234         return this.minSize;
34235     },
34236     
34237     /**
34238      * Sets the minimum size for the resizing element
34239      * @param {Number} minSize The minimum size
34240      */
34241     setMinimumSize : function(minSize){
34242         this.minSize = minSize;
34243     },
34244     
34245     /**
34246      * Gets the maximum size for the resizing element
34247      * @return {Number} The maximum size
34248      */
34249     getMaximumSize : function(){
34250         return this.maxSize;
34251     },
34252     
34253     /**
34254      * Sets the maximum size for the resizing element
34255      * @param {Number} maxSize The maximum size
34256      */
34257     setMaximumSize : function(maxSize){
34258         this.maxSize = maxSize;
34259     },
34260     
34261     /**
34262      * Sets the initialize size for the resizing element
34263      * @param {Number} size The initial size
34264      */
34265     setCurrentSize : function(size){
34266         var oldAnimate = this.animate;
34267         this.animate = false;
34268         this.adapter.setElementSize(this, size);
34269         this.animate = oldAnimate;
34270     },
34271     
34272     /**
34273      * Destroy this splitbar. 
34274      * @param {Boolean} removeEl True to remove the element
34275      */
34276     destroy : function(removeEl){
34277         if(this.shim){
34278             this.shim.remove();
34279         }
34280         this.dd.unreg();
34281         this.proxy.parentNode.removeChild(this.proxy);
34282         if(removeEl){
34283             this.el.remove();
34284         }
34285     }
34286 });
34287
34288 /**
34289  * @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.
34290  */
34291 Roo.bootstrap.SplitBar.createProxy = function(dir){
34292     var proxy = new Roo.Element(document.createElement("div"));
34293     proxy.unselectable();
34294     var cls = 'roo-splitbar-proxy';
34295     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34296     document.body.appendChild(proxy.dom);
34297     return proxy.dom;
34298 };
34299
34300 /** 
34301  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34302  * Default Adapter. It assumes the splitter and resizing element are not positioned
34303  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34304  */
34305 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34306 };
34307
34308 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34309     // do nothing for now
34310     init : function(s){
34311     
34312     },
34313     /**
34314      * Called before drag operations to get the current size of the resizing element. 
34315      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34316      */
34317      getElementSize : function(s){
34318         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34319             return s.resizingEl.getWidth();
34320         }else{
34321             return s.resizingEl.getHeight();
34322         }
34323     },
34324     
34325     /**
34326      * Called after drag operations to set the size of the resizing element.
34327      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34328      * @param {Number} newSize The new size to set
34329      * @param {Function} onComplete A function to be invoked when resizing is complete
34330      */
34331     setElementSize : function(s, newSize, onComplete){
34332         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34333             if(!s.animate){
34334                 s.resizingEl.setWidth(newSize);
34335                 if(onComplete){
34336                     onComplete(s, newSize);
34337                 }
34338             }else{
34339                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34340             }
34341         }else{
34342             
34343             if(!s.animate){
34344                 s.resizingEl.setHeight(newSize);
34345                 if(onComplete){
34346                     onComplete(s, newSize);
34347                 }
34348             }else{
34349                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34350             }
34351         }
34352     }
34353 };
34354
34355 /** 
34356  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34357  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34358  * Adapter that  moves the splitter element to align with the resized sizing element. 
34359  * Used with an absolute positioned SplitBar.
34360  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34361  * document.body, make sure you assign an id to the body element.
34362  */
34363 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34364     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34365     this.container = Roo.get(container);
34366 };
34367
34368 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34369     init : function(s){
34370         this.basic.init(s);
34371     },
34372     
34373     getElementSize : function(s){
34374         return this.basic.getElementSize(s);
34375     },
34376     
34377     setElementSize : function(s, newSize, onComplete){
34378         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34379     },
34380     
34381     moveSplitter : function(s){
34382         var yes = Roo.bootstrap.SplitBar;
34383         switch(s.placement){
34384             case yes.LEFT:
34385                 s.el.setX(s.resizingEl.getRight());
34386                 break;
34387             case yes.RIGHT:
34388                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34389                 break;
34390             case yes.TOP:
34391                 s.el.setY(s.resizingEl.getBottom());
34392                 break;
34393             case yes.BOTTOM:
34394                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34395                 break;
34396         }
34397     }
34398 };
34399
34400 /**
34401  * Orientation constant - Create a vertical SplitBar
34402  * @static
34403  * @type Number
34404  */
34405 Roo.bootstrap.SplitBar.VERTICAL = 1;
34406
34407 /**
34408  * Orientation constant - Create a horizontal SplitBar
34409  * @static
34410  * @type Number
34411  */
34412 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34413
34414 /**
34415  * Placement constant - The resizing element is to the left of the splitter element
34416  * @static
34417  * @type Number
34418  */
34419 Roo.bootstrap.SplitBar.LEFT = 1;
34420
34421 /**
34422  * Placement constant - The resizing element is to the right of the splitter element
34423  * @static
34424  * @type Number
34425  */
34426 Roo.bootstrap.SplitBar.RIGHT = 2;
34427
34428 /**
34429  * Placement constant - The resizing element is positioned above the splitter element
34430  * @static
34431  * @type Number
34432  */
34433 Roo.bootstrap.SplitBar.TOP = 3;
34434
34435 /**
34436  * Placement constant - The resizing element is positioned under splitter element
34437  * @static
34438  * @type Number
34439  */
34440 Roo.bootstrap.SplitBar.BOTTOM = 4;
34441 Roo.namespace("Roo.bootstrap.layout");/*
34442  * Based on:
34443  * Ext JS Library 1.1.1
34444  * Copyright(c) 2006-2007, Ext JS, LLC.
34445  *
34446  * Originally Released Under LGPL - original licence link has changed is not relivant.
34447  *
34448  * Fork - LGPL
34449  * <script type="text/javascript">
34450  */
34451
34452 /**
34453  * @class Roo.bootstrap.layout.Manager
34454  * @extends Roo.bootstrap.Component
34455  * Base class for layout managers.
34456  */
34457 Roo.bootstrap.layout.Manager = function(config)
34458 {
34459     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34460
34461
34462
34463
34464
34465     /** false to disable window resize monitoring @type Boolean */
34466     this.monitorWindowResize = true;
34467     this.regions = {};
34468     this.addEvents({
34469         /**
34470          * @event layout
34471          * Fires when a layout is performed.
34472          * @param {Roo.LayoutManager} this
34473          */
34474         "layout" : true,
34475         /**
34476          * @event regionresized
34477          * Fires when the user resizes a region.
34478          * @param {Roo.LayoutRegion} region The resized region
34479          * @param {Number} newSize The new size (width for east/west, height for north/south)
34480          */
34481         "regionresized" : true,
34482         /**
34483          * @event regioncollapsed
34484          * Fires when a region is collapsed.
34485          * @param {Roo.LayoutRegion} region The collapsed region
34486          */
34487         "regioncollapsed" : true,
34488         /**
34489          * @event regionexpanded
34490          * Fires when a region is expanded.
34491          * @param {Roo.LayoutRegion} region The expanded region
34492          */
34493         "regionexpanded" : true
34494     });
34495     this.updating = false;
34496
34497     if (config.el) {
34498         this.el = Roo.get(config.el);
34499         this.initEvents();
34500     }
34501
34502 };
34503
34504 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34505
34506
34507     regions : null,
34508
34509     monitorWindowResize : true,
34510
34511
34512     updating : false,
34513
34514
34515     onRender : function(ct, position)
34516     {
34517         if(!this.el){
34518             this.el = Roo.get(ct);
34519             this.initEvents();
34520         }
34521         //this.fireEvent('render',this);
34522     },
34523
34524
34525     initEvents: function()
34526     {
34527
34528
34529         // ie scrollbar fix
34530         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34531             document.body.scroll = "no";
34532         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34533             this.el.position('relative');
34534         }
34535         this.id = this.el.id;
34536         this.el.addClass("roo-layout-container");
34537         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34538         if(this.el.dom != document.body ) {
34539             this.el.on('resize', this.layout,this);
34540             this.el.on('show', this.layout,this);
34541         }
34542
34543     },
34544
34545     /**
34546      * Returns true if this layout is currently being updated
34547      * @return {Boolean}
34548      */
34549     isUpdating : function(){
34550         return this.updating;
34551     },
34552
34553     /**
34554      * Suspend the LayoutManager from doing auto-layouts while
34555      * making multiple add or remove calls
34556      */
34557     beginUpdate : function(){
34558         this.updating = true;
34559     },
34560
34561     /**
34562      * Restore auto-layouts and optionally disable the manager from performing a layout
34563      * @param {Boolean} noLayout true to disable a layout update
34564      */
34565     endUpdate : function(noLayout){
34566         this.updating = false;
34567         if(!noLayout){
34568             this.layout();
34569         }
34570     },
34571
34572     layout: function(){
34573         // abstract...
34574     },
34575
34576     onRegionResized : function(region, newSize){
34577         this.fireEvent("regionresized", region, newSize);
34578         this.layout();
34579     },
34580
34581     onRegionCollapsed : function(region){
34582         this.fireEvent("regioncollapsed", region);
34583     },
34584
34585     onRegionExpanded : function(region){
34586         this.fireEvent("regionexpanded", region);
34587     },
34588
34589     /**
34590      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34591      * performs box-model adjustments.
34592      * @return {Object} The size as an object {width: (the width), height: (the height)}
34593      */
34594     getViewSize : function()
34595     {
34596         var size;
34597         if(this.el.dom != document.body){
34598             size = this.el.getSize();
34599         }else{
34600             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34601         }
34602         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34603         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34604         return size;
34605     },
34606
34607     /**
34608      * Returns the Element this layout is bound to.
34609      * @return {Roo.Element}
34610      */
34611     getEl : function(){
34612         return this.el;
34613     },
34614
34615     /**
34616      * Returns the specified region.
34617      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34618      * @return {Roo.LayoutRegion}
34619      */
34620     getRegion : function(target){
34621         return this.regions[target.toLowerCase()];
34622     },
34623
34624     onWindowResize : function(){
34625         if(this.monitorWindowResize){
34626             this.layout();
34627         }
34628     }
34629 });
34630 /*
34631  * Based on:
34632  * Ext JS Library 1.1.1
34633  * Copyright(c) 2006-2007, Ext JS, LLC.
34634  *
34635  * Originally Released Under LGPL - original licence link has changed is not relivant.
34636  *
34637  * Fork - LGPL
34638  * <script type="text/javascript">
34639  */
34640 /**
34641  * @class Roo.bootstrap.layout.Border
34642  * @extends Roo.bootstrap.layout.Manager
34643  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34644  * please see: examples/bootstrap/nested.html<br><br>
34645  
34646 <b>The container the layout is rendered into can be either the body element or any other element.
34647 If it is not the body element, the container needs to either be an absolute positioned element,
34648 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34649 the container size if it is not the body element.</b>
34650
34651 * @constructor
34652 * Create a new Border
34653 * @param {Object} config Configuration options
34654  */
34655 Roo.bootstrap.layout.Border = function(config){
34656     config = config || {};
34657     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34658     
34659     
34660     
34661     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34662         if(config[region]){
34663             config[region].region = region;
34664             this.addRegion(config[region]);
34665         }
34666     },this);
34667     
34668 };
34669
34670 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34671
34672 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34673     /**
34674      * Creates and adds a new region if it doesn't already exist.
34675      * @param {String} target The target region key (north, south, east, west or center).
34676      * @param {Object} config The regions config object
34677      * @return {BorderLayoutRegion} The new region
34678      */
34679     addRegion : function(config)
34680     {
34681         if(!this.regions[config.region]){
34682             var r = this.factory(config);
34683             this.bindRegion(r);
34684         }
34685         return this.regions[config.region];
34686     },
34687
34688     // private (kinda)
34689     bindRegion : function(r){
34690         this.regions[r.config.region] = r;
34691         
34692         r.on("visibilitychange",    this.layout, this);
34693         r.on("paneladded",          this.layout, this);
34694         r.on("panelremoved",        this.layout, this);
34695         r.on("invalidated",         this.layout, this);
34696         r.on("resized",             this.onRegionResized, this);
34697         r.on("collapsed",           this.onRegionCollapsed, this);
34698         r.on("expanded",            this.onRegionExpanded, this);
34699     },
34700
34701     /**
34702      * Performs a layout update.
34703      */
34704     layout : function()
34705     {
34706         if(this.updating) {
34707             return;
34708         }
34709         
34710         // render all the rebions if they have not been done alreayd?
34711         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34712             if(this.regions[region] && !this.regions[region].bodyEl){
34713                 this.regions[region].onRender(this.el)
34714             }
34715         },this);
34716         
34717         var size = this.getViewSize();
34718         var w = size.width;
34719         var h = size.height;
34720         var centerW = w;
34721         var centerH = h;
34722         var centerY = 0;
34723         var centerX = 0;
34724         //var x = 0, y = 0;
34725
34726         var rs = this.regions;
34727         var north = rs["north"];
34728         var south = rs["south"]; 
34729         var west = rs["west"];
34730         var east = rs["east"];
34731         var center = rs["center"];
34732         //if(this.hideOnLayout){ // not supported anymore
34733             //c.el.setStyle("display", "none");
34734         //}
34735         if(north && north.isVisible()){
34736             var b = north.getBox();
34737             var m = north.getMargins();
34738             b.width = w - (m.left+m.right);
34739             b.x = m.left;
34740             b.y = m.top;
34741             centerY = b.height + b.y + m.bottom;
34742             centerH -= centerY;
34743             north.updateBox(this.safeBox(b));
34744         }
34745         if(south && south.isVisible()){
34746             var b = south.getBox();
34747             var m = south.getMargins();
34748             b.width = w - (m.left+m.right);
34749             b.x = m.left;
34750             var totalHeight = (b.height + m.top + m.bottom);
34751             b.y = h - totalHeight + m.top;
34752             centerH -= totalHeight;
34753             south.updateBox(this.safeBox(b));
34754         }
34755         if(west && west.isVisible()){
34756             var b = west.getBox();
34757             var m = west.getMargins();
34758             b.height = centerH - (m.top+m.bottom);
34759             b.x = m.left;
34760             b.y = centerY + m.top;
34761             var totalWidth = (b.width + m.left + m.right);
34762             centerX += totalWidth;
34763             centerW -= totalWidth;
34764             west.updateBox(this.safeBox(b));
34765         }
34766         if(east && east.isVisible()){
34767             var b = east.getBox();
34768             var m = east.getMargins();
34769             b.height = centerH - (m.top+m.bottom);
34770             var totalWidth = (b.width + m.left + m.right);
34771             b.x = w - totalWidth + m.left;
34772             b.y = centerY + m.top;
34773             centerW -= totalWidth;
34774             east.updateBox(this.safeBox(b));
34775         }
34776         if(center){
34777             var m = center.getMargins();
34778             var centerBox = {
34779                 x: centerX + m.left,
34780                 y: centerY + m.top,
34781                 width: centerW - (m.left+m.right),
34782                 height: centerH - (m.top+m.bottom)
34783             };
34784             //if(this.hideOnLayout){
34785                 //center.el.setStyle("display", "block");
34786             //}
34787             center.updateBox(this.safeBox(centerBox));
34788         }
34789         this.el.repaint();
34790         this.fireEvent("layout", this);
34791     },
34792
34793     // private
34794     safeBox : function(box){
34795         box.width = Math.max(0, box.width);
34796         box.height = Math.max(0, box.height);
34797         return box;
34798     },
34799
34800     /**
34801      * Adds a ContentPanel (or subclass) to this layout.
34802      * @param {String} target The target region key (north, south, east, west or center).
34803      * @param {Roo.ContentPanel} panel The panel to add
34804      * @return {Roo.ContentPanel} The added panel
34805      */
34806     add : function(target, panel){
34807          
34808         target = target.toLowerCase();
34809         return this.regions[target].add(panel);
34810     },
34811
34812     /**
34813      * Remove a ContentPanel (or subclass) to this layout.
34814      * @param {String} target The target region key (north, south, east, west or center).
34815      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34816      * @return {Roo.ContentPanel} The removed panel
34817      */
34818     remove : function(target, panel){
34819         target = target.toLowerCase();
34820         return this.regions[target].remove(panel);
34821     },
34822
34823     /**
34824      * Searches all regions for a panel with the specified id
34825      * @param {String} panelId
34826      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34827      */
34828     findPanel : function(panelId){
34829         var rs = this.regions;
34830         for(var target in rs){
34831             if(typeof rs[target] != "function"){
34832                 var p = rs[target].getPanel(panelId);
34833                 if(p){
34834                     return p;
34835                 }
34836             }
34837         }
34838         return null;
34839     },
34840
34841     /**
34842      * Searches all regions for a panel with the specified id and activates (shows) it.
34843      * @param {String/ContentPanel} panelId The panels id or the panel itself
34844      * @return {Roo.ContentPanel} The shown panel or null
34845      */
34846     showPanel : function(panelId) {
34847       var rs = this.regions;
34848       for(var target in rs){
34849          var r = rs[target];
34850          if(typeof r != "function"){
34851             if(r.hasPanel(panelId)){
34852                return r.showPanel(panelId);
34853             }
34854          }
34855       }
34856       return null;
34857    },
34858
34859    /**
34860      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34861      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34862      */
34863    /*
34864     restoreState : function(provider){
34865         if(!provider){
34866             provider = Roo.state.Manager;
34867         }
34868         var sm = new Roo.LayoutStateManager();
34869         sm.init(this, provider);
34870     },
34871 */
34872  
34873  
34874     /**
34875      * Adds a xtype elements to the layout.
34876      * <pre><code>
34877
34878 layout.addxtype({
34879        xtype : 'ContentPanel',
34880        region: 'west',
34881        items: [ .... ]
34882    }
34883 );
34884
34885 layout.addxtype({
34886         xtype : 'NestedLayoutPanel',
34887         region: 'west',
34888         layout: {
34889            center: { },
34890            west: { }   
34891         },
34892         items : [ ... list of content panels or nested layout panels.. ]
34893    }
34894 );
34895 </code></pre>
34896      * @param {Object} cfg Xtype definition of item to add.
34897      */
34898     addxtype : function(cfg)
34899     {
34900         // basically accepts a pannel...
34901         // can accept a layout region..!?!?
34902         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34903         
34904         
34905         // theory?  children can only be panels??
34906         
34907         //if (!cfg.xtype.match(/Panel$/)) {
34908         //    return false;
34909         //}
34910         var ret = false;
34911         
34912         if (typeof(cfg.region) == 'undefined') {
34913             Roo.log("Failed to add Panel, region was not set");
34914             Roo.log(cfg);
34915             return false;
34916         }
34917         var region = cfg.region;
34918         delete cfg.region;
34919         
34920           
34921         var xitems = [];
34922         if (cfg.items) {
34923             xitems = cfg.items;
34924             delete cfg.items;
34925         }
34926         var nb = false;
34927         
34928         switch(cfg.xtype) 
34929         {
34930             case 'Content':  // ContentPanel (el, cfg)
34931             case 'Scroll':  // ContentPanel (el, cfg)
34932             case 'View': 
34933                 cfg.autoCreate = true;
34934                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34935                 //} else {
34936                 //    var el = this.el.createChild();
34937                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34938                 //}
34939                 
34940                 this.add(region, ret);
34941                 break;
34942             
34943             /*
34944             case 'TreePanel': // our new panel!
34945                 cfg.el = this.el.createChild();
34946                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34947                 this.add(region, ret);
34948                 break;
34949             */
34950             
34951             case 'Nest': 
34952                 // create a new Layout (which is  a Border Layout...
34953                 
34954                 var clayout = cfg.layout;
34955                 clayout.el  = this.el.createChild();
34956                 clayout.items   = clayout.items  || [];
34957                 
34958                 delete cfg.layout;
34959                 
34960                 // replace this exitems with the clayout ones..
34961                 xitems = clayout.items;
34962                  
34963                 // force background off if it's in center...
34964                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34965                     cfg.background = false;
34966                 }
34967                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34968                 
34969                 
34970                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34971                 //console.log('adding nested layout panel '  + cfg.toSource());
34972                 this.add(region, ret);
34973                 nb = {}; /// find first...
34974                 break;
34975             
34976             case 'Grid':
34977                 
34978                 // needs grid and region
34979                 
34980                 //var el = this.getRegion(region).el.createChild();
34981                 /*
34982                  *var el = this.el.createChild();
34983                 // create the grid first...
34984                 cfg.grid.container = el;
34985                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34986                 */
34987                 
34988                 if (region == 'center' && this.active ) {
34989                     cfg.background = false;
34990                 }
34991                 
34992                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34993                 
34994                 this.add(region, ret);
34995                 /*
34996                 if (cfg.background) {
34997                     // render grid on panel activation (if panel background)
34998                     ret.on('activate', function(gp) {
34999                         if (!gp.grid.rendered) {
35000                     //        gp.grid.render(el);
35001                         }
35002                     });
35003                 } else {
35004                   //  cfg.grid.render(el);
35005                 }
35006                 */
35007                 break;
35008            
35009            
35010             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35011                 // it was the old xcomponent building that caused this before.
35012                 // espeically if border is the top element in the tree.
35013                 ret = this;
35014                 break; 
35015                 
35016                     
35017                 
35018                 
35019                 
35020             default:
35021                 /*
35022                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35023                     
35024                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35025                     this.add(region, ret);
35026                 } else {
35027                 */
35028                     Roo.log(cfg);
35029                     throw "Can not add '" + cfg.xtype + "' to Border";
35030                     return null;
35031              
35032                                 
35033              
35034         }
35035         this.beginUpdate();
35036         // add children..
35037         var region = '';
35038         var abn = {};
35039         Roo.each(xitems, function(i)  {
35040             region = nb && i.region ? i.region : false;
35041             
35042             var add = ret.addxtype(i);
35043            
35044             if (region) {
35045                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35046                 if (!i.background) {
35047                     abn[region] = nb[region] ;
35048                 }
35049             }
35050             
35051         });
35052         this.endUpdate();
35053
35054         // make the last non-background panel active..
35055         //if (nb) { Roo.log(abn); }
35056         if (nb) {
35057             
35058             for(var r in abn) {
35059                 region = this.getRegion(r);
35060                 if (region) {
35061                     // tried using nb[r], but it does not work..
35062                      
35063                     region.showPanel(abn[r]);
35064                    
35065                 }
35066             }
35067         }
35068         return ret;
35069         
35070     },
35071     
35072     
35073 // private
35074     factory : function(cfg)
35075     {
35076         
35077         var validRegions = Roo.bootstrap.layout.Border.regions;
35078
35079         var target = cfg.region;
35080         cfg.mgr = this;
35081         
35082         var r = Roo.bootstrap.layout;
35083         Roo.log(target);
35084         switch(target){
35085             case "north":
35086                 return new r.North(cfg);
35087             case "south":
35088                 return new r.South(cfg);
35089             case "east":
35090                 return new r.East(cfg);
35091             case "west":
35092                 return new r.West(cfg);
35093             case "center":
35094                 return new r.Center(cfg);
35095         }
35096         throw 'Layout region "'+target+'" not supported.';
35097     }
35098     
35099     
35100 });
35101  /*
35102  * Based on:
35103  * Ext JS Library 1.1.1
35104  * Copyright(c) 2006-2007, Ext JS, LLC.
35105  *
35106  * Originally Released Under LGPL - original licence link has changed is not relivant.
35107  *
35108  * Fork - LGPL
35109  * <script type="text/javascript">
35110  */
35111  
35112 /**
35113  * @class Roo.bootstrap.layout.Basic
35114  * @extends Roo.util.Observable
35115  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35116  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35117  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35118  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35119  * @cfg {string}   region  the region that it inhabits..
35120  * @cfg {bool}   skipConfig skip config?
35121  * 
35122
35123  */
35124 Roo.bootstrap.layout.Basic = function(config){
35125     
35126     this.mgr = config.mgr;
35127     
35128     this.position = config.region;
35129     
35130     var skipConfig = config.skipConfig;
35131     
35132     this.events = {
35133         /**
35134          * @scope Roo.BasicLayoutRegion
35135          */
35136         
35137         /**
35138          * @event beforeremove
35139          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35140          * @param {Roo.LayoutRegion} this
35141          * @param {Roo.ContentPanel} panel The panel
35142          * @param {Object} e The cancel event object
35143          */
35144         "beforeremove" : true,
35145         /**
35146          * @event invalidated
35147          * Fires when the layout for this region is changed.
35148          * @param {Roo.LayoutRegion} this
35149          */
35150         "invalidated" : true,
35151         /**
35152          * @event visibilitychange
35153          * Fires when this region is shown or hidden 
35154          * @param {Roo.LayoutRegion} this
35155          * @param {Boolean} visibility true or false
35156          */
35157         "visibilitychange" : true,
35158         /**
35159          * @event paneladded
35160          * Fires when a panel is added. 
35161          * @param {Roo.LayoutRegion} this
35162          * @param {Roo.ContentPanel} panel The panel
35163          */
35164         "paneladded" : true,
35165         /**
35166          * @event panelremoved
35167          * Fires when a panel is removed. 
35168          * @param {Roo.LayoutRegion} this
35169          * @param {Roo.ContentPanel} panel The panel
35170          */
35171         "panelremoved" : true,
35172         /**
35173          * @event beforecollapse
35174          * Fires when this region before collapse.
35175          * @param {Roo.LayoutRegion} this
35176          */
35177         "beforecollapse" : true,
35178         /**
35179          * @event collapsed
35180          * Fires when this region is collapsed.
35181          * @param {Roo.LayoutRegion} this
35182          */
35183         "collapsed" : true,
35184         /**
35185          * @event expanded
35186          * Fires when this region is expanded.
35187          * @param {Roo.LayoutRegion} this
35188          */
35189         "expanded" : true,
35190         /**
35191          * @event slideshow
35192          * Fires when this region is slid into view.
35193          * @param {Roo.LayoutRegion} this
35194          */
35195         "slideshow" : true,
35196         /**
35197          * @event slidehide
35198          * Fires when this region slides out of view. 
35199          * @param {Roo.LayoutRegion} this
35200          */
35201         "slidehide" : true,
35202         /**
35203          * @event panelactivated
35204          * Fires when a panel is activated. 
35205          * @param {Roo.LayoutRegion} this
35206          * @param {Roo.ContentPanel} panel The activated panel
35207          */
35208         "panelactivated" : true,
35209         /**
35210          * @event resized
35211          * Fires when the user resizes this region. 
35212          * @param {Roo.LayoutRegion} this
35213          * @param {Number} newSize The new size (width for east/west, height for north/south)
35214          */
35215         "resized" : true
35216     };
35217     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35218     this.panels = new Roo.util.MixedCollection();
35219     this.panels.getKey = this.getPanelId.createDelegate(this);
35220     this.box = null;
35221     this.activePanel = null;
35222     // ensure listeners are added...
35223     
35224     if (config.listeners || config.events) {
35225         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35226             listeners : config.listeners || {},
35227             events : config.events || {}
35228         });
35229     }
35230     
35231     if(skipConfig !== true){
35232         this.applyConfig(config);
35233     }
35234 };
35235
35236 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35237 {
35238     getPanelId : function(p){
35239         return p.getId();
35240     },
35241     
35242     applyConfig : function(config){
35243         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35244         this.config = config;
35245         
35246     },
35247     
35248     /**
35249      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35250      * the width, for horizontal (north, south) the height.
35251      * @param {Number} newSize The new width or height
35252      */
35253     resizeTo : function(newSize){
35254         var el = this.el ? this.el :
35255                  (this.activePanel ? this.activePanel.getEl() : null);
35256         if(el){
35257             switch(this.position){
35258                 case "east":
35259                 case "west":
35260                     el.setWidth(newSize);
35261                     this.fireEvent("resized", this, newSize);
35262                 break;
35263                 case "north":
35264                 case "south":
35265                     el.setHeight(newSize);
35266                     this.fireEvent("resized", this, newSize);
35267                 break;                
35268             }
35269         }
35270     },
35271     
35272     getBox : function(){
35273         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35274     },
35275     
35276     getMargins : function(){
35277         return this.margins;
35278     },
35279     
35280     updateBox : function(box){
35281         this.box = box;
35282         var el = this.activePanel.getEl();
35283         el.dom.style.left = box.x + "px";
35284         el.dom.style.top = box.y + "px";
35285         this.activePanel.setSize(box.width, box.height);
35286     },
35287     
35288     /**
35289      * Returns the container element for this region.
35290      * @return {Roo.Element}
35291      */
35292     getEl : function(){
35293         return this.activePanel;
35294     },
35295     
35296     /**
35297      * Returns true if this region is currently visible.
35298      * @return {Boolean}
35299      */
35300     isVisible : function(){
35301         return this.activePanel ? true : false;
35302     },
35303     
35304     setActivePanel : function(panel){
35305         panel = this.getPanel(panel);
35306         if(this.activePanel && this.activePanel != panel){
35307             this.activePanel.setActiveState(false);
35308             this.activePanel.getEl().setLeftTop(-10000,-10000);
35309         }
35310         this.activePanel = panel;
35311         panel.setActiveState(true);
35312         if(this.box){
35313             panel.setSize(this.box.width, this.box.height);
35314         }
35315         this.fireEvent("panelactivated", this, panel);
35316         this.fireEvent("invalidated");
35317     },
35318     
35319     /**
35320      * Show the specified panel.
35321      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35322      * @return {Roo.ContentPanel} The shown panel or null
35323      */
35324     showPanel : function(panel){
35325         panel = this.getPanel(panel);
35326         if(panel){
35327             this.setActivePanel(panel);
35328         }
35329         return panel;
35330     },
35331     
35332     /**
35333      * Get the active panel for this region.
35334      * @return {Roo.ContentPanel} The active panel or null
35335      */
35336     getActivePanel : function(){
35337         return this.activePanel;
35338     },
35339     
35340     /**
35341      * Add the passed ContentPanel(s)
35342      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35343      * @return {Roo.ContentPanel} The panel added (if only one was added)
35344      */
35345     add : function(panel){
35346         if(arguments.length > 1){
35347             for(var i = 0, len = arguments.length; i < len; i++) {
35348                 this.add(arguments[i]);
35349             }
35350             return null;
35351         }
35352         if(this.hasPanel(panel)){
35353             this.showPanel(panel);
35354             return panel;
35355         }
35356         var el = panel.getEl();
35357         if(el.dom.parentNode != this.mgr.el.dom){
35358             this.mgr.el.dom.appendChild(el.dom);
35359         }
35360         if(panel.setRegion){
35361             panel.setRegion(this);
35362         }
35363         this.panels.add(panel);
35364         el.setStyle("position", "absolute");
35365         if(!panel.background){
35366             this.setActivePanel(panel);
35367             if(this.config.initialSize && this.panels.getCount()==1){
35368                 this.resizeTo(this.config.initialSize);
35369             }
35370         }
35371         this.fireEvent("paneladded", this, panel);
35372         return panel;
35373     },
35374     
35375     /**
35376      * Returns true if the panel is in this region.
35377      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35378      * @return {Boolean}
35379      */
35380     hasPanel : function(panel){
35381         if(typeof panel == "object"){ // must be panel obj
35382             panel = panel.getId();
35383         }
35384         return this.getPanel(panel) ? true : false;
35385     },
35386     
35387     /**
35388      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35389      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35390      * @param {Boolean} preservePanel Overrides the config preservePanel option
35391      * @return {Roo.ContentPanel} The panel that was removed
35392      */
35393     remove : function(panel, preservePanel){
35394         panel = this.getPanel(panel);
35395         if(!panel){
35396             return null;
35397         }
35398         var e = {};
35399         this.fireEvent("beforeremove", this, panel, e);
35400         if(e.cancel === true){
35401             return null;
35402         }
35403         var panelId = panel.getId();
35404         this.panels.removeKey(panelId);
35405         return panel;
35406     },
35407     
35408     /**
35409      * Returns the panel specified or null if it's not in this region.
35410      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35411      * @return {Roo.ContentPanel}
35412      */
35413     getPanel : function(id){
35414         if(typeof id == "object"){ // must be panel obj
35415             return id;
35416         }
35417         return this.panels.get(id);
35418     },
35419     
35420     /**
35421      * Returns this regions position (north/south/east/west/center).
35422      * @return {String} 
35423      */
35424     getPosition: function(){
35425         return this.position;    
35426     }
35427 });/*
35428  * Based on:
35429  * Ext JS Library 1.1.1
35430  * Copyright(c) 2006-2007, Ext JS, LLC.
35431  *
35432  * Originally Released Under LGPL - original licence link has changed is not relivant.
35433  *
35434  * Fork - LGPL
35435  * <script type="text/javascript">
35436  */
35437  
35438 /**
35439  * @class Roo.bootstrap.layout.Region
35440  * @extends Roo.bootstrap.layout.Basic
35441  * This class represents a region in a layout manager.
35442  
35443  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35444  * @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})
35445  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35446  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35447  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35448  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35449  * @cfg {String}    title           The title for the region (overrides panel titles)
35450  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35451  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35452  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35453  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35454  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35455  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35456  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35457  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35458  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35459  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35460
35461  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35462  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35463  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35464  * @cfg {Number}    width           For East/West panels
35465  * @cfg {Number}    height          For North/South panels
35466  * @cfg {Boolean}   split           To show the splitter
35467  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35468  * 
35469  * @cfg {string}   cls             Extra CSS classes to add to region
35470  * 
35471  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35472  * @cfg {string}   region  the region that it inhabits..
35473  *
35474
35475  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35476  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35477
35478  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35479  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35480  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35481  */
35482 Roo.bootstrap.layout.Region = function(config)
35483 {
35484     this.applyConfig(config);
35485
35486     var mgr = config.mgr;
35487     var pos = config.region;
35488     config.skipConfig = true;
35489     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35490     
35491     if (mgr.el) {
35492         this.onRender(mgr.el);   
35493     }
35494      
35495     this.visible = true;
35496     this.collapsed = false;
35497     this.unrendered_panels = [];
35498 };
35499
35500 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35501
35502     position: '', // set by wrapper (eg. north/south etc..)
35503     unrendered_panels : null,  // unrendered panels.
35504     createBody : function(){
35505         /** This region's body element 
35506         * @type Roo.Element */
35507         this.bodyEl = this.el.createChild({
35508                 tag: "div",
35509                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35510         });
35511     },
35512
35513     onRender: function(ctr, pos)
35514     {
35515         var dh = Roo.DomHelper;
35516         /** This region's container element 
35517         * @type Roo.Element */
35518         this.el = dh.append(ctr.dom, {
35519                 tag: "div",
35520                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35521             }, true);
35522         /** This region's title element 
35523         * @type Roo.Element */
35524     
35525         this.titleEl = dh.append(this.el.dom,
35526             {
35527                     tag: "div",
35528                     unselectable: "on",
35529                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35530                     children:[
35531                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35532                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35533                     ]}, true);
35534         
35535         this.titleEl.enableDisplayMode();
35536         /** This region's title text element 
35537         * @type HTMLElement */
35538         this.titleTextEl = this.titleEl.dom.firstChild;
35539         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35540         /*
35541         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35542         this.closeBtn.enableDisplayMode();
35543         this.closeBtn.on("click", this.closeClicked, this);
35544         this.closeBtn.hide();
35545     */
35546         this.createBody(this.config);
35547         if(this.config.hideWhenEmpty){
35548             this.hide();
35549             this.on("paneladded", this.validateVisibility, this);
35550             this.on("panelremoved", this.validateVisibility, this);
35551         }
35552         if(this.autoScroll){
35553             this.bodyEl.setStyle("overflow", "auto");
35554         }else{
35555             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35556         }
35557         //if(c.titlebar !== false){
35558             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35559                 this.titleEl.hide();
35560             }else{
35561                 this.titleEl.show();
35562                 if(this.config.title){
35563                     this.titleTextEl.innerHTML = this.config.title;
35564                 }
35565             }
35566         //}
35567         if(this.config.collapsed){
35568             this.collapse(true);
35569         }
35570         if(this.config.hidden){
35571             this.hide();
35572         }
35573         
35574         if (this.unrendered_panels && this.unrendered_panels.length) {
35575             for (var i =0;i< this.unrendered_panels.length; i++) {
35576                 this.add(this.unrendered_panels[i]);
35577             }
35578             this.unrendered_panels = null;
35579             
35580         }
35581         
35582     },
35583     
35584     applyConfig : function(c)
35585     {
35586         /*
35587          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35588             var dh = Roo.DomHelper;
35589             if(c.titlebar !== false){
35590                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35591                 this.collapseBtn.on("click", this.collapse, this);
35592                 this.collapseBtn.enableDisplayMode();
35593                 /*
35594                 if(c.showPin === true || this.showPin){
35595                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35596                     this.stickBtn.enableDisplayMode();
35597                     this.stickBtn.on("click", this.expand, this);
35598                     this.stickBtn.hide();
35599                 }
35600                 
35601             }
35602             */
35603             /** This region's collapsed element
35604             * @type Roo.Element */
35605             /*
35606              *
35607             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35608                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35609             ]}, true);
35610             
35611             if(c.floatable !== false){
35612                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35613                this.collapsedEl.on("click", this.collapseClick, this);
35614             }
35615
35616             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35617                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35618                    id: "message", unselectable: "on", style:{"float":"left"}});
35619                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35620              }
35621             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35622             this.expandBtn.on("click", this.expand, this);
35623             
35624         }
35625         
35626         if(this.collapseBtn){
35627             this.collapseBtn.setVisible(c.collapsible == true);
35628         }
35629         
35630         this.cmargins = c.cmargins || this.cmargins ||
35631                          (this.position == "west" || this.position == "east" ?
35632                              {top: 0, left: 2, right:2, bottom: 0} :
35633                              {top: 2, left: 0, right:0, bottom: 2});
35634         */
35635         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35636         
35637         
35638         this.bottomTabs = c.tabPosition != "top";
35639         
35640         this.autoScroll = c.autoScroll || false;
35641         
35642         
35643        
35644         
35645         this.duration = c.duration || .30;
35646         this.slideDuration = c.slideDuration || .45;
35647         this.config = c;
35648        
35649     },
35650     /**
35651      * Returns true if this region is currently visible.
35652      * @return {Boolean}
35653      */
35654     isVisible : function(){
35655         return this.visible;
35656     },
35657
35658     /**
35659      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35660      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35661      */
35662     //setCollapsedTitle : function(title){
35663     //    title = title || "&#160;";
35664      //   if(this.collapsedTitleTextEl){
35665       //      this.collapsedTitleTextEl.innerHTML = title;
35666        // }
35667     //},
35668
35669     getBox : function(){
35670         var b;
35671       //  if(!this.collapsed){
35672             b = this.el.getBox(false, true);
35673        // }else{
35674           //  b = this.collapsedEl.getBox(false, true);
35675         //}
35676         return b;
35677     },
35678
35679     getMargins : function(){
35680         return this.margins;
35681         //return this.collapsed ? this.cmargins : this.margins;
35682     },
35683 /*
35684     highlight : function(){
35685         this.el.addClass("x-layout-panel-dragover");
35686     },
35687
35688     unhighlight : function(){
35689         this.el.removeClass("x-layout-panel-dragover");
35690     },
35691 */
35692     updateBox : function(box)
35693     {
35694         if (!this.bodyEl) {
35695             return; // not rendered yet..
35696         }
35697         
35698         this.box = box;
35699         if(!this.collapsed){
35700             this.el.dom.style.left = box.x + "px";
35701             this.el.dom.style.top = box.y + "px";
35702             this.updateBody(box.width, box.height);
35703         }else{
35704             this.collapsedEl.dom.style.left = box.x + "px";
35705             this.collapsedEl.dom.style.top = box.y + "px";
35706             this.collapsedEl.setSize(box.width, box.height);
35707         }
35708         if(this.tabs){
35709             this.tabs.autoSizeTabs();
35710         }
35711     },
35712
35713     updateBody : function(w, h)
35714     {
35715         if(w !== null){
35716             this.el.setWidth(w);
35717             w -= this.el.getBorderWidth("rl");
35718             if(this.config.adjustments){
35719                 w += this.config.adjustments[0];
35720             }
35721         }
35722         if(h !== null && h > 0){
35723             this.el.setHeight(h);
35724             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35725             h -= this.el.getBorderWidth("tb");
35726             if(this.config.adjustments){
35727                 h += this.config.adjustments[1];
35728             }
35729             this.bodyEl.setHeight(h);
35730             if(this.tabs){
35731                 h = this.tabs.syncHeight(h);
35732             }
35733         }
35734         if(this.panelSize){
35735             w = w !== null ? w : this.panelSize.width;
35736             h = h !== null ? h : this.panelSize.height;
35737         }
35738         if(this.activePanel){
35739             var el = this.activePanel.getEl();
35740             w = w !== null ? w : el.getWidth();
35741             h = h !== null ? h : el.getHeight();
35742             this.panelSize = {width: w, height: h};
35743             this.activePanel.setSize(w, h);
35744         }
35745         if(Roo.isIE && this.tabs){
35746             this.tabs.el.repaint();
35747         }
35748     },
35749
35750     /**
35751      * Returns the container element for this region.
35752      * @return {Roo.Element}
35753      */
35754     getEl : function(){
35755         return this.el;
35756     },
35757
35758     /**
35759      * Hides this region.
35760      */
35761     hide : function(){
35762         //if(!this.collapsed){
35763             this.el.dom.style.left = "-2000px";
35764             this.el.hide();
35765         //}else{
35766          //   this.collapsedEl.dom.style.left = "-2000px";
35767          //   this.collapsedEl.hide();
35768        // }
35769         this.visible = false;
35770         this.fireEvent("visibilitychange", this, false);
35771     },
35772
35773     /**
35774      * Shows this region if it was previously hidden.
35775      */
35776     show : function(){
35777         //if(!this.collapsed){
35778             this.el.show();
35779         //}else{
35780         //    this.collapsedEl.show();
35781        // }
35782         this.visible = true;
35783         this.fireEvent("visibilitychange", this, true);
35784     },
35785 /*
35786     closeClicked : function(){
35787         if(this.activePanel){
35788             this.remove(this.activePanel);
35789         }
35790     },
35791
35792     collapseClick : function(e){
35793         if(this.isSlid){
35794            e.stopPropagation();
35795            this.slideIn();
35796         }else{
35797            e.stopPropagation();
35798            this.slideOut();
35799         }
35800     },
35801 */
35802     /**
35803      * Collapses this region.
35804      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35805      */
35806     /*
35807     collapse : function(skipAnim, skipCheck = false){
35808         if(this.collapsed) {
35809             return;
35810         }
35811         
35812         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35813             
35814             this.collapsed = true;
35815             if(this.split){
35816                 this.split.el.hide();
35817             }
35818             if(this.config.animate && skipAnim !== true){
35819                 this.fireEvent("invalidated", this);
35820                 this.animateCollapse();
35821             }else{
35822                 this.el.setLocation(-20000,-20000);
35823                 this.el.hide();
35824                 this.collapsedEl.show();
35825                 this.fireEvent("collapsed", this);
35826                 this.fireEvent("invalidated", this);
35827             }
35828         }
35829         
35830     },
35831 */
35832     animateCollapse : function(){
35833         // overridden
35834     },
35835
35836     /**
35837      * Expands this region if it was previously collapsed.
35838      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35839      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35840      */
35841     /*
35842     expand : function(e, skipAnim){
35843         if(e) {
35844             e.stopPropagation();
35845         }
35846         if(!this.collapsed || this.el.hasActiveFx()) {
35847             return;
35848         }
35849         if(this.isSlid){
35850             this.afterSlideIn();
35851             skipAnim = true;
35852         }
35853         this.collapsed = false;
35854         if(this.config.animate && skipAnim !== true){
35855             this.animateExpand();
35856         }else{
35857             this.el.show();
35858             if(this.split){
35859                 this.split.el.show();
35860             }
35861             this.collapsedEl.setLocation(-2000,-2000);
35862             this.collapsedEl.hide();
35863             this.fireEvent("invalidated", this);
35864             this.fireEvent("expanded", this);
35865         }
35866     },
35867 */
35868     animateExpand : function(){
35869         // overridden
35870     },
35871
35872     initTabs : function()
35873     {
35874         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35875         
35876         var ts = new Roo.bootstrap.panel.Tabs({
35877                 el: this.bodyEl.dom,
35878                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35879                 disableTooltips: this.config.disableTabTips,
35880                 toolbar : this.config.toolbar
35881             });
35882         
35883         if(this.config.hideTabs){
35884             ts.stripWrap.setDisplayed(false);
35885         }
35886         this.tabs = ts;
35887         ts.resizeTabs = this.config.resizeTabs === true;
35888         ts.minTabWidth = this.config.minTabWidth || 40;
35889         ts.maxTabWidth = this.config.maxTabWidth || 250;
35890         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35891         ts.monitorResize = false;
35892         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35893         ts.bodyEl.addClass('roo-layout-tabs-body');
35894         this.panels.each(this.initPanelAsTab, this);
35895     },
35896
35897     initPanelAsTab : function(panel){
35898         var ti = this.tabs.addTab(
35899             panel.getEl().id,
35900             panel.getTitle(),
35901             null,
35902             this.config.closeOnTab && panel.isClosable(),
35903             panel.tpl
35904         );
35905         if(panel.tabTip !== undefined){
35906             ti.setTooltip(panel.tabTip);
35907         }
35908         ti.on("activate", function(){
35909               this.setActivePanel(panel);
35910         }, this);
35911         
35912         if(this.config.closeOnTab){
35913             ti.on("beforeclose", function(t, e){
35914                 e.cancel = true;
35915                 this.remove(panel);
35916             }, this);
35917         }
35918         
35919         panel.tabItem = ti;
35920         
35921         return ti;
35922     },
35923
35924     updatePanelTitle : function(panel, title)
35925     {
35926         if(this.activePanel == panel){
35927             this.updateTitle(title);
35928         }
35929         if(this.tabs){
35930             var ti = this.tabs.getTab(panel.getEl().id);
35931             ti.setText(title);
35932             if(panel.tabTip !== undefined){
35933                 ti.setTooltip(panel.tabTip);
35934             }
35935         }
35936     },
35937
35938     updateTitle : function(title){
35939         if(this.titleTextEl && !this.config.title){
35940             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35941         }
35942     },
35943
35944     setActivePanel : function(panel)
35945     {
35946         panel = this.getPanel(panel);
35947         if(this.activePanel && this.activePanel != panel){
35948             if(this.activePanel.setActiveState(false) === false){
35949                 return;
35950             }
35951         }
35952         this.activePanel = panel;
35953         panel.setActiveState(true);
35954         if(this.panelSize){
35955             panel.setSize(this.panelSize.width, this.panelSize.height);
35956         }
35957         if(this.closeBtn){
35958             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35959         }
35960         this.updateTitle(panel.getTitle());
35961         if(this.tabs){
35962             this.fireEvent("invalidated", this);
35963         }
35964         this.fireEvent("panelactivated", this, panel);
35965     },
35966
35967     /**
35968      * Shows the specified panel.
35969      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35970      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35971      */
35972     showPanel : function(panel)
35973     {
35974         panel = this.getPanel(panel);
35975         if(panel){
35976             if(this.tabs){
35977                 var tab = this.tabs.getTab(panel.getEl().id);
35978                 if(tab.isHidden()){
35979                     this.tabs.unhideTab(tab.id);
35980                 }
35981                 tab.activate();
35982             }else{
35983                 this.setActivePanel(panel);
35984             }
35985         }
35986         return panel;
35987     },
35988
35989     /**
35990      * Get the active panel for this region.
35991      * @return {Roo.ContentPanel} The active panel or null
35992      */
35993     getActivePanel : function(){
35994         return this.activePanel;
35995     },
35996
35997     validateVisibility : function(){
35998         if(this.panels.getCount() < 1){
35999             this.updateTitle("&#160;");
36000             this.closeBtn.hide();
36001             this.hide();
36002         }else{
36003             if(!this.isVisible()){
36004                 this.show();
36005             }
36006         }
36007     },
36008
36009     /**
36010      * Adds the passed ContentPanel(s) to this region.
36011      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36012      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36013      */
36014     add : function(panel)
36015     {
36016         if(arguments.length > 1){
36017             for(var i = 0, len = arguments.length; i < len; i++) {
36018                 this.add(arguments[i]);
36019             }
36020             return null;
36021         }
36022         
36023         // if we have not been rendered yet, then we can not really do much of this..
36024         if (!this.bodyEl) {
36025             this.unrendered_panels.push(panel);
36026             return panel;
36027         }
36028         
36029         
36030         
36031         
36032         if(this.hasPanel(panel)){
36033             this.showPanel(panel);
36034             return panel;
36035         }
36036         panel.setRegion(this);
36037         this.panels.add(panel);
36038        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36039             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36040             // and hide them... ???
36041             this.bodyEl.dom.appendChild(panel.getEl().dom);
36042             if(panel.background !== true){
36043                 this.setActivePanel(panel);
36044             }
36045             this.fireEvent("paneladded", this, panel);
36046             return panel;
36047         }
36048         */
36049         if(!this.tabs){
36050             this.initTabs();
36051         }else{
36052             this.initPanelAsTab(panel);
36053         }
36054         
36055         
36056         if(panel.background !== true){
36057             this.tabs.activate(panel.getEl().id);
36058         }
36059         this.fireEvent("paneladded", this, panel);
36060         return panel;
36061     },
36062
36063     /**
36064      * Hides the tab for the specified panel.
36065      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36066      */
36067     hidePanel : function(panel){
36068         if(this.tabs && (panel = this.getPanel(panel))){
36069             this.tabs.hideTab(panel.getEl().id);
36070         }
36071     },
36072
36073     /**
36074      * Unhides the tab for a previously hidden panel.
36075      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36076      */
36077     unhidePanel : function(panel){
36078         if(this.tabs && (panel = this.getPanel(panel))){
36079             this.tabs.unhideTab(panel.getEl().id);
36080         }
36081     },
36082
36083     clearPanels : function(){
36084         while(this.panels.getCount() > 0){
36085              this.remove(this.panels.first());
36086         }
36087     },
36088
36089     /**
36090      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36091      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36092      * @param {Boolean} preservePanel Overrides the config preservePanel option
36093      * @return {Roo.ContentPanel} The panel that was removed
36094      */
36095     remove : function(panel, preservePanel)
36096     {
36097         panel = this.getPanel(panel);
36098         if(!panel){
36099             return null;
36100         }
36101         var e = {};
36102         this.fireEvent("beforeremove", this, panel, e);
36103         if(e.cancel === true){
36104             return null;
36105         }
36106         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36107         var panelId = panel.getId();
36108         this.panels.removeKey(panelId);
36109         if(preservePanel){
36110             document.body.appendChild(panel.getEl().dom);
36111         }
36112         if(this.tabs){
36113             this.tabs.removeTab(panel.getEl().id);
36114         }else if (!preservePanel){
36115             this.bodyEl.dom.removeChild(panel.getEl().dom);
36116         }
36117         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36118             var p = this.panels.first();
36119             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36120             tempEl.appendChild(p.getEl().dom);
36121             this.bodyEl.update("");
36122             this.bodyEl.dom.appendChild(p.getEl().dom);
36123             tempEl = null;
36124             this.updateTitle(p.getTitle());
36125             this.tabs = null;
36126             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36127             this.setActivePanel(p);
36128         }
36129         panel.setRegion(null);
36130         if(this.activePanel == panel){
36131             this.activePanel = null;
36132         }
36133         if(this.config.autoDestroy !== false && preservePanel !== true){
36134             try{panel.destroy();}catch(e){}
36135         }
36136         this.fireEvent("panelremoved", this, panel);
36137         return panel;
36138     },
36139
36140     /**
36141      * Returns the TabPanel component used by this region
36142      * @return {Roo.TabPanel}
36143      */
36144     getTabs : function(){
36145         return this.tabs;
36146     },
36147
36148     createTool : function(parentEl, className){
36149         var btn = Roo.DomHelper.append(parentEl, {
36150             tag: "div",
36151             cls: "x-layout-tools-button",
36152             children: [ {
36153                 tag: "div",
36154                 cls: "roo-layout-tools-button-inner " + className,
36155                 html: "&#160;"
36156             }]
36157         }, true);
36158         btn.addClassOnOver("roo-layout-tools-button-over");
36159         return btn;
36160     }
36161 });/*
36162  * Based on:
36163  * Ext JS Library 1.1.1
36164  * Copyright(c) 2006-2007, Ext JS, LLC.
36165  *
36166  * Originally Released Under LGPL - original licence link has changed is not relivant.
36167  *
36168  * Fork - LGPL
36169  * <script type="text/javascript">
36170  */
36171  
36172
36173
36174 /**
36175  * @class Roo.SplitLayoutRegion
36176  * @extends Roo.LayoutRegion
36177  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36178  */
36179 Roo.bootstrap.layout.Split = function(config){
36180     this.cursor = config.cursor;
36181     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36182 };
36183
36184 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36185 {
36186     splitTip : "Drag to resize.",
36187     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36188     useSplitTips : false,
36189
36190     applyConfig : function(config){
36191         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36192     },
36193     
36194     onRender : function(ctr,pos) {
36195         
36196         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36197         if(!this.config.split){
36198             return;
36199         }
36200         if(!this.split){
36201             
36202             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36203                             tag: "div",
36204                             id: this.el.id + "-split",
36205                             cls: "roo-layout-split roo-layout-split-"+this.position,
36206                             html: "&#160;"
36207             });
36208             /** The SplitBar for this region 
36209             * @type Roo.SplitBar */
36210             // does not exist yet...
36211             Roo.log([this.position, this.orientation]);
36212             
36213             this.split = new Roo.bootstrap.SplitBar({
36214                 dragElement : splitEl,
36215                 resizingElement: this.el,
36216                 orientation : this.orientation
36217             });
36218             
36219             this.split.on("moved", this.onSplitMove, this);
36220             this.split.useShim = this.config.useShim === true;
36221             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36222             if(this.useSplitTips){
36223                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36224             }
36225             //if(config.collapsible){
36226             //    this.split.el.on("dblclick", this.collapse,  this);
36227             //}
36228         }
36229         if(typeof this.config.minSize != "undefined"){
36230             this.split.minSize = this.config.minSize;
36231         }
36232         if(typeof this.config.maxSize != "undefined"){
36233             this.split.maxSize = this.config.maxSize;
36234         }
36235         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36236             this.hideSplitter();
36237         }
36238         
36239     },
36240
36241     getHMaxSize : function(){
36242          var cmax = this.config.maxSize || 10000;
36243          var center = this.mgr.getRegion("center");
36244          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36245     },
36246
36247     getVMaxSize : function(){
36248          var cmax = this.config.maxSize || 10000;
36249          var center = this.mgr.getRegion("center");
36250          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36251     },
36252
36253     onSplitMove : function(split, newSize){
36254         this.fireEvent("resized", this, newSize);
36255     },
36256     
36257     /** 
36258      * Returns the {@link Roo.SplitBar} for this region.
36259      * @return {Roo.SplitBar}
36260      */
36261     getSplitBar : function(){
36262         return this.split;
36263     },
36264     
36265     hide : function(){
36266         this.hideSplitter();
36267         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36268     },
36269
36270     hideSplitter : function(){
36271         if(this.split){
36272             this.split.el.setLocation(-2000,-2000);
36273             this.split.el.hide();
36274         }
36275     },
36276
36277     show : function(){
36278         if(this.split){
36279             this.split.el.show();
36280         }
36281         Roo.bootstrap.layout.Split.superclass.show.call(this);
36282     },
36283     
36284     beforeSlide: function(){
36285         if(Roo.isGecko){// firefox overflow auto bug workaround
36286             this.bodyEl.clip();
36287             if(this.tabs) {
36288                 this.tabs.bodyEl.clip();
36289             }
36290             if(this.activePanel){
36291                 this.activePanel.getEl().clip();
36292                 
36293                 if(this.activePanel.beforeSlide){
36294                     this.activePanel.beforeSlide();
36295                 }
36296             }
36297         }
36298     },
36299     
36300     afterSlide : function(){
36301         if(Roo.isGecko){// firefox overflow auto bug workaround
36302             this.bodyEl.unclip();
36303             if(this.tabs) {
36304                 this.tabs.bodyEl.unclip();
36305             }
36306             if(this.activePanel){
36307                 this.activePanel.getEl().unclip();
36308                 if(this.activePanel.afterSlide){
36309                     this.activePanel.afterSlide();
36310                 }
36311             }
36312         }
36313     },
36314
36315     initAutoHide : function(){
36316         if(this.autoHide !== false){
36317             if(!this.autoHideHd){
36318                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36319                 this.autoHideHd = {
36320                     "mouseout": function(e){
36321                         if(!e.within(this.el, true)){
36322                             st.delay(500);
36323                         }
36324                     },
36325                     "mouseover" : function(e){
36326                         st.cancel();
36327                     },
36328                     scope : this
36329                 };
36330             }
36331             this.el.on(this.autoHideHd);
36332         }
36333     },
36334
36335     clearAutoHide : function(){
36336         if(this.autoHide !== false){
36337             this.el.un("mouseout", this.autoHideHd.mouseout);
36338             this.el.un("mouseover", this.autoHideHd.mouseover);
36339         }
36340     },
36341
36342     clearMonitor : function(){
36343         Roo.get(document).un("click", this.slideInIf, this);
36344     },
36345
36346     // these names are backwards but not changed for compat
36347     slideOut : function(){
36348         if(this.isSlid || this.el.hasActiveFx()){
36349             return;
36350         }
36351         this.isSlid = true;
36352         if(this.collapseBtn){
36353             this.collapseBtn.hide();
36354         }
36355         this.closeBtnState = this.closeBtn.getStyle('display');
36356         this.closeBtn.hide();
36357         if(this.stickBtn){
36358             this.stickBtn.show();
36359         }
36360         this.el.show();
36361         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36362         this.beforeSlide();
36363         this.el.setStyle("z-index", 10001);
36364         this.el.slideIn(this.getSlideAnchor(), {
36365             callback: function(){
36366                 this.afterSlide();
36367                 this.initAutoHide();
36368                 Roo.get(document).on("click", this.slideInIf, this);
36369                 this.fireEvent("slideshow", this);
36370             },
36371             scope: this,
36372             block: true
36373         });
36374     },
36375
36376     afterSlideIn : function(){
36377         this.clearAutoHide();
36378         this.isSlid = false;
36379         this.clearMonitor();
36380         this.el.setStyle("z-index", "");
36381         if(this.collapseBtn){
36382             this.collapseBtn.show();
36383         }
36384         this.closeBtn.setStyle('display', this.closeBtnState);
36385         if(this.stickBtn){
36386             this.stickBtn.hide();
36387         }
36388         this.fireEvent("slidehide", this);
36389     },
36390
36391     slideIn : function(cb){
36392         if(!this.isSlid || this.el.hasActiveFx()){
36393             Roo.callback(cb);
36394             return;
36395         }
36396         this.isSlid = false;
36397         this.beforeSlide();
36398         this.el.slideOut(this.getSlideAnchor(), {
36399             callback: function(){
36400                 this.el.setLeftTop(-10000, -10000);
36401                 this.afterSlide();
36402                 this.afterSlideIn();
36403                 Roo.callback(cb);
36404             },
36405             scope: this,
36406             block: true
36407         });
36408     },
36409     
36410     slideInIf : function(e){
36411         if(!e.within(this.el)){
36412             this.slideIn();
36413         }
36414     },
36415
36416     animateCollapse : function(){
36417         this.beforeSlide();
36418         this.el.setStyle("z-index", 20000);
36419         var anchor = this.getSlideAnchor();
36420         this.el.slideOut(anchor, {
36421             callback : function(){
36422                 this.el.setStyle("z-index", "");
36423                 this.collapsedEl.slideIn(anchor, {duration:.3});
36424                 this.afterSlide();
36425                 this.el.setLocation(-10000,-10000);
36426                 this.el.hide();
36427                 this.fireEvent("collapsed", this);
36428             },
36429             scope: this,
36430             block: true
36431         });
36432     },
36433
36434     animateExpand : function(){
36435         this.beforeSlide();
36436         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36437         this.el.setStyle("z-index", 20000);
36438         this.collapsedEl.hide({
36439             duration:.1
36440         });
36441         this.el.slideIn(this.getSlideAnchor(), {
36442             callback : function(){
36443                 this.el.setStyle("z-index", "");
36444                 this.afterSlide();
36445                 if(this.split){
36446                     this.split.el.show();
36447                 }
36448                 this.fireEvent("invalidated", this);
36449                 this.fireEvent("expanded", this);
36450             },
36451             scope: this,
36452             block: true
36453         });
36454     },
36455
36456     anchors : {
36457         "west" : "left",
36458         "east" : "right",
36459         "north" : "top",
36460         "south" : "bottom"
36461     },
36462
36463     sanchors : {
36464         "west" : "l",
36465         "east" : "r",
36466         "north" : "t",
36467         "south" : "b"
36468     },
36469
36470     canchors : {
36471         "west" : "tl-tr",
36472         "east" : "tr-tl",
36473         "north" : "tl-bl",
36474         "south" : "bl-tl"
36475     },
36476
36477     getAnchor : function(){
36478         return this.anchors[this.position];
36479     },
36480
36481     getCollapseAnchor : function(){
36482         return this.canchors[this.position];
36483     },
36484
36485     getSlideAnchor : function(){
36486         return this.sanchors[this.position];
36487     },
36488
36489     getAlignAdj : function(){
36490         var cm = this.cmargins;
36491         switch(this.position){
36492             case "west":
36493                 return [0, 0];
36494             break;
36495             case "east":
36496                 return [0, 0];
36497             break;
36498             case "north":
36499                 return [0, 0];
36500             break;
36501             case "south":
36502                 return [0, 0];
36503             break;
36504         }
36505     },
36506
36507     getExpandAdj : function(){
36508         var c = this.collapsedEl, cm = this.cmargins;
36509         switch(this.position){
36510             case "west":
36511                 return [-(cm.right+c.getWidth()+cm.left), 0];
36512             break;
36513             case "east":
36514                 return [cm.right+c.getWidth()+cm.left, 0];
36515             break;
36516             case "north":
36517                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36518             break;
36519             case "south":
36520                 return [0, cm.top+cm.bottom+c.getHeight()];
36521             break;
36522         }
36523     }
36524 });/*
36525  * Based on:
36526  * Ext JS Library 1.1.1
36527  * Copyright(c) 2006-2007, Ext JS, LLC.
36528  *
36529  * Originally Released Under LGPL - original licence link has changed is not relivant.
36530  *
36531  * Fork - LGPL
36532  * <script type="text/javascript">
36533  */
36534 /*
36535  * These classes are private internal classes
36536  */
36537 Roo.bootstrap.layout.Center = function(config){
36538     config.region = "center";
36539     Roo.bootstrap.layout.Region.call(this, config);
36540     this.visible = true;
36541     this.minWidth = config.minWidth || 20;
36542     this.minHeight = config.minHeight || 20;
36543 };
36544
36545 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36546     hide : function(){
36547         // center panel can't be hidden
36548     },
36549     
36550     show : function(){
36551         // center panel can't be hidden
36552     },
36553     
36554     getMinWidth: function(){
36555         return this.minWidth;
36556     },
36557     
36558     getMinHeight: function(){
36559         return this.minHeight;
36560     }
36561 });
36562
36563
36564
36565
36566  
36567
36568
36569
36570
36571
36572 Roo.bootstrap.layout.North = function(config)
36573 {
36574     config.region = 'north';
36575     config.cursor = 'n-resize';
36576     
36577     Roo.bootstrap.layout.Split.call(this, config);
36578     
36579     
36580     if(this.split){
36581         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36582         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36583         this.split.el.addClass("roo-layout-split-v");
36584     }
36585     var size = config.initialSize || config.height;
36586     if(typeof size != "undefined"){
36587         this.el.setHeight(size);
36588     }
36589 };
36590 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36591 {
36592     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36593     
36594     
36595     
36596     getBox : function(){
36597         if(this.collapsed){
36598             return this.collapsedEl.getBox();
36599         }
36600         var box = this.el.getBox();
36601         if(this.split){
36602             box.height += this.split.el.getHeight();
36603         }
36604         return box;
36605     },
36606     
36607     updateBox : function(box){
36608         if(this.split && !this.collapsed){
36609             box.height -= this.split.el.getHeight();
36610             this.split.el.setLeft(box.x);
36611             this.split.el.setTop(box.y+box.height);
36612             this.split.el.setWidth(box.width);
36613         }
36614         if(this.collapsed){
36615             this.updateBody(box.width, null);
36616         }
36617         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36618     }
36619 });
36620
36621
36622
36623
36624
36625 Roo.bootstrap.layout.South = function(config){
36626     config.region = 'south';
36627     config.cursor = 's-resize';
36628     Roo.bootstrap.layout.Split.call(this, config);
36629     if(this.split){
36630         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36631         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36632         this.split.el.addClass("roo-layout-split-v");
36633     }
36634     var size = config.initialSize || config.height;
36635     if(typeof size != "undefined"){
36636         this.el.setHeight(size);
36637     }
36638 };
36639
36640 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36641     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36642     getBox : function(){
36643         if(this.collapsed){
36644             return this.collapsedEl.getBox();
36645         }
36646         var box = this.el.getBox();
36647         if(this.split){
36648             var sh = this.split.el.getHeight();
36649             box.height += sh;
36650             box.y -= sh;
36651         }
36652         return box;
36653     },
36654     
36655     updateBox : function(box){
36656         if(this.split && !this.collapsed){
36657             var sh = this.split.el.getHeight();
36658             box.height -= sh;
36659             box.y += sh;
36660             this.split.el.setLeft(box.x);
36661             this.split.el.setTop(box.y-sh);
36662             this.split.el.setWidth(box.width);
36663         }
36664         if(this.collapsed){
36665             this.updateBody(box.width, null);
36666         }
36667         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36668     }
36669 });
36670
36671 Roo.bootstrap.layout.East = function(config){
36672     config.region = "east";
36673     config.cursor = "e-resize";
36674     Roo.bootstrap.layout.Split.call(this, config);
36675     if(this.split){
36676         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36677         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36678         this.split.el.addClass("roo-layout-split-h");
36679     }
36680     var size = config.initialSize || config.width;
36681     if(typeof size != "undefined"){
36682         this.el.setWidth(size);
36683     }
36684 };
36685 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36686     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36687     getBox : function(){
36688         if(this.collapsed){
36689             return this.collapsedEl.getBox();
36690         }
36691         var box = this.el.getBox();
36692         if(this.split){
36693             var sw = this.split.el.getWidth();
36694             box.width += sw;
36695             box.x -= sw;
36696         }
36697         return box;
36698     },
36699
36700     updateBox : function(box){
36701         if(this.split && !this.collapsed){
36702             var sw = this.split.el.getWidth();
36703             box.width -= sw;
36704             this.split.el.setLeft(box.x);
36705             this.split.el.setTop(box.y);
36706             this.split.el.setHeight(box.height);
36707             box.x += sw;
36708         }
36709         if(this.collapsed){
36710             this.updateBody(null, box.height);
36711         }
36712         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36713     }
36714 });
36715
36716 Roo.bootstrap.layout.West = function(config){
36717     config.region = "west";
36718     config.cursor = "w-resize";
36719     
36720     Roo.bootstrap.layout.Split.call(this, config);
36721     if(this.split){
36722         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36723         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36724         this.split.el.addClass("roo-layout-split-h");
36725     }
36726     
36727 };
36728 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36729     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36730     
36731     onRender: function(ctr, pos)
36732     {
36733         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36734         var size = this.config.initialSize || this.config.width;
36735         if(typeof size != "undefined"){
36736             this.el.setWidth(size);
36737         }
36738     },
36739     
36740     getBox : function(){
36741         if(this.collapsed){
36742             return this.collapsedEl.getBox();
36743         }
36744         var box = this.el.getBox();
36745         if(this.split){
36746             box.width += this.split.el.getWidth();
36747         }
36748         return box;
36749     },
36750     
36751     updateBox : function(box){
36752         if(this.split && !this.collapsed){
36753             var sw = this.split.el.getWidth();
36754             box.width -= sw;
36755             this.split.el.setLeft(box.x+box.width);
36756             this.split.el.setTop(box.y);
36757             this.split.el.setHeight(box.height);
36758         }
36759         if(this.collapsed){
36760             this.updateBody(null, box.height);
36761         }
36762         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36763     }
36764 });
36765 Roo.namespace("Roo.bootstrap.panel");/*
36766  * Based on:
36767  * Ext JS Library 1.1.1
36768  * Copyright(c) 2006-2007, Ext JS, LLC.
36769  *
36770  * Originally Released Under LGPL - original licence link has changed is not relivant.
36771  *
36772  * Fork - LGPL
36773  * <script type="text/javascript">
36774  */
36775 /**
36776  * @class Roo.ContentPanel
36777  * @extends Roo.util.Observable
36778  * A basic ContentPanel element.
36779  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36780  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36781  * @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
36782  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36783  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36784  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36785  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36786  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36787  * @cfg {String} title          The title for this panel
36788  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36789  * @cfg {String} url            Calls {@link #setUrl} with this value
36790  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36791  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36792  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36793  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36794  * @cfg {Boolean} badges render the badges
36795
36796  * @constructor
36797  * Create a new ContentPanel.
36798  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36799  * @param {String/Object} config A string to set only the title or a config object
36800  * @param {String} content (optional) Set the HTML content for this panel
36801  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36802  */
36803 Roo.bootstrap.panel.Content = function( config){
36804     
36805     this.tpl = config.tpl || false;
36806     
36807     var el = config.el;
36808     var content = config.content;
36809
36810     if(config.autoCreate){ // xtype is available if this is called from factory
36811         el = Roo.id();
36812     }
36813     this.el = Roo.get(el);
36814     if(!this.el && config && config.autoCreate){
36815         if(typeof config.autoCreate == "object"){
36816             if(!config.autoCreate.id){
36817                 config.autoCreate.id = config.id||el;
36818             }
36819             this.el = Roo.DomHelper.append(document.body,
36820                         config.autoCreate, true);
36821         }else{
36822             var elcfg =  {   tag: "div",
36823                             cls: "roo-layout-inactive-content",
36824                             id: config.id||el
36825                             };
36826             if (config.html) {
36827                 elcfg.html = config.html;
36828                 
36829             }
36830                         
36831             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36832         }
36833     } 
36834     this.closable = false;
36835     this.loaded = false;
36836     this.active = false;
36837    
36838       
36839     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36840         
36841         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36842         
36843         this.wrapEl = this.el; //this.el.wrap();
36844         var ti = [];
36845         if (config.toolbar.items) {
36846             ti = config.toolbar.items ;
36847             delete config.toolbar.items ;
36848         }
36849         
36850         var nitems = [];
36851         this.toolbar.render(this.wrapEl, 'before');
36852         for(var i =0;i < ti.length;i++) {
36853           //  Roo.log(['add child', items[i]]);
36854             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36855         }
36856         this.toolbar.items = nitems;
36857         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36858         delete config.toolbar;
36859         
36860     }
36861     /*
36862     // xtype created footer. - not sure if will work as we normally have to render first..
36863     if (this.footer && !this.footer.el && this.footer.xtype) {
36864         if (!this.wrapEl) {
36865             this.wrapEl = this.el.wrap();
36866         }
36867     
36868         this.footer.container = this.wrapEl.createChild();
36869          
36870         this.footer = Roo.factory(this.footer, Roo);
36871         
36872     }
36873     */
36874     
36875      if(typeof config == "string"){
36876         this.title = config;
36877     }else{
36878         Roo.apply(this, config);
36879     }
36880     
36881     if(this.resizeEl){
36882         this.resizeEl = Roo.get(this.resizeEl, true);
36883     }else{
36884         this.resizeEl = this.el;
36885     }
36886     // handle view.xtype
36887     
36888  
36889     
36890     
36891     this.addEvents({
36892         /**
36893          * @event activate
36894          * Fires when this panel is activated. 
36895          * @param {Roo.ContentPanel} this
36896          */
36897         "activate" : true,
36898         /**
36899          * @event deactivate
36900          * Fires when this panel is activated. 
36901          * @param {Roo.ContentPanel} this
36902          */
36903         "deactivate" : true,
36904
36905         /**
36906          * @event resize
36907          * Fires when this panel is resized if fitToFrame is true.
36908          * @param {Roo.ContentPanel} this
36909          * @param {Number} width The width after any component adjustments
36910          * @param {Number} height The height after any component adjustments
36911          */
36912         "resize" : true,
36913         
36914          /**
36915          * @event render
36916          * Fires when this tab is created
36917          * @param {Roo.ContentPanel} this
36918          */
36919         "render" : true
36920         
36921         
36922         
36923     });
36924     
36925
36926     
36927     
36928     if(this.autoScroll){
36929         this.resizeEl.setStyle("overflow", "auto");
36930     } else {
36931         // fix randome scrolling
36932         //this.el.on('scroll', function() {
36933         //    Roo.log('fix random scolling');
36934         //    this.scrollTo('top',0); 
36935         //});
36936     }
36937     content = content || this.content;
36938     if(content){
36939         this.setContent(content);
36940     }
36941     if(config && config.url){
36942         this.setUrl(this.url, this.params, this.loadOnce);
36943     }
36944     
36945     
36946     
36947     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36948     
36949     if (this.view && typeof(this.view.xtype) != 'undefined') {
36950         this.view.el = this.el.appendChild(document.createElement("div"));
36951         this.view = Roo.factory(this.view); 
36952         this.view.render  &&  this.view.render(false, '');  
36953     }
36954     
36955     
36956     this.fireEvent('render', this);
36957 };
36958
36959 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36960     
36961     tabTip : '',
36962     
36963     setRegion : function(region){
36964         this.region = region;
36965         this.setActiveClass(region && !this.background);
36966     },
36967     
36968     
36969     setActiveClass: function(state)
36970     {
36971         if(state){
36972            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36973            this.el.setStyle('position','relative');
36974         }else{
36975            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36976            this.el.setStyle('position', 'absolute');
36977         } 
36978     },
36979     
36980     /**
36981      * Returns the toolbar for this Panel if one was configured. 
36982      * @return {Roo.Toolbar} 
36983      */
36984     getToolbar : function(){
36985         return this.toolbar;
36986     },
36987     
36988     setActiveState : function(active)
36989     {
36990         this.active = active;
36991         this.setActiveClass(active);
36992         if(!active){
36993             if(this.fireEvent("deactivate", this) === false){
36994                 return false;
36995             }
36996             return true;
36997         }
36998         this.fireEvent("activate", this);
36999         return true;
37000     },
37001     /**
37002      * Updates this panel's element
37003      * @param {String} content The new content
37004      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37005     */
37006     setContent : function(content, loadScripts){
37007         this.el.update(content, loadScripts);
37008     },
37009
37010     ignoreResize : function(w, h){
37011         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37012             return true;
37013         }else{
37014             this.lastSize = {width: w, height: h};
37015             return false;
37016         }
37017     },
37018     /**
37019      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37020      * @return {Roo.UpdateManager} The UpdateManager
37021      */
37022     getUpdateManager : function(){
37023         return this.el.getUpdateManager();
37024     },
37025      /**
37026      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37027      * @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:
37028 <pre><code>
37029 panel.load({
37030     url: "your-url.php",
37031     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37032     callback: yourFunction,
37033     scope: yourObject, //(optional scope)
37034     discardUrl: false,
37035     nocache: false,
37036     text: "Loading...",
37037     timeout: 30,
37038     scripts: false
37039 });
37040 </code></pre>
37041      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37042      * 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.
37043      * @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}
37044      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37045      * @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.
37046      * @return {Roo.ContentPanel} this
37047      */
37048     load : function(){
37049         var um = this.el.getUpdateManager();
37050         um.update.apply(um, arguments);
37051         return this;
37052     },
37053
37054
37055     /**
37056      * 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.
37057      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37058      * @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)
37059      * @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)
37060      * @return {Roo.UpdateManager} The UpdateManager
37061      */
37062     setUrl : function(url, params, loadOnce){
37063         if(this.refreshDelegate){
37064             this.removeListener("activate", this.refreshDelegate);
37065         }
37066         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37067         this.on("activate", this.refreshDelegate);
37068         return this.el.getUpdateManager();
37069     },
37070     
37071     _handleRefresh : function(url, params, loadOnce){
37072         if(!loadOnce || !this.loaded){
37073             var updater = this.el.getUpdateManager();
37074             updater.update(url, params, this._setLoaded.createDelegate(this));
37075         }
37076     },
37077     
37078     _setLoaded : function(){
37079         this.loaded = true;
37080     }, 
37081     
37082     /**
37083      * Returns this panel's id
37084      * @return {String} 
37085      */
37086     getId : function(){
37087         return this.el.id;
37088     },
37089     
37090     /** 
37091      * Returns this panel's element - used by regiosn to add.
37092      * @return {Roo.Element} 
37093      */
37094     getEl : function(){
37095         return this.wrapEl || this.el;
37096     },
37097     
37098    
37099     
37100     adjustForComponents : function(width, height)
37101     {
37102         //Roo.log('adjustForComponents ');
37103         if(this.resizeEl != this.el){
37104             width -= this.el.getFrameWidth('lr');
37105             height -= this.el.getFrameWidth('tb');
37106         }
37107         if(this.toolbar){
37108             var te = this.toolbar.getEl();
37109             te.setWidth(width);
37110             height -= te.getHeight();
37111         }
37112         if(this.footer){
37113             var te = this.footer.getEl();
37114             te.setWidth(width);
37115             height -= te.getHeight();
37116         }
37117         
37118         
37119         if(this.adjustments){
37120             width += this.adjustments[0];
37121             height += this.adjustments[1];
37122         }
37123         return {"width": width, "height": height};
37124     },
37125     
37126     setSize : function(width, height){
37127         if(this.fitToFrame && !this.ignoreResize(width, height)){
37128             if(this.fitContainer && this.resizeEl != this.el){
37129                 this.el.setSize(width, height);
37130             }
37131             var size = this.adjustForComponents(width, height);
37132             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37133             this.fireEvent('resize', this, size.width, size.height);
37134         }
37135     },
37136     
37137     /**
37138      * Returns this panel's title
37139      * @return {String} 
37140      */
37141     getTitle : function(){
37142         
37143         if (typeof(this.title) != 'object') {
37144             return this.title;
37145         }
37146         
37147         var t = '';
37148         for (var k in this.title) {
37149             if (!this.title.hasOwnProperty(k)) {
37150                 continue;
37151             }
37152             
37153             if (k.indexOf('-') >= 0) {
37154                 var s = k.split('-');
37155                 for (var i = 0; i<s.length; i++) {
37156                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37157                 }
37158             } else {
37159                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37160             }
37161         }
37162         return t;
37163     },
37164     
37165     /**
37166      * Set this panel's title
37167      * @param {String} title
37168      */
37169     setTitle : function(title){
37170         this.title = title;
37171         if(this.region){
37172             this.region.updatePanelTitle(this, title);
37173         }
37174     },
37175     
37176     /**
37177      * Returns true is this panel was configured to be closable
37178      * @return {Boolean} 
37179      */
37180     isClosable : function(){
37181         return this.closable;
37182     },
37183     
37184     beforeSlide : function(){
37185         this.el.clip();
37186         this.resizeEl.clip();
37187     },
37188     
37189     afterSlide : function(){
37190         this.el.unclip();
37191         this.resizeEl.unclip();
37192     },
37193     
37194     /**
37195      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37196      *   Will fail silently if the {@link #setUrl} method has not been called.
37197      *   This does not activate the panel, just updates its content.
37198      */
37199     refresh : function(){
37200         if(this.refreshDelegate){
37201            this.loaded = false;
37202            this.refreshDelegate();
37203         }
37204     },
37205     
37206     /**
37207      * Destroys this panel
37208      */
37209     destroy : function(){
37210         this.el.removeAllListeners();
37211         var tempEl = document.createElement("span");
37212         tempEl.appendChild(this.el.dom);
37213         tempEl.innerHTML = "";
37214         this.el.remove();
37215         this.el = null;
37216     },
37217     
37218     /**
37219      * form - if the content panel contains a form - this is a reference to it.
37220      * @type {Roo.form.Form}
37221      */
37222     form : false,
37223     /**
37224      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37225      *    This contains a reference to it.
37226      * @type {Roo.View}
37227      */
37228     view : false,
37229     
37230       /**
37231      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37232      * <pre><code>
37233
37234 layout.addxtype({
37235        xtype : 'Form',
37236        items: [ .... ]
37237    }
37238 );
37239
37240 </code></pre>
37241      * @param {Object} cfg Xtype definition of item to add.
37242      */
37243     
37244     
37245     getChildContainer: function () {
37246         return this.getEl();
37247     }
37248     
37249     
37250     /*
37251         var  ret = new Roo.factory(cfg);
37252         return ret;
37253         
37254         
37255         // add form..
37256         if (cfg.xtype.match(/^Form$/)) {
37257             
37258             var el;
37259             //if (this.footer) {
37260             //    el = this.footer.container.insertSibling(false, 'before');
37261             //} else {
37262                 el = this.el.createChild();
37263             //}
37264
37265             this.form = new  Roo.form.Form(cfg);
37266             
37267             
37268             if ( this.form.allItems.length) {
37269                 this.form.render(el.dom);
37270             }
37271             return this.form;
37272         }
37273         // should only have one of theses..
37274         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37275             // views.. should not be just added - used named prop 'view''
37276             
37277             cfg.el = this.el.appendChild(document.createElement("div"));
37278             // factory?
37279             
37280             var ret = new Roo.factory(cfg);
37281              
37282              ret.render && ret.render(false, ''); // render blank..
37283             this.view = ret;
37284             return ret;
37285         }
37286         return false;
37287     }
37288     \*/
37289 });
37290  
37291 /**
37292  * @class Roo.bootstrap.panel.Grid
37293  * @extends Roo.bootstrap.panel.Content
37294  * @constructor
37295  * Create a new GridPanel.
37296  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37297  * @param {Object} config A the config object
37298   
37299  */
37300
37301
37302
37303 Roo.bootstrap.panel.Grid = function(config)
37304 {
37305     
37306       
37307     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37308         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37309
37310     config.el = this.wrapper;
37311     //this.el = this.wrapper;
37312     
37313       if (config.container) {
37314         // ctor'ed from a Border/panel.grid
37315         
37316         
37317         this.wrapper.setStyle("overflow", "hidden");
37318         this.wrapper.addClass('roo-grid-container');
37319
37320     }
37321     
37322     
37323     if(config.toolbar){
37324         var tool_el = this.wrapper.createChild();    
37325         this.toolbar = Roo.factory(config.toolbar);
37326         var ti = [];
37327         if (config.toolbar.items) {
37328             ti = config.toolbar.items ;
37329             delete config.toolbar.items ;
37330         }
37331         
37332         var nitems = [];
37333         this.toolbar.render(tool_el);
37334         for(var i =0;i < ti.length;i++) {
37335           //  Roo.log(['add child', items[i]]);
37336             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37337         }
37338         this.toolbar.items = nitems;
37339         
37340         delete config.toolbar;
37341     }
37342     
37343     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37344     config.grid.scrollBody = true;;
37345     config.grid.monitorWindowResize = false; // turn off autosizing
37346     config.grid.autoHeight = false;
37347     config.grid.autoWidth = false;
37348     
37349     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37350     
37351     if (config.background) {
37352         // render grid on panel activation (if panel background)
37353         this.on('activate', function(gp) {
37354             if (!gp.grid.rendered) {
37355                 gp.grid.render(this.wrapper);
37356                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37357             }
37358         });
37359             
37360     } else {
37361         this.grid.render(this.wrapper);
37362         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37363
37364     }
37365     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37366     // ??? needed ??? config.el = this.wrapper;
37367     
37368     
37369     
37370   
37371     // xtype created footer. - not sure if will work as we normally have to render first..
37372     if (this.footer && !this.footer.el && this.footer.xtype) {
37373         
37374         var ctr = this.grid.getView().getFooterPanel(true);
37375         this.footer.dataSource = this.grid.dataSource;
37376         this.footer = Roo.factory(this.footer, Roo);
37377         this.footer.render(ctr);
37378         
37379     }
37380     
37381     
37382     
37383     
37384      
37385 };
37386
37387 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37388     getId : function(){
37389         return this.grid.id;
37390     },
37391     
37392     /**
37393      * Returns the grid for this panel
37394      * @return {Roo.bootstrap.Table} 
37395      */
37396     getGrid : function(){
37397         return this.grid;    
37398     },
37399     
37400     setSize : function(width, height){
37401         if(!this.ignoreResize(width, height)){
37402             var grid = this.grid;
37403             var size = this.adjustForComponents(width, height);
37404             var gridel = grid.getGridEl();
37405             gridel.setSize(size.width, size.height);
37406             /*
37407             var thd = grid.getGridEl().select('thead',true).first();
37408             var tbd = grid.getGridEl().select('tbody', true).first();
37409             if (tbd) {
37410                 tbd.setSize(width, height - thd.getHeight());
37411             }
37412             */
37413             grid.autoSize();
37414         }
37415     },
37416      
37417     
37418     
37419     beforeSlide : function(){
37420         this.grid.getView().scroller.clip();
37421     },
37422     
37423     afterSlide : function(){
37424         this.grid.getView().scroller.unclip();
37425     },
37426     
37427     destroy : function(){
37428         this.grid.destroy();
37429         delete this.grid;
37430         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37431     }
37432 });
37433
37434 /**
37435  * @class Roo.bootstrap.panel.Nest
37436  * @extends Roo.bootstrap.panel.Content
37437  * @constructor
37438  * Create a new Panel, that can contain a layout.Border.
37439  * 
37440  * 
37441  * @param {Roo.BorderLayout} layout The layout for this panel
37442  * @param {String/Object} config A string to set only the title or a config object
37443  */
37444 Roo.bootstrap.panel.Nest = function(config)
37445 {
37446     // construct with only one argument..
37447     /* FIXME - implement nicer consturctors
37448     if (layout.layout) {
37449         config = layout;
37450         layout = config.layout;
37451         delete config.layout;
37452     }
37453     if (layout.xtype && !layout.getEl) {
37454         // then layout needs constructing..
37455         layout = Roo.factory(layout, Roo);
37456     }
37457     */
37458     
37459     config.el =  config.layout.getEl();
37460     
37461     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37462     
37463     config.layout.monitorWindowResize = false; // turn off autosizing
37464     this.layout = config.layout;
37465     this.layout.getEl().addClass("roo-layout-nested-layout");
37466     
37467     
37468     
37469     
37470 };
37471
37472 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37473
37474     setSize : function(width, height){
37475         if(!this.ignoreResize(width, height)){
37476             var size = this.adjustForComponents(width, height);
37477             var el = this.layout.getEl();
37478             if (size.height < 1) {
37479                 el.setWidth(size.width);   
37480             } else {
37481                 el.setSize(size.width, size.height);
37482             }
37483             var touch = el.dom.offsetWidth;
37484             this.layout.layout();
37485             // ie requires a double layout on the first pass
37486             if(Roo.isIE && !this.initialized){
37487                 this.initialized = true;
37488                 this.layout.layout();
37489             }
37490         }
37491     },
37492     
37493     // activate all subpanels if not currently active..
37494     
37495     setActiveState : function(active){
37496         this.active = active;
37497         this.setActiveClass(active);
37498         
37499         if(!active){
37500             this.fireEvent("deactivate", this);
37501             return;
37502         }
37503         
37504         this.fireEvent("activate", this);
37505         // not sure if this should happen before or after..
37506         if (!this.layout) {
37507             return; // should not happen..
37508         }
37509         var reg = false;
37510         for (var r in this.layout.regions) {
37511             reg = this.layout.getRegion(r);
37512             if (reg.getActivePanel()) {
37513                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37514                 reg.setActivePanel(reg.getActivePanel());
37515                 continue;
37516             }
37517             if (!reg.panels.length) {
37518                 continue;
37519             }
37520             reg.showPanel(reg.getPanel(0));
37521         }
37522         
37523         
37524         
37525         
37526     },
37527     
37528     /**
37529      * Returns the nested BorderLayout for this panel
37530      * @return {Roo.BorderLayout} 
37531      */
37532     getLayout : function(){
37533         return this.layout;
37534     },
37535     
37536      /**
37537      * Adds a xtype elements to the layout of the nested panel
37538      * <pre><code>
37539
37540 panel.addxtype({
37541        xtype : 'ContentPanel',
37542        region: 'west',
37543        items: [ .... ]
37544    }
37545 );
37546
37547 panel.addxtype({
37548         xtype : 'NestedLayoutPanel',
37549         region: 'west',
37550         layout: {
37551            center: { },
37552            west: { }   
37553         },
37554         items : [ ... list of content panels or nested layout panels.. ]
37555    }
37556 );
37557 </code></pre>
37558      * @param {Object} cfg Xtype definition of item to add.
37559      */
37560     addxtype : function(cfg) {
37561         return this.layout.addxtype(cfg);
37562     
37563     }
37564 });        /*
37565  * Based on:
37566  * Ext JS Library 1.1.1
37567  * Copyright(c) 2006-2007, Ext JS, LLC.
37568  *
37569  * Originally Released Under LGPL - original licence link has changed is not relivant.
37570  *
37571  * Fork - LGPL
37572  * <script type="text/javascript">
37573  */
37574 /**
37575  * @class Roo.TabPanel
37576  * @extends Roo.util.Observable
37577  * A lightweight tab container.
37578  * <br><br>
37579  * Usage:
37580  * <pre><code>
37581 // basic tabs 1, built from existing content
37582 var tabs = new Roo.TabPanel("tabs1");
37583 tabs.addTab("script", "View Script");
37584 tabs.addTab("markup", "View Markup");
37585 tabs.activate("script");
37586
37587 // more advanced tabs, built from javascript
37588 var jtabs = new Roo.TabPanel("jtabs");
37589 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37590
37591 // set up the UpdateManager
37592 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37593 var updater = tab2.getUpdateManager();
37594 updater.setDefaultUrl("ajax1.htm");
37595 tab2.on('activate', updater.refresh, updater, true);
37596
37597 // Use setUrl for Ajax loading
37598 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37599 tab3.setUrl("ajax2.htm", null, true);
37600
37601 // Disabled tab
37602 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37603 tab4.disable();
37604
37605 jtabs.activate("jtabs-1");
37606  * </code></pre>
37607  * @constructor
37608  * Create a new TabPanel.
37609  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37610  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37611  */
37612 Roo.bootstrap.panel.Tabs = function(config){
37613     /**
37614     * The container element for this TabPanel.
37615     * @type Roo.Element
37616     */
37617     this.el = Roo.get(config.el);
37618     delete config.el;
37619     if(config){
37620         if(typeof config == "boolean"){
37621             this.tabPosition = config ? "bottom" : "top";
37622         }else{
37623             Roo.apply(this, config);
37624         }
37625     }
37626     
37627     if(this.tabPosition == "bottom"){
37628         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37629         this.el.addClass("roo-tabs-bottom");
37630     }
37631     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37632     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37633     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37634     if(Roo.isIE){
37635         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37636     }
37637     if(this.tabPosition != "bottom"){
37638         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37639          * @type Roo.Element
37640          */
37641         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37642         this.el.addClass("roo-tabs-top");
37643     }
37644     this.items = [];
37645
37646     this.bodyEl.setStyle("position", "relative");
37647
37648     this.active = null;
37649     this.activateDelegate = this.activate.createDelegate(this);
37650
37651     this.addEvents({
37652         /**
37653          * @event tabchange
37654          * Fires when the active tab changes
37655          * @param {Roo.TabPanel} this
37656          * @param {Roo.TabPanelItem} activePanel The new active tab
37657          */
37658         "tabchange": true,
37659         /**
37660          * @event beforetabchange
37661          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37662          * @param {Roo.TabPanel} this
37663          * @param {Object} e Set cancel to true on this object to cancel the tab change
37664          * @param {Roo.TabPanelItem} tab The tab being changed to
37665          */
37666         "beforetabchange" : true
37667     });
37668
37669     Roo.EventManager.onWindowResize(this.onResize, this);
37670     this.cpad = this.el.getPadding("lr");
37671     this.hiddenCount = 0;
37672
37673
37674     // toolbar on the tabbar support...
37675     if (this.toolbar) {
37676         alert("no toolbar support yet");
37677         this.toolbar  = false;
37678         /*
37679         var tcfg = this.toolbar;
37680         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37681         this.toolbar = new Roo.Toolbar(tcfg);
37682         if (Roo.isSafari) {
37683             var tbl = tcfg.container.child('table', true);
37684             tbl.setAttribute('width', '100%');
37685         }
37686         */
37687         
37688     }
37689    
37690
37691
37692     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37693 };
37694
37695 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37696     /*
37697      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37698      */
37699     tabPosition : "top",
37700     /*
37701      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37702      */
37703     currentTabWidth : 0,
37704     /*
37705      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37706      */
37707     minTabWidth : 40,
37708     /*
37709      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37710      */
37711     maxTabWidth : 250,
37712     /*
37713      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37714      */
37715     preferredTabWidth : 175,
37716     /*
37717      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37718      */
37719     resizeTabs : false,
37720     /*
37721      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37722      */
37723     monitorResize : true,
37724     /*
37725      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37726      */
37727     toolbar : false,
37728
37729     /**
37730      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37731      * @param {String} id The id of the div to use <b>or create</b>
37732      * @param {String} text The text for the tab
37733      * @param {String} content (optional) Content to put in the TabPanelItem body
37734      * @param {Boolean} closable (optional) True to create a close icon on the tab
37735      * @return {Roo.TabPanelItem} The created TabPanelItem
37736      */
37737     addTab : function(id, text, content, closable, tpl)
37738     {
37739         var item = new Roo.bootstrap.panel.TabItem({
37740             panel: this,
37741             id : id,
37742             text : text,
37743             closable : closable,
37744             tpl : tpl
37745         });
37746         this.addTabItem(item);
37747         if(content){
37748             item.setContent(content);
37749         }
37750         return item;
37751     },
37752
37753     /**
37754      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37755      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37756      * @return {Roo.TabPanelItem}
37757      */
37758     getTab : function(id){
37759         return this.items[id];
37760     },
37761
37762     /**
37763      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37764      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37765      */
37766     hideTab : function(id){
37767         var t = this.items[id];
37768         if(!t.isHidden()){
37769            t.setHidden(true);
37770            this.hiddenCount++;
37771            this.autoSizeTabs();
37772         }
37773     },
37774
37775     /**
37776      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37777      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37778      */
37779     unhideTab : function(id){
37780         var t = this.items[id];
37781         if(t.isHidden()){
37782            t.setHidden(false);
37783            this.hiddenCount--;
37784            this.autoSizeTabs();
37785         }
37786     },
37787
37788     /**
37789      * Adds an existing {@link Roo.TabPanelItem}.
37790      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37791      */
37792     addTabItem : function(item){
37793         this.items[item.id] = item;
37794         this.items.push(item);
37795       //  if(this.resizeTabs){
37796     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37797   //         this.autoSizeTabs();
37798 //        }else{
37799 //            item.autoSize();
37800        // }
37801     },
37802
37803     /**
37804      * Removes a {@link Roo.TabPanelItem}.
37805      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37806      */
37807     removeTab : function(id){
37808         var items = this.items;
37809         var tab = items[id];
37810         if(!tab) { return; }
37811         var index = items.indexOf(tab);
37812         if(this.active == tab && items.length > 1){
37813             var newTab = this.getNextAvailable(index);
37814             if(newTab) {
37815                 newTab.activate();
37816             }
37817         }
37818         this.stripEl.dom.removeChild(tab.pnode.dom);
37819         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37820             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37821         }
37822         items.splice(index, 1);
37823         delete this.items[tab.id];
37824         tab.fireEvent("close", tab);
37825         tab.purgeListeners();
37826         this.autoSizeTabs();
37827     },
37828
37829     getNextAvailable : function(start){
37830         var items = this.items;
37831         var index = start;
37832         // look for a next tab that will slide over to
37833         // replace the one being removed
37834         while(index < items.length){
37835             var item = items[++index];
37836             if(item && !item.isHidden()){
37837                 return item;
37838             }
37839         }
37840         // if one isn't found select the previous tab (on the left)
37841         index = start;
37842         while(index >= 0){
37843             var item = items[--index];
37844             if(item && !item.isHidden()){
37845                 return item;
37846             }
37847         }
37848         return null;
37849     },
37850
37851     /**
37852      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37853      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37854      */
37855     disableTab : function(id){
37856         var tab = this.items[id];
37857         if(tab && this.active != tab){
37858             tab.disable();
37859         }
37860     },
37861
37862     /**
37863      * Enables a {@link Roo.TabPanelItem} that is disabled.
37864      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37865      */
37866     enableTab : function(id){
37867         var tab = this.items[id];
37868         tab.enable();
37869     },
37870
37871     /**
37872      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37873      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37874      * @return {Roo.TabPanelItem} The TabPanelItem.
37875      */
37876     activate : function(id){
37877         var tab = this.items[id];
37878         if(!tab){
37879             return null;
37880         }
37881         if(tab == this.active || tab.disabled){
37882             return tab;
37883         }
37884         var e = {};
37885         this.fireEvent("beforetabchange", this, e, tab);
37886         if(e.cancel !== true && !tab.disabled){
37887             if(this.active){
37888                 this.active.hide();
37889             }
37890             this.active = this.items[id];
37891             this.active.show();
37892             this.fireEvent("tabchange", this, this.active);
37893         }
37894         return tab;
37895     },
37896
37897     /**
37898      * Gets the active {@link Roo.TabPanelItem}.
37899      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37900      */
37901     getActiveTab : function(){
37902         return this.active;
37903     },
37904
37905     /**
37906      * Updates the tab body element to fit the height of the container element
37907      * for overflow scrolling
37908      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37909      */
37910     syncHeight : function(targetHeight){
37911         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37912         var bm = this.bodyEl.getMargins();
37913         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37914         this.bodyEl.setHeight(newHeight);
37915         return newHeight;
37916     },
37917
37918     onResize : function(){
37919         if(this.monitorResize){
37920             this.autoSizeTabs();
37921         }
37922     },
37923
37924     /**
37925      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37926      */
37927     beginUpdate : function(){
37928         this.updating = true;
37929     },
37930
37931     /**
37932      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37933      */
37934     endUpdate : function(){
37935         this.updating = false;
37936         this.autoSizeTabs();
37937     },
37938
37939     /**
37940      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37941      */
37942     autoSizeTabs : function(){
37943         var count = this.items.length;
37944         var vcount = count - this.hiddenCount;
37945         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37946             return;
37947         }
37948         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37949         var availWidth = Math.floor(w / vcount);
37950         var b = this.stripBody;
37951         if(b.getWidth() > w){
37952             var tabs = this.items;
37953             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37954             if(availWidth < this.minTabWidth){
37955                 /*if(!this.sleft){    // incomplete scrolling code
37956                     this.createScrollButtons();
37957                 }
37958                 this.showScroll();
37959                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37960             }
37961         }else{
37962             if(this.currentTabWidth < this.preferredTabWidth){
37963                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37964             }
37965         }
37966     },
37967
37968     /**
37969      * Returns the number of tabs in this TabPanel.
37970      * @return {Number}
37971      */
37972      getCount : function(){
37973          return this.items.length;
37974      },
37975
37976     /**
37977      * Resizes all the tabs to the passed width
37978      * @param {Number} The new width
37979      */
37980     setTabWidth : function(width){
37981         this.currentTabWidth = width;
37982         for(var i = 0, len = this.items.length; i < len; i++) {
37983                 if(!this.items[i].isHidden()) {
37984                 this.items[i].setWidth(width);
37985             }
37986         }
37987     },
37988
37989     /**
37990      * Destroys this TabPanel
37991      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37992      */
37993     destroy : function(removeEl){
37994         Roo.EventManager.removeResizeListener(this.onResize, this);
37995         for(var i = 0, len = this.items.length; i < len; i++){
37996             this.items[i].purgeListeners();
37997         }
37998         if(removeEl === true){
37999             this.el.update("");
38000             this.el.remove();
38001         }
38002     },
38003     
38004     createStrip : function(container)
38005     {
38006         var strip = document.createElement("nav");
38007         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38008         container.appendChild(strip);
38009         return strip;
38010     },
38011     
38012     createStripList : function(strip)
38013     {
38014         // div wrapper for retard IE
38015         // returns the "tr" element.
38016         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38017         //'<div class="x-tabs-strip-wrap">'+
38018           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38019           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38020         return strip.firstChild; //.firstChild.firstChild.firstChild;
38021     },
38022     createBody : function(container)
38023     {
38024         var body = document.createElement("div");
38025         Roo.id(body, "tab-body");
38026         //Roo.fly(body).addClass("x-tabs-body");
38027         Roo.fly(body).addClass("tab-content");
38028         container.appendChild(body);
38029         return body;
38030     },
38031     createItemBody :function(bodyEl, id){
38032         var body = Roo.getDom(id);
38033         if(!body){
38034             body = document.createElement("div");
38035             body.id = id;
38036         }
38037         //Roo.fly(body).addClass("x-tabs-item-body");
38038         Roo.fly(body).addClass("tab-pane");
38039          bodyEl.insertBefore(body, bodyEl.firstChild);
38040         return body;
38041     },
38042     /** @private */
38043     createStripElements :  function(stripEl, text, closable, tpl)
38044     {
38045         var td = document.createElement("li"); // was td..
38046         
38047         
38048         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38049         
38050         
38051         stripEl.appendChild(td);
38052         /*if(closable){
38053             td.className = "x-tabs-closable";
38054             if(!this.closeTpl){
38055                 this.closeTpl = new Roo.Template(
38056                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38057                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38058                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38059                 );
38060             }
38061             var el = this.closeTpl.overwrite(td, {"text": text});
38062             var close = el.getElementsByTagName("div")[0];
38063             var inner = el.getElementsByTagName("em")[0];
38064             return {"el": el, "close": close, "inner": inner};
38065         } else {
38066         */
38067         // not sure what this is..
38068 //            if(!this.tabTpl){
38069                 //this.tabTpl = new Roo.Template(
38070                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38071                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38072                 //);
38073 //                this.tabTpl = new Roo.Template(
38074 //                   '<a href="#">' +
38075 //                   '<span unselectable="on"' +
38076 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38077 //                            ' >{text}</span></a>'
38078 //                );
38079 //                
38080 //            }
38081
38082
38083             var template = tpl || this.tabTpl || false;
38084             
38085             if(!template){
38086                 
38087                 template = new Roo.Template(
38088                    '<a href="#">' +
38089                    '<span unselectable="on"' +
38090                             (this.disableTooltips ? '' : ' title="{text}"') +
38091                             ' >{text}</span></a>'
38092                 );
38093             }
38094             
38095             switch (typeof(template)) {
38096                 case 'object' :
38097                     break;
38098                 case 'string' :
38099                     template = new Roo.Template(template);
38100                     break;
38101                 default :
38102                     break;
38103             }
38104             
38105             var el = template.overwrite(td, {"text": text});
38106             
38107             var inner = el.getElementsByTagName("span")[0];
38108             
38109             return {"el": el, "inner": inner};
38110             
38111     }
38112         
38113     
38114 });
38115
38116 /**
38117  * @class Roo.TabPanelItem
38118  * @extends Roo.util.Observable
38119  * Represents an individual item (tab plus body) in a TabPanel.
38120  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38121  * @param {String} id The id of this TabPanelItem
38122  * @param {String} text The text for the tab of this TabPanelItem
38123  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38124  */
38125 Roo.bootstrap.panel.TabItem = function(config){
38126     /**
38127      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38128      * @type Roo.TabPanel
38129      */
38130     this.tabPanel = config.panel;
38131     /**
38132      * The id for this TabPanelItem
38133      * @type String
38134      */
38135     this.id = config.id;
38136     /** @private */
38137     this.disabled = false;
38138     /** @private */
38139     this.text = config.text;
38140     /** @private */
38141     this.loaded = false;
38142     this.closable = config.closable;
38143
38144     /**
38145      * The body element for this TabPanelItem.
38146      * @type Roo.Element
38147      */
38148     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38149     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38150     this.bodyEl.setStyle("display", "block");
38151     this.bodyEl.setStyle("zoom", "1");
38152     //this.hideAction();
38153
38154     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38155     /** @private */
38156     this.el = Roo.get(els.el);
38157     this.inner = Roo.get(els.inner, true);
38158     this.textEl = Roo.get(this.el.dom.firstChild, true);
38159     this.pnode = Roo.get(els.el.parentNode, true);
38160 //    this.el.on("mousedown", this.onTabMouseDown, this);
38161     this.el.on("click", this.onTabClick, this);
38162     /** @private */
38163     if(config.closable){
38164         var c = Roo.get(els.close, true);
38165         c.dom.title = this.closeText;
38166         c.addClassOnOver("close-over");
38167         c.on("click", this.closeClick, this);
38168      }
38169
38170     this.addEvents({
38171          /**
38172          * @event activate
38173          * Fires when this tab becomes the active tab.
38174          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38175          * @param {Roo.TabPanelItem} this
38176          */
38177         "activate": true,
38178         /**
38179          * @event beforeclose
38180          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38181          * @param {Roo.TabPanelItem} this
38182          * @param {Object} e Set cancel to true on this object to cancel the close.
38183          */
38184         "beforeclose": true,
38185         /**
38186          * @event close
38187          * Fires when this tab is closed.
38188          * @param {Roo.TabPanelItem} this
38189          */
38190          "close": true,
38191         /**
38192          * @event deactivate
38193          * Fires when this tab is no longer the active tab.
38194          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38195          * @param {Roo.TabPanelItem} this
38196          */
38197          "deactivate" : true
38198     });
38199     this.hidden = false;
38200
38201     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38202 };
38203
38204 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38205            {
38206     purgeListeners : function(){
38207        Roo.util.Observable.prototype.purgeListeners.call(this);
38208        this.el.removeAllListeners();
38209     },
38210     /**
38211      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38212      */
38213     show : function(){
38214         this.pnode.addClass("active");
38215         this.showAction();
38216         if(Roo.isOpera){
38217             this.tabPanel.stripWrap.repaint();
38218         }
38219         this.fireEvent("activate", this.tabPanel, this);
38220     },
38221
38222     /**
38223      * Returns true if this tab is the active tab.
38224      * @return {Boolean}
38225      */
38226     isActive : function(){
38227         return this.tabPanel.getActiveTab() == this;
38228     },
38229
38230     /**
38231      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38232      */
38233     hide : function(){
38234         this.pnode.removeClass("active");
38235         this.hideAction();
38236         this.fireEvent("deactivate", this.tabPanel, this);
38237     },
38238
38239     hideAction : function(){
38240         this.bodyEl.hide();
38241         this.bodyEl.setStyle("position", "absolute");
38242         this.bodyEl.setLeft("-20000px");
38243         this.bodyEl.setTop("-20000px");
38244     },
38245
38246     showAction : function(){
38247         this.bodyEl.setStyle("position", "relative");
38248         this.bodyEl.setTop("");
38249         this.bodyEl.setLeft("");
38250         this.bodyEl.show();
38251     },
38252
38253     /**
38254      * Set the tooltip for the tab.
38255      * @param {String} tooltip The tab's tooltip
38256      */
38257     setTooltip : function(text){
38258         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38259             this.textEl.dom.qtip = text;
38260             this.textEl.dom.removeAttribute('title');
38261         }else{
38262             this.textEl.dom.title = text;
38263         }
38264     },
38265
38266     onTabClick : function(e){
38267         e.preventDefault();
38268         this.tabPanel.activate(this.id);
38269     },
38270
38271     onTabMouseDown : function(e){
38272         e.preventDefault();
38273         this.tabPanel.activate(this.id);
38274     },
38275 /*
38276     getWidth : function(){
38277         return this.inner.getWidth();
38278     },
38279
38280     setWidth : function(width){
38281         var iwidth = width - this.pnode.getPadding("lr");
38282         this.inner.setWidth(iwidth);
38283         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38284         this.pnode.setWidth(width);
38285     },
38286 */
38287     /**
38288      * Show or hide the tab
38289      * @param {Boolean} hidden True to hide or false to show.
38290      */
38291     setHidden : function(hidden){
38292         this.hidden = hidden;
38293         this.pnode.setStyle("display", hidden ? "none" : "");
38294     },
38295
38296     /**
38297      * Returns true if this tab is "hidden"
38298      * @return {Boolean}
38299      */
38300     isHidden : function(){
38301         return this.hidden;
38302     },
38303
38304     /**
38305      * Returns the text for this tab
38306      * @return {String}
38307      */
38308     getText : function(){
38309         return this.text;
38310     },
38311     /*
38312     autoSize : function(){
38313         //this.el.beginMeasure();
38314         this.textEl.setWidth(1);
38315         /*
38316          *  #2804 [new] Tabs in Roojs
38317          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38318          */
38319         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38320         //this.el.endMeasure();
38321     //},
38322
38323     /**
38324      * Sets the text for the tab (Note: this also sets the tooltip text)
38325      * @param {String} text The tab's text and tooltip
38326      */
38327     setText : function(text){
38328         this.text = text;
38329         this.textEl.update(text);
38330         this.setTooltip(text);
38331         //if(!this.tabPanel.resizeTabs){
38332         //    this.autoSize();
38333         //}
38334     },
38335     /**
38336      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38337      */
38338     activate : function(){
38339         this.tabPanel.activate(this.id);
38340     },
38341
38342     /**
38343      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38344      */
38345     disable : function(){
38346         if(this.tabPanel.active != this){
38347             this.disabled = true;
38348             this.pnode.addClass("disabled");
38349         }
38350     },
38351
38352     /**
38353      * Enables this TabPanelItem if it was previously disabled.
38354      */
38355     enable : function(){
38356         this.disabled = false;
38357         this.pnode.removeClass("disabled");
38358     },
38359
38360     /**
38361      * Sets the content for this TabPanelItem.
38362      * @param {String} content The content
38363      * @param {Boolean} loadScripts true to look for and load scripts
38364      */
38365     setContent : function(content, loadScripts){
38366         this.bodyEl.update(content, loadScripts);
38367     },
38368
38369     /**
38370      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38371      * @return {Roo.UpdateManager} The UpdateManager
38372      */
38373     getUpdateManager : function(){
38374         return this.bodyEl.getUpdateManager();
38375     },
38376
38377     /**
38378      * Set a URL to be used to load the content for this TabPanelItem.
38379      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38380      * @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)
38381      * @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)
38382      * @return {Roo.UpdateManager} The UpdateManager
38383      */
38384     setUrl : function(url, params, loadOnce){
38385         if(this.refreshDelegate){
38386             this.un('activate', this.refreshDelegate);
38387         }
38388         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38389         this.on("activate", this.refreshDelegate);
38390         return this.bodyEl.getUpdateManager();
38391     },
38392
38393     /** @private */
38394     _handleRefresh : function(url, params, loadOnce){
38395         if(!loadOnce || !this.loaded){
38396             var updater = this.bodyEl.getUpdateManager();
38397             updater.update(url, params, this._setLoaded.createDelegate(this));
38398         }
38399     },
38400
38401     /**
38402      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38403      *   Will fail silently if the setUrl method has not been called.
38404      *   This does not activate the panel, just updates its content.
38405      */
38406     refresh : function(){
38407         if(this.refreshDelegate){
38408            this.loaded = false;
38409            this.refreshDelegate();
38410         }
38411     },
38412
38413     /** @private */
38414     _setLoaded : function(){
38415         this.loaded = true;
38416     },
38417
38418     /** @private */
38419     closeClick : function(e){
38420         var o = {};
38421         e.stopEvent();
38422         this.fireEvent("beforeclose", this, o);
38423         if(o.cancel !== true){
38424             this.tabPanel.removeTab(this.id);
38425         }
38426     },
38427     /**
38428      * The text displayed in the tooltip for the close icon.
38429      * @type String
38430      */
38431     closeText : "Close this tab"
38432 });
38433 /**
38434 *    This script refer to:
38435 *    Title: International Telephone Input
38436 *    Author: Jack O'Connor
38437 *    Code version:  v12.1.12
38438 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38439 **/
38440
38441 Roo.bootstrap.PhoneInputData = function() {
38442     var d = [
38443       [
38444         "Afghanistan (‫افغانستان‬‎)",
38445         "af",
38446         "93"
38447       ],
38448       [
38449         "Albania (Shqipëri)",
38450         "al",
38451         "355"
38452       ],
38453       [
38454         "Algeria (‫الجزائر‬‎)",
38455         "dz",
38456         "213"
38457       ],
38458       [
38459         "American Samoa",
38460         "as",
38461         "1684"
38462       ],
38463       [
38464         "Andorra",
38465         "ad",
38466         "376"
38467       ],
38468       [
38469         "Angola",
38470         "ao",
38471         "244"
38472       ],
38473       [
38474         "Anguilla",
38475         "ai",
38476         "1264"
38477       ],
38478       [
38479         "Antigua and Barbuda",
38480         "ag",
38481         "1268"
38482       ],
38483       [
38484         "Argentina",
38485         "ar",
38486         "54"
38487       ],
38488       [
38489         "Armenia (Հայաստան)",
38490         "am",
38491         "374"
38492       ],
38493       [
38494         "Aruba",
38495         "aw",
38496         "297"
38497       ],
38498       [
38499         "Australia",
38500         "au",
38501         "61",
38502         0
38503       ],
38504       [
38505         "Austria (Österreich)",
38506         "at",
38507         "43"
38508       ],
38509       [
38510         "Azerbaijan (Azərbaycan)",
38511         "az",
38512         "994"
38513       ],
38514       [
38515         "Bahamas",
38516         "bs",
38517         "1242"
38518       ],
38519       [
38520         "Bahrain (‫البحرين‬‎)",
38521         "bh",
38522         "973"
38523       ],
38524       [
38525         "Bangladesh (বাংলাদেশ)",
38526         "bd",
38527         "880"
38528       ],
38529       [
38530         "Barbados",
38531         "bb",
38532         "1246"
38533       ],
38534       [
38535         "Belarus (Беларусь)",
38536         "by",
38537         "375"
38538       ],
38539       [
38540         "Belgium (België)",
38541         "be",
38542         "32"
38543       ],
38544       [
38545         "Belize",
38546         "bz",
38547         "501"
38548       ],
38549       [
38550         "Benin (Bénin)",
38551         "bj",
38552         "229"
38553       ],
38554       [
38555         "Bermuda",
38556         "bm",
38557         "1441"
38558       ],
38559       [
38560         "Bhutan (འབྲུག)",
38561         "bt",
38562         "975"
38563       ],
38564       [
38565         "Bolivia",
38566         "bo",
38567         "591"
38568       ],
38569       [
38570         "Bosnia and Herzegovina (Босна и Херцеговина)",
38571         "ba",
38572         "387"
38573       ],
38574       [
38575         "Botswana",
38576         "bw",
38577         "267"
38578       ],
38579       [
38580         "Brazil (Brasil)",
38581         "br",
38582         "55"
38583       ],
38584       [
38585         "British Indian Ocean Territory",
38586         "io",
38587         "246"
38588       ],
38589       [
38590         "British Virgin Islands",
38591         "vg",
38592         "1284"
38593       ],
38594       [
38595         "Brunei",
38596         "bn",
38597         "673"
38598       ],
38599       [
38600         "Bulgaria (България)",
38601         "bg",
38602         "359"
38603       ],
38604       [
38605         "Burkina Faso",
38606         "bf",
38607         "226"
38608       ],
38609       [
38610         "Burundi (Uburundi)",
38611         "bi",
38612         "257"
38613       ],
38614       [
38615         "Cambodia (កម្ពុជា)",
38616         "kh",
38617         "855"
38618       ],
38619       [
38620         "Cameroon (Cameroun)",
38621         "cm",
38622         "237"
38623       ],
38624       [
38625         "Canada",
38626         "ca",
38627         "1",
38628         1,
38629         ["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"]
38630       ],
38631       [
38632         "Cape Verde (Kabu Verdi)",
38633         "cv",
38634         "238"
38635       ],
38636       [
38637         "Caribbean Netherlands",
38638         "bq",
38639         "599",
38640         1
38641       ],
38642       [
38643         "Cayman Islands",
38644         "ky",
38645         "1345"
38646       ],
38647       [
38648         "Central African Republic (République centrafricaine)",
38649         "cf",
38650         "236"
38651       ],
38652       [
38653         "Chad (Tchad)",
38654         "td",
38655         "235"
38656       ],
38657       [
38658         "Chile",
38659         "cl",
38660         "56"
38661       ],
38662       [
38663         "China (中国)",
38664         "cn",
38665         "86"
38666       ],
38667       [
38668         "Christmas Island",
38669         "cx",
38670         "61",
38671         2
38672       ],
38673       [
38674         "Cocos (Keeling) Islands",
38675         "cc",
38676         "61",
38677         1
38678       ],
38679       [
38680         "Colombia",
38681         "co",
38682         "57"
38683       ],
38684       [
38685         "Comoros (‫جزر القمر‬‎)",
38686         "km",
38687         "269"
38688       ],
38689       [
38690         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38691         "cd",
38692         "243"
38693       ],
38694       [
38695         "Congo (Republic) (Congo-Brazzaville)",
38696         "cg",
38697         "242"
38698       ],
38699       [
38700         "Cook Islands",
38701         "ck",
38702         "682"
38703       ],
38704       [
38705         "Costa Rica",
38706         "cr",
38707         "506"
38708       ],
38709       [
38710         "Côte d’Ivoire",
38711         "ci",
38712         "225"
38713       ],
38714       [
38715         "Croatia (Hrvatska)",
38716         "hr",
38717         "385"
38718       ],
38719       [
38720         "Cuba",
38721         "cu",
38722         "53"
38723       ],
38724       [
38725         "Curaçao",
38726         "cw",
38727         "599",
38728         0
38729       ],
38730       [
38731         "Cyprus (Κύπρος)",
38732         "cy",
38733         "357"
38734       ],
38735       [
38736         "Czech Republic (Česká republika)",
38737         "cz",
38738         "420"
38739       ],
38740       [
38741         "Denmark (Danmark)",
38742         "dk",
38743         "45"
38744       ],
38745       [
38746         "Djibouti",
38747         "dj",
38748         "253"
38749       ],
38750       [
38751         "Dominica",
38752         "dm",
38753         "1767"
38754       ],
38755       [
38756         "Dominican Republic (República Dominicana)",
38757         "do",
38758         "1",
38759         2,
38760         ["809", "829", "849"]
38761       ],
38762       [
38763         "Ecuador",
38764         "ec",
38765         "593"
38766       ],
38767       [
38768         "Egypt (‫مصر‬‎)",
38769         "eg",
38770         "20"
38771       ],
38772       [
38773         "El Salvador",
38774         "sv",
38775         "503"
38776       ],
38777       [
38778         "Equatorial Guinea (Guinea Ecuatorial)",
38779         "gq",
38780         "240"
38781       ],
38782       [
38783         "Eritrea",
38784         "er",
38785         "291"
38786       ],
38787       [
38788         "Estonia (Eesti)",
38789         "ee",
38790         "372"
38791       ],
38792       [
38793         "Ethiopia",
38794         "et",
38795         "251"
38796       ],
38797       [
38798         "Falkland Islands (Islas Malvinas)",
38799         "fk",
38800         "500"
38801       ],
38802       [
38803         "Faroe Islands (Føroyar)",
38804         "fo",
38805         "298"
38806       ],
38807       [
38808         "Fiji",
38809         "fj",
38810         "679"
38811       ],
38812       [
38813         "Finland (Suomi)",
38814         "fi",
38815         "358",
38816         0
38817       ],
38818       [
38819         "France",
38820         "fr",
38821         "33"
38822       ],
38823       [
38824         "French Guiana (Guyane française)",
38825         "gf",
38826         "594"
38827       ],
38828       [
38829         "French Polynesia (Polynésie française)",
38830         "pf",
38831         "689"
38832       ],
38833       [
38834         "Gabon",
38835         "ga",
38836         "241"
38837       ],
38838       [
38839         "Gambia",
38840         "gm",
38841         "220"
38842       ],
38843       [
38844         "Georgia (საქართველო)",
38845         "ge",
38846         "995"
38847       ],
38848       [
38849         "Germany (Deutschland)",
38850         "de",
38851         "49"
38852       ],
38853       [
38854         "Ghana (Gaana)",
38855         "gh",
38856         "233"
38857       ],
38858       [
38859         "Gibraltar",
38860         "gi",
38861         "350"
38862       ],
38863       [
38864         "Greece (Ελλάδα)",
38865         "gr",
38866         "30"
38867       ],
38868       [
38869         "Greenland (Kalaallit Nunaat)",
38870         "gl",
38871         "299"
38872       ],
38873       [
38874         "Grenada",
38875         "gd",
38876         "1473"
38877       ],
38878       [
38879         "Guadeloupe",
38880         "gp",
38881         "590",
38882         0
38883       ],
38884       [
38885         "Guam",
38886         "gu",
38887         "1671"
38888       ],
38889       [
38890         "Guatemala",
38891         "gt",
38892         "502"
38893       ],
38894       [
38895         "Guernsey",
38896         "gg",
38897         "44",
38898         1
38899       ],
38900       [
38901         "Guinea (Guinée)",
38902         "gn",
38903         "224"
38904       ],
38905       [
38906         "Guinea-Bissau (Guiné Bissau)",
38907         "gw",
38908         "245"
38909       ],
38910       [
38911         "Guyana",
38912         "gy",
38913         "592"
38914       ],
38915       [
38916         "Haiti",
38917         "ht",
38918         "509"
38919       ],
38920       [
38921         "Honduras",
38922         "hn",
38923         "504"
38924       ],
38925       [
38926         "Hong Kong (香港)",
38927         "hk",
38928         "852"
38929       ],
38930       [
38931         "Hungary (Magyarország)",
38932         "hu",
38933         "36"
38934       ],
38935       [
38936         "Iceland (Ísland)",
38937         "is",
38938         "354"
38939       ],
38940       [
38941         "India (भारत)",
38942         "in",
38943         "91"
38944       ],
38945       [
38946         "Indonesia",
38947         "id",
38948         "62"
38949       ],
38950       [
38951         "Iran (‫ایران‬‎)",
38952         "ir",
38953         "98"
38954       ],
38955       [
38956         "Iraq (‫العراق‬‎)",
38957         "iq",
38958         "964"
38959       ],
38960       [
38961         "Ireland",
38962         "ie",
38963         "353"
38964       ],
38965       [
38966         "Isle of Man",
38967         "im",
38968         "44",
38969         2
38970       ],
38971       [
38972         "Israel (‫ישראל‬‎)",
38973         "il",
38974         "972"
38975       ],
38976       [
38977         "Italy (Italia)",
38978         "it",
38979         "39",
38980         0
38981       ],
38982       [
38983         "Jamaica",
38984         "jm",
38985         "1876"
38986       ],
38987       [
38988         "Japan (日本)",
38989         "jp",
38990         "81"
38991       ],
38992       [
38993         "Jersey",
38994         "je",
38995         "44",
38996         3
38997       ],
38998       [
38999         "Jordan (‫الأردن‬‎)",
39000         "jo",
39001         "962"
39002       ],
39003       [
39004         "Kazakhstan (Казахстан)",
39005         "kz",
39006         "7",
39007         1
39008       ],
39009       [
39010         "Kenya",
39011         "ke",
39012         "254"
39013       ],
39014       [
39015         "Kiribati",
39016         "ki",
39017         "686"
39018       ],
39019       [
39020         "Kosovo",
39021         "xk",
39022         "383"
39023       ],
39024       [
39025         "Kuwait (‫الكويت‬‎)",
39026         "kw",
39027         "965"
39028       ],
39029       [
39030         "Kyrgyzstan (Кыргызстан)",
39031         "kg",
39032         "996"
39033       ],
39034       [
39035         "Laos (ລາວ)",
39036         "la",
39037         "856"
39038       ],
39039       [
39040         "Latvia (Latvija)",
39041         "lv",
39042         "371"
39043       ],
39044       [
39045         "Lebanon (‫لبنان‬‎)",
39046         "lb",
39047         "961"
39048       ],
39049       [
39050         "Lesotho",
39051         "ls",
39052         "266"
39053       ],
39054       [
39055         "Liberia",
39056         "lr",
39057         "231"
39058       ],
39059       [
39060         "Libya (‫ليبيا‬‎)",
39061         "ly",
39062         "218"
39063       ],
39064       [
39065         "Liechtenstein",
39066         "li",
39067         "423"
39068       ],
39069       [
39070         "Lithuania (Lietuva)",
39071         "lt",
39072         "370"
39073       ],
39074       [
39075         "Luxembourg",
39076         "lu",
39077         "352"
39078       ],
39079       [
39080         "Macau (澳門)",
39081         "mo",
39082         "853"
39083       ],
39084       [
39085         "Macedonia (FYROM) (Македонија)",
39086         "mk",
39087         "389"
39088       ],
39089       [
39090         "Madagascar (Madagasikara)",
39091         "mg",
39092         "261"
39093       ],
39094       [
39095         "Malawi",
39096         "mw",
39097         "265"
39098       ],
39099       [
39100         "Malaysia",
39101         "my",
39102         "60"
39103       ],
39104       [
39105         "Maldives",
39106         "mv",
39107         "960"
39108       ],
39109       [
39110         "Mali",
39111         "ml",
39112         "223"
39113       ],
39114       [
39115         "Malta",
39116         "mt",
39117         "356"
39118       ],
39119       [
39120         "Marshall Islands",
39121         "mh",
39122         "692"
39123       ],
39124       [
39125         "Martinique",
39126         "mq",
39127         "596"
39128       ],
39129       [
39130         "Mauritania (‫موريتانيا‬‎)",
39131         "mr",
39132         "222"
39133       ],
39134       [
39135         "Mauritius (Moris)",
39136         "mu",
39137         "230"
39138       ],
39139       [
39140         "Mayotte",
39141         "yt",
39142         "262",
39143         1
39144       ],
39145       [
39146         "Mexico (México)",
39147         "mx",
39148         "52"
39149       ],
39150       [
39151         "Micronesia",
39152         "fm",
39153         "691"
39154       ],
39155       [
39156         "Moldova (Republica Moldova)",
39157         "md",
39158         "373"
39159       ],
39160       [
39161         "Monaco",
39162         "mc",
39163         "377"
39164       ],
39165       [
39166         "Mongolia (Монгол)",
39167         "mn",
39168         "976"
39169       ],
39170       [
39171         "Montenegro (Crna Gora)",
39172         "me",
39173         "382"
39174       ],
39175       [
39176         "Montserrat",
39177         "ms",
39178         "1664"
39179       ],
39180       [
39181         "Morocco (‫المغرب‬‎)",
39182         "ma",
39183         "212",
39184         0
39185       ],
39186       [
39187         "Mozambique (Moçambique)",
39188         "mz",
39189         "258"
39190       ],
39191       [
39192         "Myanmar (Burma) (မြန်မာ)",
39193         "mm",
39194         "95"
39195       ],
39196       [
39197         "Namibia (Namibië)",
39198         "na",
39199         "264"
39200       ],
39201       [
39202         "Nauru",
39203         "nr",
39204         "674"
39205       ],
39206       [
39207         "Nepal (नेपाल)",
39208         "np",
39209         "977"
39210       ],
39211       [
39212         "Netherlands (Nederland)",
39213         "nl",
39214         "31"
39215       ],
39216       [
39217         "New Caledonia (Nouvelle-Calédonie)",
39218         "nc",
39219         "687"
39220       ],
39221       [
39222         "New Zealand",
39223         "nz",
39224         "64"
39225       ],
39226       [
39227         "Nicaragua",
39228         "ni",
39229         "505"
39230       ],
39231       [
39232         "Niger (Nijar)",
39233         "ne",
39234         "227"
39235       ],
39236       [
39237         "Nigeria",
39238         "ng",
39239         "234"
39240       ],
39241       [
39242         "Niue",
39243         "nu",
39244         "683"
39245       ],
39246       [
39247         "Norfolk Island",
39248         "nf",
39249         "672"
39250       ],
39251       [
39252         "North Korea (조선 민주주의 인민 공화국)",
39253         "kp",
39254         "850"
39255       ],
39256       [
39257         "Northern Mariana Islands",
39258         "mp",
39259         "1670"
39260       ],
39261       [
39262         "Norway (Norge)",
39263         "no",
39264         "47",
39265         0
39266       ],
39267       [
39268         "Oman (‫عُمان‬‎)",
39269         "om",
39270         "968"
39271       ],
39272       [
39273         "Pakistan (‫پاکستان‬‎)",
39274         "pk",
39275         "92"
39276       ],
39277       [
39278         "Palau",
39279         "pw",
39280         "680"
39281       ],
39282       [
39283         "Palestine (‫فلسطين‬‎)",
39284         "ps",
39285         "970"
39286       ],
39287       [
39288         "Panama (Panamá)",
39289         "pa",
39290         "507"
39291       ],
39292       [
39293         "Papua New Guinea",
39294         "pg",
39295         "675"
39296       ],
39297       [
39298         "Paraguay",
39299         "py",
39300         "595"
39301       ],
39302       [
39303         "Peru (Perú)",
39304         "pe",
39305         "51"
39306       ],
39307       [
39308         "Philippines",
39309         "ph",
39310         "63"
39311       ],
39312       [
39313         "Poland (Polska)",
39314         "pl",
39315         "48"
39316       ],
39317       [
39318         "Portugal",
39319         "pt",
39320         "351"
39321       ],
39322       [
39323         "Puerto Rico",
39324         "pr",
39325         "1",
39326         3,
39327         ["787", "939"]
39328       ],
39329       [
39330         "Qatar (‫قطر‬‎)",
39331         "qa",
39332         "974"
39333       ],
39334       [
39335         "Réunion (La Réunion)",
39336         "re",
39337         "262",
39338         0
39339       ],
39340       [
39341         "Romania (România)",
39342         "ro",
39343         "40"
39344       ],
39345       [
39346         "Russia (Россия)",
39347         "ru",
39348         "7",
39349         0
39350       ],
39351       [
39352         "Rwanda",
39353         "rw",
39354         "250"
39355       ],
39356       [
39357         "Saint Barthélemy",
39358         "bl",
39359         "590",
39360         1
39361       ],
39362       [
39363         "Saint Helena",
39364         "sh",
39365         "290"
39366       ],
39367       [
39368         "Saint Kitts and Nevis",
39369         "kn",
39370         "1869"
39371       ],
39372       [
39373         "Saint Lucia",
39374         "lc",
39375         "1758"
39376       ],
39377       [
39378         "Saint Martin (Saint-Martin (partie française))",
39379         "mf",
39380         "590",
39381         2
39382       ],
39383       [
39384         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39385         "pm",
39386         "508"
39387       ],
39388       [
39389         "Saint Vincent and the Grenadines",
39390         "vc",
39391         "1784"
39392       ],
39393       [
39394         "Samoa",
39395         "ws",
39396         "685"
39397       ],
39398       [
39399         "San Marino",
39400         "sm",
39401         "378"
39402       ],
39403       [
39404         "São Tomé and Príncipe (São Tomé e Príncipe)",
39405         "st",
39406         "239"
39407       ],
39408       [
39409         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39410         "sa",
39411         "966"
39412       ],
39413       [
39414         "Senegal (Sénégal)",
39415         "sn",
39416         "221"
39417       ],
39418       [
39419         "Serbia (Србија)",
39420         "rs",
39421         "381"
39422       ],
39423       [
39424         "Seychelles",
39425         "sc",
39426         "248"
39427       ],
39428       [
39429         "Sierra Leone",
39430         "sl",
39431         "232"
39432       ],
39433       [
39434         "Singapore",
39435         "sg",
39436         "65"
39437       ],
39438       [
39439         "Sint Maarten",
39440         "sx",
39441         "1721"
39442       ],
39443       [
39444         "Slovakia (Slovensko)",
39445         "sk",
39446         "421"
39447       ],
39448       [
39449         "Slovenia (Slovenija)",
39450         "si",
39451         "386"
39452       ],
39453       [
39454         "Solomon Islands",
39455         "sb",
39456         "677"
39457       ],
39458       [
39459         "Somalia (Soomaaliya)",
39460         "so",
39461         "252"
39462       ],
39463       [
39464         "South Africa",
39465         "za",
39466         "27"
39467       ],
39468       [
39469         "South Korea (대한민국)",
39470         "kr",
39471         "82"
39472       ],
39473       [
39474         "South Sudan (‫جنوب السودان‬‎)",
39475         "ss",
39476         "211"
39477       ],
39478       [
39479         "Spain (España)",
39480         "es",
39481         "34"
39482       ],
39483       [
39484         "Sri Lanka (ශ්‍රී ලංකාව)",
39485         "lk",
39486         "94"
39487       ],
39488       [
39489         "Sudan (‫السودان‬‎)",
39490         "sd",
39491         "249"
39492       ],
39493       [
39494         "Suriname",
39495         "sr",
39496         "597"
39497       ],
39498       [
39499         "Svalbard and Jan Mayen",
39500         "sj",
39501         "47",
39502         1
39503       ],
39504       [
39505         "Swaziland",
39506         "sz",
39507         "268"
39508       ],
39509       [
39510         "Sweden (Sverige)",
39511         "se",
39512         "46"
39513       ],
39514       [
39515         "Switzerland (Schweiz)",
39516         "ch",
39517         "41"
39518       ],
39519       [
39520         "Syria (‫سوريا‬‎)",
39521         "sy",
39522         "963"
39523       ],
39524       [
39525         "Taiwan (台灣)",
39526         "tw",
39527         "886"
39528       ],
39529       [
39530         "Tajikistan",
39531         "tj",
39532         "992"
39533       ],
39534       [
39535         "Tanzania",
39536         "tz",
39537         "255"
39538       ],
39539       [
39540         "Thailand (ไทย)",
39541         "th",
39542         "66"
39543       ],
39544       [
39545         "Timor-Leste",
39546         "tl",
39547         "670"
39548       ],
39549       [
39550         "Togo",
39551         "tg",
39552         "228"
39553       ],
39554       [
39555         "Tokelau",
39556         "tk",
39557         "690"
39558       ],
39559       [
39560         "Tonga",
39561         "to",
39562         "676"
39563       ],
39564       [
39565         "Trinidad and Tobago",
39566         "tt",
39567         "1868"
39568       ],
39569       [
39570         "Tunisia (‫تونس‬‎)",
39571         "tn",
39572         "216"
39573       ],
39574       [
39575         "Turkey (Türkiye)",
39576         "tr",
39577         "90"
39578       ],
39579       [
39580         "Turkmenistan",
39581         "tm",
39582         "993"
39583       ],
39584       [
39585         "Turks and Caicos Islands",
39586         "tc",
39587         "1649"
39588       ],
39589       [
39590         "Tuvalu",
39591         "tv",
39592         "688"
39593       ],
39594       [
39595         "U.S. Virgin Islands",
39596         "vi",
39597         "1340"
39598       ],
39599       [
39600         "Uganda",
39601         "ug",
39602         "256"
39603       ],
39604       [
39605         "Ukraine (Україна)",
39606         "ua",
39607         "380"
39608       ],
39609       [
39610         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39611         "ae",
39612         "971"
39613       ],
39614       [
39615         "United Kingdom",
39616         "gb",
39617         "44",
39618         0
39619       ],
39620       [
39621         "United States",
39622         "us",
39623         "1",
39624         0
39625       ],
39626       [
39627         "Uruguay",
39628         "uy",
39629         "598"
39630       ],
39631       [
39632         "Uzbekistan (Oʻzbekiston)",
39633         "uz",
39634         "998"
39635       ],
39636       [
39637         "Vanuatu",
39638         "vu",
39639         "678"
39640       ],
39641       [
39642         "Vatican City (Città del Vaticano)",
39643         "va",
39644         "39",
39645         1
39646       ],
39647       [
39648         "Venezuela",
39649         "ve",
39650         "58"
39651       ],
39652       [
39653         "Vietnam (Việt Nam)",
39654         "vn",
39655         "84"
39656       ],
39657       [
39658         "Wallis and Futuna (Wallis-et-Futuna)",
39659         "wf",
39660         "681"
39661       ],
39662       [
39663         "Western Sahara (‫الصحراء الغربية‬‎)",
39664         "eh",
39665         "212",
39666         1
39667       ],
39668       [
39669         "Yemen (‫اليمن‬‎)",
39670         "ye",
39671         "967"
39672       ],
39673       [
39674         "Zambia",
39675         "zm",
39676         "260"
39677       ],
39678       [
39679         "Zimbabwe",
39680         "zw",
39681         "263"
39682       ],
39683       [
39684         "Åland Islands",
39685         "ax",
39686         "358",
39687         1
39688       ]
39689   ];
39690   
39691   return d;
39692 }/**
39693 *    This script refer to:
39694 *    Title: International Telephone Input
39695 *    Author: Jack O'Connor
39696 *    Code version:  v12.1.12
39697 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39698 **/
39699
39700 /**
39701  * @class Roo.bootstrap.PhoneInput
39702  * @extends Roo.bootstrap.TriggerField
39703  * An input with International dial-code selection
39704  
39705  * @cfg {String} defaultDialCode default '+852'
39706  * @cfg {Array} preferedCountries default []
39707   
39708  * @constructor
39709  * Create a new PhoneInput.
39710  * @param {Object} config Configuration options
39711  */
39712
39713 Roo.bootstrap.PhoneInput = function(config) {
39714     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39715 };
39716
39717 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39718         
39719         listWidth: undefined,
39720         
39721         selectedClass: 'active',
39722         
39723         invalidClass : "has-warning",
39724         
39725         validClass: 'has-success',
39726         
39727         allowed: '0123456789',
39728         
39729         /**
39730          * @cfg {String} defaultDialCode The default dial code when initializing the input
39731          */
39732         defaultDialCode: '+852',
39733         
39734         /**
39735          * @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
39736          */
39737         preferedCountries: false,
39738         
39739         getAutoCreate : function()
39740         {
39741             var data = Roo.bootstrap.PhoneInputData();
39742             var align = this.labelAlign || this.parentLabelAlign();
39743             var id = Roo.id();
39744             
39745             this.allCountries = [];
39746             this.dialCodeMapping = [];
39747             
39748             for (var i = 0; i < data.length; i++) {
39749               var c = data[i];
39750               this.allCountries[i] = {
39751                 name: c[0],
39752                 iso2: c[1],
39753                 dialCode: c[2],
39754                 priority: c[3] || 0,
39755                 areaCodes: c[4] || null
39756               };
39757               this.dialCodeMapping[c[2]] = {
39758                   name: c[0],
39759                   iso2: c[1],
39760                   priority: c[3] || 0,
39761                   areaCodes: c[4] || null
39762               };
39763             }
39764             
39765             var cfg = {
39766                 cls: 'form-group',
39767                 cn: []
39768             };
39769             
39770             var input =  {
39771                 tag: 'input',
39772                 id : id,
39773                 cls : 'form-control tel-input',
39774                 autocomplete: 'new-password'
39775             };
39776             
39777             var hiddenInput = {
39778                 tag: 'input',
39779                 type: 'hidden',
39780                 cls: 'hidden-tel-input'
39781             };
39782             
39783             if (this.name) {
39784                 hiddenInput.name = this.name;
39785             }
39786             
39787             if (this.disabled) {
39788                 input.disabled = true;
39789             }
39790             
39791             var flag_container = {
39792                 tag: 'div',
39793                 cls: 'flag-box',
39794                 cn: [
39795                     {
39796                         tag: 'div',
39797                         cls: 'flag'
39798                     },
39799                     {
39800                         tag: 'div',
39801                         cls: 'caret'
39802                     }
39803                 ]
39804             };
39805             
39806             var box = {
39807                 tag: 'div',
39808                 cls: this.hasFeedback ? 'has-feedback' : '',
39809                 cn: [
39810                     hiddenInput,
39811                     input,
39812                     {
39813                         tag: 'input',
39814                         cls: 'dial-code-holder',
39815                         disabled: true
39816                     }
39817                 ]
39818             };
39819             
39820             var container = {
39821                 cls: 'roo-select2-container input-group',
39822                 cn: [
39823                     flag_container,
39824                     box
39825                 ]
39826             };
39827             
39828             if (this.fieldLabel.length) {
39829                 var indicator = {
39830                     tag: 'i',
39831                     tooltip: 'This field is required'
39832                 };
39833                 
39834                 var label = {
39835                     tag: 'label',
39836                     'for':  id,
39837                     cls: 'control-label',
39838                     cn: []
39839                 };
39840                 
39841                 var label_text = {
39842                     tag: 'span',
39843                     html: this.fieldLabel
39844                 };
39845                 
39846                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39847                 label.cn = [
39848                     indicator,
39849                     label_text
39850                 ];
39851                 
39852                 if(this.indicatorpos == 'right') {
39853                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39854                     label.cn = [
39855                         label_text,
39856                         indicator
39857                     ];
39858                 }
39859                 
39860                 if(align == 'left') {
39861                     container = {
39862                         tag: 'div',
39863                         cn: [
39864                             container
39865                         ]
39866                     };
39867                     
39868                     if(this.labelWidth > 12){
39869                         label.style = "width: " + this.labelWidth + 'px';
39870                     }
39871                     if(this.labelWidth < 13 && this.labelmd == 0){
39872                         this.labelmd = this.labelWidth;
39873                     }
39874                     if(this.labellg > 0){
39875                         label.cls += ' col-lg-' + this.labellg;
39876                         input.cls += ' col-lg-' + (12 - this.labellg);
39877                     }
39878                     if(this.labelmd > 0){
39879                         label.cls += ' col-md-' + this.labelmd;
39880                         container.cls += ' col-md-' + (12 - this.labelmd);
39881                     }
39882                     if(this.labelsm > 0){
39883                         label.cls += ' col-sm-' + this.labelsm;
39884                         container.cls += ' col-sm-' + (12 - this.labelsm);
39885                     }
39886                     if(this.labelxs > 0){
39887                         label.cls += ' col-xs-' + this.labelxs;
39888                         container.cls += ' col-xs-' + (12 - this.labelxs);
39889                     }
39890                 }
39891             }
39892             
39893             cfg.cn = [
39894                 label,
39895                 container
39896             ];
39897             
39898             var settings = this;
39899             
39900             ['xs','sm','md','lg'].map(function(size){
39901                 if (settings[size]) {
39902                     cfg.cls += ' col-' + size + '-' + settings[size];
39903                 }
39904             });
39905             
39906             this.store = new Roo.data.Store({
39907                 proxy : new Roo.data.MemoryProxy({}),
39908                 reader : new Roo.data.JsonReader({
39909                     fields : [
39910                         {
39911                             'name' : 'name',
39912                             'type' : 'string'
39913                         },
39914                         {
39915                             'name' : 'iso2',
39916                             'type' : 'string'
39917                         },
39918                         {
39919                             'name' : 'dialCode',
39920                             'type' : 'string'
39921                         },
39922                         {
39923                             'name' : 'priority',
39924                             'type' : 'string'
39925                         },
39926                         {
39927                             'name' : 'areaCodes',
39928                             'type' : 'string'
39929                         }
39930                     ]
39931                 })
39932             });
39933             
39934             if(!this.preferedCountries) {
39935                 this.preferedCountries = [
39936                     'hk',
39937                     'gb',
39938                     'us'
39939                 ];
39940             }
39941             
39942             var p = this.preferedCountries.reverse();
39943             
39944             if(p) {
39945                 for (var i = 0; i < p.length; i++) {
39946                     for (var j = 0; j < this.allCountries.length; j++) {
39947                         if(this.allCountries[j].iso2 == p[i]) {
39948                             var t = this.allCountries[j];
39949                             this.allCountries.splice(j,1);
39950                             this.allCountries.unshift(t);
39951                         }
39952                     } 
39953                 }
39954             }
39955             
39956             this.store.proxy.data = {
39957                 success: true,
39958                 data: this.allCountries
39959             };
39960             
39961             return cfg;
39962         },
39963         
39964         initEvents : function()
39965         {
39966             this.createList();
39967             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39968             
39969             this.indicator = this.indicatorEl();
39970             this.flag = this.flagEl();
39971             this.dialCodeHolder = this.dialCodeHolderEl();
39972             
39973             this.trigger = this.el.select('div.flag-box',true).first();
39974             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39975             
39976             var _this = this;
39977             
39978             (function(){
39979                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39980                 _this.list.setWidth(lw);
39981             }).defer(100);
39982             
39983             this.list.on('mouseover', this.onViewOver, this);
39984             this.list.on('mousemove', this.onViewMove, this);
39985             this.inputEl().on("keyup", this.onKeyUp, this);
39986             
39987             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39988
39989             this.view = new Roo.View(this.list, this.tpl, {
39990                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39991             });
39992             
39993             this.view.on('click', this.onViewClick, this);
39994             this.setValue(this.defaultDialCode);
39995         },
39996         
39997         onTriggerClick : function(e)
39998         {
39999             Roo.log('trigger click');
40000             if(this.disabled){
40001                 return;
40002             }
40003             
40004             if(this.isExpanded()){
40005                 this.collapse();
40006                 this.hasFocus = false;
40007             }else {
40008                 this.store.load({});
40009                 this.hasFocus = true;
40010                 this.expand();
40011             }
40012         },
40013         
40014         isExpanded : function()
40015         {
40016             return this.list.isVisible();
40017         },
40018         
40019         collapse : function()
40020         {
40021             if(!this.isExpanded()){
40022                 return;
40023             }
40024             this.list.hide();
40025             Roo.get(document).un('mousedown', this.collapseIf, this);
40026             Roo.get(document).un('mousewheel', this.collapseIf, this);
40027             this.fireEvent('collapse', this);
40028             this.validate();
40029         },
40030         
40031         expand : function()
40032         {
40033             Roo.log('expand');
40034
40035             if(this.isExpanded() || !this.hasFocus){
40036                 return;
40037             }
40038             
40039             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40040             this.list.setWidth(lw);
40041             
40042             this.list.show();
40043             this.restrictHeight();
40044             
40045             Roo.get(document).on('mousedown', this.collapseIf, this);
40046             Roo.get(document).on('mousewheel', this.collapseIf, this);
40047             
40048             this.fireEvent('expand', this);
40049         },
40050         
40051         restrictHeight : function()
40052         {
40053             this.list.alignTo(this.inputEl(), this.listAlign);
40054             this.list.alignTo(this.inputEl(), this.listAlign);
40055         },
40056         
40057         onViewOver : function(e, t)
40058         {
40059             if(this.inKeyMode){
40060                 return;
40061             }
40062             var item = this.view.findItemFromChild(t);
40063             
40064             if(item){
40065                 var index = this.view.indexOf(item);
40066                 this.select(index, false);
40067             }
40068         },
40069
40070         // private
40071         onViewClick : function(view, doFocus, el, e)
40072         {
40073             var index = this.view.getSelectedIndexes()[0];
40074             
40075             var r = this.store.getAt(index);
40076             
40077             if(r){
40078                 this.onSelect(r, index);
40079             }
40080             if(doFocus !== false && !this.blockFocus){
40081                 this.inputEl().focus();
40082             }
40083         },
40084         
40085         onViewMove : function(e, t)
40086         {
40087             this.inKeyMode = false;
40088         },
40089         
40090         select : function(index, scrollIntoView)
40091         {
40092             this.selectedIndex = index;
40093             this.view.select(index);
40094             if(scrollIntoView !== false){
40095                 var el = this.view.getNode(index);
40096                 if(el){
40097                     this.list.scrollChildIntoView(el, false);
40098                 }
40099             }
40100         },
40101         
40102         createList : function()
40103         {
40104             this.list = Roo.get(document.body).createChild({
40105                 tag: 'ul',
40106                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40107                 style: 'display:none'
40108             });
40109             
40110             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40111         },
40112         
40113         collapseIf : function(e)
40114         {
40115             var in_combo  = e.within(this.el);
40116             var in_list =  e.within(this.list);
40117             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40118             
40119             if (in_combo || in_list || is_list) {
40120                 return;
40121             }
40122             this.collapse();
40123         },
40124         
40125         onSelect : function(record, index)
40126         {
40127             if(this.fireEvent('beforeselect', this, record, index) !== false){
40128                 
40129                 this.setFlagClass(record.data.iso2);
40130                 this.setDialCode(record.data.dialCode);
40131                 this.hasFocus = false;
40132                 this.collapse();
40133                 this.fireEvent('select', this, record, index);
40134             }
40135         },
40136         
40137         flagEl : function()
40138         {
40139             var flag = this.el.select('div.flag',true).first();
40140             if(!flag){
40141                 return false;
40142             }
40143             return flag;
40144         },
40145         
40146         dialCodeHolderEl : function()
40147         {
40148             var d = this.el.select('input.dial-code-holder',true).first();
40149             if(!d){
40150                 return false;
40151             }
40152             return d;
40153         },
40154         
40155         setDialCode : function(v)
40156         {
40157             this.dialCodeHolder.dom.value = '+'+v;
40158         },
40159         
40160         setFlagClass : function(n)
40161         {
40162             this.flag.dom.className = 'flag '+n;
40163         },
40164         
40165         getValue : function()
40166         {
40167             var v = this.inputEl().getValue();
40168             if(this.dialCodeHolder) {
40169                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40170             }
40171             return v;
40172         },
40173         
40174         setValue : function(v)
40175         {
40176             var d = this.getDialCode(v);
40177             
40178             //invalid dial code
40179             if(v.length == 0 || !d || d.length == 0) {
40180                 if(this.rendered){
40181                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40182                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40183                 }
40184                 return;
40185             }
40186             
40187             //valid dial code
40188             this.setFlagClass(this.dialCodeMapping[d].iso2);
40189             this.setDialCode(d);
40190             this.inputEl().dom.value = v.replace('+'+d,'');
40191             this.hiddenEl().dom.value = this.getValue();
40192             
40193             this.validate();
40194         },
40195         
40196         getDialCode : function(v)
40197         {
40198             v = v ||  '';
40199             
40200             if (v.length == 0) {
40201                 return this.dialCodeHolder.dom.value;
40202             }
40203             
40204             var dialCode = "";
40205             if (v.charAt(0) != "+") {
40206                 return false;
40207             }
40208             var numericChars = "";
40209             for (var i = 1; i < v.length; i++) {
40210               var c = v.charAt(i);
40211               if (!isNaN(c)) {
40212                 numericChars += c;
40213                 if (this.dialCodeMapping[numericChars]) {
40214                   dialCode = v.substr(1, i);
40215                 }
40216                 if (numericChars.length == 4) {
40217                   break;
40218                 }
40219               }
40220             }
40221             return dialCode;
40222         },
40223         
40224         reset : function()
40225         {
40226             this.setValue(this.defaultDialCode);
40227             this.validate();
40228         },
40229         
40230         hiddenEl : function()
40231         {
40232             return this.el.select('input.hidden-tel-input',true).first();
40233         },
40234         
40235         onKeyUp : function(e){
40236             
40237             var k = e.getKey();
40238             var c = e.getCharCode();
40239             
40240             if(
40241                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40242                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40243             ){
40244                 e.stopEvent();
40245             }
40246             
40247             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40248             //     return;
40249             // }
40250             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40251                 e.stopEvent();
40252             }
40253             
40254             this.setValue(this.getValue());
40255         }
40256         
40257 });
40258 /**
40259  * @class Roo.bootstrap.MoneyField
40260  * @extends Roo.bootstrap.ComboBox
40261  * Bootstrap MoneyField class
40262  * 
40263  * @constructor
40264  * Create a new MoneyField.
40265  * @param {Object} config Configuration options
40266  */
40267
40268 Roo.bootstrap.MoneyField = function(config) {
40269     
40270     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40271     
40272 };
40273
40274 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40275     
40276     /**
40277      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40278      */
40279     allowDecimals : true,
40280     /**
40281      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40282      */
40283     decimalSeparator : ".",
40284     /**
40285      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40286      */
40287     decimalPrecision : 0,
40288     /**
40289      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40290      */
40291     allowNegative : true,
40292     /**
40293      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40294      */
40295     allowZero: true,
40296     /**
40297      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40298      */
40299     minValue : Number.NEGATIVE_INFINITY,
40300     /**
40301      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40302      */
40303     maxValue : Number.MAX_VALUE,
40304     /**
40305      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40306      */
40307     minText : "The minimum value for this field is {0}",
40308     /**
40309      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40310      */
40311     maxText : "The maximum value for this field is {0}",
40312     /**
40313      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40314      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40315      */
40316     nanText : "{0} is not a valid number",
40317     /**
40318      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40319      */
40320     castInt : true,
40321     /**
40322      * @cfg {String} defaults currency of the MoneyField
40323      * value should be in lkey
40324      */
40325     defaultCurrency : false,
40326     /**
40327      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40328      */
40329     thousandsDelimiter : false,
40330     
40331     
40332     inputlg : 9,
40333     inputmd : 9,
40334     inputsm : 9,
40335     inputxs : 6,
40336     
40337     store : false,
40338     
40339     getAutoCreate : function()
40340     {
40341         var align = this.labelAlign || this.parentLabelAlign();
40342         
40343         var id = Roo.id();
40344
40345         var cfg = {
40346             cls: 'form-group',
40347             cn: []
40348         };
40349
40350         var input =  {
40351             tag: 'input',
40352             id : id,
40353             cls : 'form-control roo-money-amount-input',
40354             autocomplete: 'new-password'
40355         };
40356         
40357         var hiddenInput = {
40358             tag: 'input',
40359             type: 'hidden',
40360             id: Roo.id(),
40361             cls: 'hidden-number-input'
40362         };
40363         
40364         if (this.name) {
40365             hiddenInput.name = this.name;
40366         }
40367
40368         if (this.disabled) {
40369             input.disabled = true;
40370         }
40371
40372         var clg = 12 - this.inputlg;
40373         var cmd = 12 - this.inputmd;
40374         var csm = 12 - this.inputsm;
40375         var cxs = 12 - this.inputxs;
40376         
40377         var container = {
40378             tag : 'div',
40379             cls : 'row roo-money-field',
40380             cn : [
40381                 {
40382                     tag : 'div',
40383                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40384                     cn : [
40385                         {
40386                             tag : 'div',
40387                             cls: 'roo-select2-container input-group',
40388                             cn: [
40389                                 {
40390                                     tag : 'input',
40391                                     cls : 'form-control roo-money-currency-input',
40392                                     autocomplete: 'new-password',
40393                                     readOnly : 1,
40394                                     name : this.currencyName
40395                                 },
40396                                 {
40397                                     tag :'span',
40398                                     cls : 'input-group-addon',
40399                                     cn : [
40400                                         {
40401                                             tag: 'span',
40402                                             cls: 'caret'
40403                                         }
40404                                     ]
40405                                 }
40406                             ]
40407                         }
40408                     ]
40409                 },
40410                 {
40411                     tag : 'div',
40412                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40413                     cn : [
40414                         {
40415                             tag: 'div',
40416                             cls: this.hasFeedback ? 'has-feedback' : '',
40417                             cn: [
40418                                 input
40419                             ]
40420                         }
40421                     ]
40422                 }
40423             ]
40424             
40425         };
40426         
40427         if (this.fieldLabel.length) {
40428             var indicator = {
40429                 tag: 'i',
40430                 tooltip: 'This field is required'
40431             };
40432
40433             var label = {
40434                 tag: 'label',
40435                 'for':  id,
40436                 cls: 'control-label',
40437                 cn: []
40438             };
40439
40440             var label_text = {
40441                 tag: 'span',
40442                 html: this.fieldLabel
40443             };
40444
40445             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40446             label.cn = [
40447                 indicator,
40448                 label_text
40449             ];
40450
40451             if(this.indicatorpos == 'right') {
40452                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40453                 label.cn = [
40454                     label_text,
40455                     indicator
40456                 ];
40457             }
40458
40459             if(align == 'left') {
40460                 container = {
40461                     tag: 'div',
40462                     cn: [
40463                         container
40464                     ]
40465                 };
40466
40467                 if(this.labelWidth > 12){
40468                     label.style = "width: " + this.labelWidth + 'px';
40469                 }
40470                 if(this.labelWidth < 13 && this.labelmd == 0){
40471                     this.labelmd = this.labelWidth;
40472                 }
40473                 if(this.labellg > 0){
40474                     label.cls += ' col-lg-' + this.labellg;
40475                     input.cls += ' col-lg-' + (12 - this.labellg);
40476                 }
40477                 if(this.labelmd > 0){
40478                     label.cls += ' col-md-' + this.labelmd;
40479                     container.cls += ' col-md-' + (12 - this.labelmd);
40480                 }
40481                 if(this.labelsm > 0){
40482                     label.cls += ' col-sm-' + this.labelsm;
40483                     container.cls += ' col-sm-' + (12 - this.labelsm);
40484                 }
40485                 if(this.labelxs > 0){
40486                     label.cls += ' col-xs-' + this.labelxs;
40487                     container.cls += ' col-xs-' + (12 - this.labelxs);
40488                 }
40489             }
40490         }
40491
40492         cfg.cn = [
40493             label,
40494             container,
40495             hiddenInput
40496         ];
40497         
40498         var settings = this;
40499
40500         ['xs','sm','md','lg'].map(function(size){
40501             if (settings[size]) {
40502                 cfg.cls += ' col-' + size + '-' + settings[size];
40503             }
40504         });
40505         
40506         return cfg;
40507     },
40508     
40509     initEvents : function()
40510     {
40511         this.indicator = this.indicatorEl();
40512         
40513         this.initCurrencyEvent();
40514         
40515         this.initNumberEvent();
40516     },
40517     
40518     initCurrencyEvent : function()
40519     {
40520         if (!this.store) {
40521             throw "can not find store for combo";
40522         }
40523         
40524         this.store = Roo.factory(this.store, Roo.data);
40525         this.store.parent = this;
40526         
40527         this.createList();
40528         
40529         this.triggerEl = this.el.select('.input-group-addon', true).first();
40530         
40531         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40532         
40533         var _this = this;
40534         
40535         (function(){
40536             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40537             _this.list.setWidth(lw);
40538         }).defer(100);
40539         
40540         this.list.on('mouseover', this.onViewOver, this);
40541         this.list.on('mousemove', this.onViewMove, this);
40542         this.list.on('scroll', this.onViewScroll, this);
40543         
40544         if(!this.tpl){
40545             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40546         }
40547         
40548         this.view = new Roo.View(this.list, this.tpl, {
40549             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40550         });
40551         
40552         this.view.on('click', this.onViewClick, this);
40553         
40554         this.store.on('beforeload', this.onBeforeLoad, this);
40555         this.store.on('load', this.onLoad, this);
40556         this.store.on('loadexception', this.onLoadException, this);
40557         
40558         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40559             "up" : function(e){
40560                 this.inKeyMode = true;
40561                 this.selectPrev();
40562             },
40563
40564             "down" : function(e){
40565                 if(!this.isExpanded()){
40566                     this.onTriggerClick();
40567                 }else{
40568                     this.inKeyMode = true;
40569                     this.selectNext();
40570                 }
40571             },
40572
40573             "enter" : function(e){
40574                 this.collapse();
40575                 
40576                 if(this.fireEvent("specialkey", this, e)){
40577                     this.onViewClick(false);
40578                 }
40579                 
40580                 return true;
40581             },
40582
40583             "esc" : function(e){
40584                 this.collapse();
40585             },
40586
40587             "tab" : function(e){
40588                 this.collapse();
40589                 
40590                 if(this.fireEvent("specialkey", this, e)){
40591                     this.onViewClick(false);
40592                 }
40593                 
40594                 return true;
40595             },
40596
40597             scope : this,
40598
40599             doRelay : function(foo, bar, hname){
40600                 if(hname == 'down' || this.scope.isExpanded()){
40601                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40602                 }
40603                 return true;
40604             },
40605
40606             forceKeyDown: true
40607         });
40608         
40609         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40610         
40611     },
40612     
40613     initNumberEvent : function(e)
40614     {
40615         this.inputEl().on("keydown" , this.fireKey,  this);
40616         this.inputEl().on("focus", this.onFocus,  this);
40617         this.inputEl().on("blur", this.onBlur,  this);
40618         
40619         this.inputEl().relayEvent('keyup', this);
40620         
40621         if(this.indicator){
40622             this.indicator.addClass('invisible');
40623         }
40624  
40625         this.originalValue = this.getValue();
40626         
40627         if(this.validationEvent == 'keyup'){
40628             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40629             this.inputEl().on('keyup', this.filterValidation, this);
40630         }
40631         else if(this.validationEvent !== false){
40632             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40633         }
40634         
40635         if(this.selectOnFocus){
40636             this.on("focus", this.preFocus, this);
40637             
40638         }
40639         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40640             this.inputEl().on("keypress", this.filterKeys, this);
40641         } else {
40642             this.inputEl().relayEvent('keypress', this);
40643         }
40644         
40645         var allowed = "0123456789";
40646         
40647         if(this.allowDecimals){
40648             allowed += this.decimalSeparator;
40649         }
40650         
40651         if(this.allowNegative){
40652             allowed += "-";
40653         }
40654         
40655         if(this.thousandsDelimiter) {
40656             allowed += ",";
40657         }
40658         
40659         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40660         
40661         var keyPress = function(e){
40662             
40663             var k = e.getKey();
40664             
40665             var c = e.getCharCode();
40666             
40667             if(
40668                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40669                     allowed.indexOf(String.fromCharCode(c)) === -1
40670             ){
40671                 e.stopEvent();
40672                 return;
40673             }
40674             
40675             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40676                 return;
40677             }
40678             
40679             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40680                 e.stopEvent();
40681             }
40682         };
40683         
40684         this.inputEl().on("keypress", keyPress, this);
40685         
40686     },
40687     
40688     onTriggerClick : function(e)
40689     {   
40690         if(this.disabled){
40691             return;
40692         }
40693         
40694         this.page = 0;
40695         this.loadNext = false;
40696         
40697         if(this.isExpanded()){
40698             this.collapse();
40699             return;
40700         }
40701         
40702         this.hasFocus = true;
40703         
40704         if(this.triggerAction == 'all') {
40705             this.doQuery(this.allQuery, true);
40706             return;
40707         }
40708         
40709         this.doQuery(this.getRawValue());
40710     },
40711     
40712     getCurrency : function()
40713     {   
40714         var v = this.currencyEl().getValue();
40715         
40716         return v;
40717     },
40718     
40719     restrictHeight : function()
40720     {
40721         this.list.alignTo(this.currencyEl(), this.listAlign);
40722         this.list.alignTo(this.currencyEl(), this.listAlign);
40723     },
40724     
40725     onViewClick : function(view, doFocus, el, e)
40726     {
40727         var index = this.view.getSelectedIndexes()[0];
40728         
40729         var r = this.store.getAt(index);
40730         
40731         if(r){
40732             this.onSelect(r, index);
40733         }
40734     },
40735     
40736     onSelect : function(record, index){
40737         
40738         if(this.fireEvent('beforeselect', this, record, index) !== false){
40739         
40740             this.setFromCurrencyData(index > -1 ? record.data : false);
40741             
40742             this.collapse();
40743             
40744             this.fireEvent('select', this, record, index);
40745         }
40746     },
40747     
40748     setFromCurrencyData : function(o)
40749     {
40750         var currency = '';
40751         
40752         this.lastCurrency = o;
40753         
40754         if (this.currencyField) {
40755             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40756         } else {
40757             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40758         }
40759         
40760         this.lastSelectionText = currency;
40761         
40762         //setting default currency
40763         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40764             this.setCurrency(this.defaultCurrency);
40765             return;
40766         }
40767         
40768         this.setCurrency(currency);
40769     },
40770     
40771     setFromData : function(o)
40772     {
40773         var c = {};
40774         
40775         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40776         
40777         this.setFromCurrencyData(c);
40778         
40779         var value = '';
40780         
40781         if (this.name) {
40782             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40783         } else {
40784             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40785         }
40786         
40787         this.setValue(value);
40788         
40789     },
40790     
40791     setCurrency : function(v)
40792     {   
40793         this.currencyValue = v;
40794         
40795         if(this.rendered){
40796             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40797             this.validate();
40798         }
40799     },
40800     
40801     setValue : function(v)
40802     {
40803         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40804         
40805         this.value = v;
40806         
40807         if(this.rendered){
40808             
40809             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40810             
40811             this.inputEl().dom.value = (v == '') ? '' :
40812                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40813             
40814             if(!this.allowZero && v === '0') {
40815                 this.hiddenEl().dom.value = '';
40816                 this.inputEl().dom.value = '';
40817             }
40818             
40819             this.validate();
40820         }
40821     },
40822     
40823     getRawValue : function()
40824     {
40825         var v = this.inputEl().getValue();
40826         
40827         return v;
40828     },
40829     
40830     getValue : function()
40831     {
40832         return this.fixPrecision(this.parseValue(this.getRawValue()));
40833     },
40834     
40835     parseValue : function(value)
40836     {
40837         if(this.thousandsDelimiter) {
40838             value += "";
40839             r = new RegExp(",", "g");
40840             value = value.replace(r, "");
40841         }
40842         
40843         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40844         return isNaN(value) ? '' : value;
40845         
40846     },
40847     
40848     fixPrecision : function(value)
40849     {
40850         if(this.thousandsDelimiter) {
40851             value += "";
40852             r = new RegExp(",", "g");
40853             value = value.replace(r, "");
40854         }
40855         
40856         var nan = isNaN(value);
40857         
40858         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40859             return nan ? '' : value;
40860         }
40861         return parseFloat(value).toFixed(this.decimalPrecision);
40862     },
40863     
40864     decimalPrecisionFcn : function(v)
40865     {
40866         return Math.floor(v);
40867     },
40868     
40869     validateValue : function(value)
40870     {
40871         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40872             return false;
40873         }
40874         
40875         var num = this.parseValue(value);
40876         
40877         if(isNaN(num)){
40878             this.markInvalid(String.format(this.nanText, value));
40879             return false;
40880         }
40881         
40882         if(num < this.minValue){
40883             this.markInvalid(String.format(this.minText, this.minValue));
40884             return false;
40885         }
40886         
40887         if(num > this.maxValue){
40888             this.markInvalid(String.format(this.maxText, this.maxValue));
40889             return false;
40890         }
40891         
40892         return true;
40893     },
40894     
40895     validate : function()
40896     {
40897         if(this.disabled || this.allowBlank){
40898             this.markValid();
40899             return true;
40900         }
40901         
40902         var currency = this.getCurrency();
40903         
40904         if(this.validateValue(this.getRawValue()) && currency.length){
40905             this.markValid();
40906             return true;
40907         }
40908         
40909         this.markInvalid();
40910         return false;
40911     },
40912     
40913     getName: function()
40914     {
40915         return this.name;
40916     },
40917     
40918     beforeBlur : function()
40919     {
40920         if(!this.castInt){
40921             return;
40922         }
40923         
40924         var v = this.parseValue(this.getRawValue());
40925         
40926         if(v || v == 0){
40927             this.setValue(v);
40928         }
40929     },
40930     
40931     onBlur : function()
40932     {
40933         this.beforeBlur();
40934         
40935         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40936             //this.el.removeClass(this.focusClass);
40937         }
40938         
40939         this.hasFocus = false;
40940         
40941         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40942             this.validate();
40943         }
40944         
40945         var v = this.getValue();
40946         
40947         if(String(v) !== String(this.startValue)){
40948             this.fireEvent('change', this, v, this.startValue);
40949         }
40950         
40951         this.fireEvent("blur", this);
40952     },
40953     
40954     inputEl : function()
40955     {
40956         return this.el.select('.roo-money-amount-input', true).first();
40957     },
40958     
40959     currencyEl : function()
40960     {
40961         return this.el.select('.roo-money-currency-input', true).first();
40962     },
40963     
40964     hiddenEl : function()
40965     {
40966         return this.el.select('input.hidden-number-input',true).first();
40967     }
40968     
40969 });