Roo/bootstrap/Modal.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368     
369     /**
370      * Set the element that will be used to show or hide
371      */
372     setVisibilityEl : function(el)
373     {
374         this.visibilityEl = el;
375     },
376     
377      /**
378      * Get the element that will be used to show or hide
379      */
380     getVisibilityEl : function()
381     {
382         if (typeof(this.visibilityEl) == 'object') {
383             return this.visibilityEl;
384         }
385         
386         if (typeof(this.visibilityEl) == 'string') {
387             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
388         }
389         
390         return this.getEl();
391     },
392     
393     /**
394      * Show a component - removes 'hidden' class
395      */
396     show : function()
397     {
398         if(!this.getVisibilityEl()){
399             return;
400         }
401          
402         this.getVisibilityEl().removeClass('hidden');
403         
404         this.fireEvent('show', this);
405         
406         
407     },
408     /**
409      * Hide a component - adds 'hidden' class
410      */
411     hide: function()
412     {
413         if(!this.getVisibilityEl()){
414             return;
415         }
416         
417         this.getVisibilityEl().addClass('hidden');
418         
419         this.fireEvent('hide', this);
420         
421     }
422 });
423
424  /*
425  * - LGPL
426  *
427  * Body
428  *
429  */
430
431 /**
432  * @class Roo.bootstrap.Body
433  * @extends Roo.bootstrap.Component
434  * Bootstrap Body class
435  *
436  * @constructor
437  * Create a new body
438  * @param {Object} config The config object
439  */
440
441 Roo.bootstrap.Body = function(config){
442
443     config = config || {};
444
445     Roo.bootstrap.Body.superclass.constructor.call(this, config);
446     this.el = Roo.get(config.el ? config.el : document.body );
447     if (this.cls && this.cls.length) {
448         Roo.get(document.body).addClass(this.cls);
449     }
450 };
451
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
453
454     is_body : true,// just to make sure it's constructed?
455
456         autoCreate : {
457         cls: 'container'
458     },
459     onRender : function(ct, position)
460     {
461        /* Roo.log("Roo.bootstrap.Body - onRender");
462         if (this.cls && this.cls.length) {
463             Roo.get(document.body).addClass(this.cls);
464         }
465         // style??? xttr???
466         */
467     }
468
469
470
471
472 });
473 /*
474  * - LGPL
475  *
476  * button group
477  * 
478  */
479
480
481 /**
482  * @class Roo.bootstrap.ButtonGroup
483  * @extends Roo.bootstrap.Component
484  * Bootstrap ButtonGroup class
485  * @cfg {String} size lg | sm | xs (default empty normal)
486  * @cfg {String} align vertical | justified  (default none)
487  * @cfg {String} direction up | down (default down)
488  * @cfg {Boolean} toolbar false | true
489  * @cfg {Boolean} btn true | false
490  * 
491  * 
492  * @constructor
493  * Create a new Input
494  * @param {Object} config The config object
495  */
496
497 Roo.bootstrap.ButtonGroup = function(config){
498     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
499 };
500
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
502     
503     size: '',
504     align: '',
505     direction: '',
506     toolbar: false,
507     btn: true,
508
509     getAutoCreate : function(){
510         var cfg = {
511             cls: 'btn-group',
512             html : null
513         };
514         
515         cfg.html = this.html || cfg.html;
516         
517         if (this.toolbar) {
518             cfg = {
519                 cls: 'btn-toolbar',
520                 html: null
521             };
522             
523             return cfg;
524         }
525         
526         if (['vertical','justified'].indexOf(this.align)!==-1) {
527             cfg.cls = 'btn-group-' + this.align;
528             
529             if (this.align == 'justified') {
530                 console.log(this.items);
531             }
532         }
533         
534         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535             cfg.cls += ' btn-group-' + this.size;
536         }
537         
538         if (this.direction == 'up') {
539             cfg.cls += ' dropup' ;
540         }
541         
542         return cfg;
543     }
544    
545 });
546
547  /*
548  * - LGPL
549  *
550  * button
551  * 
552  */
553
554 /**
555  * @class Roo.bootstrap.Button
556  * @extends Roo.bootstrap.Component
557  * Bootstrap Button class
558  * @cfg {String} html The button content
559  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
560  * @cfg {String} size ( lg | sm | xs)
561  * @cfg {String} tag ( a | input | submit)
562  * @cfg {String} href empty or href
563  * @cfg {Boolean} disabled default false;
564  * @cfg {Boolean} isClose default false;
565  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566  * @cfg {String} badge text for badge
567  * @cfg {String} theme (default|glow)  
568  * @cfg {Boolean} inverse dark themed version
569  * @cfg {Boolean} toggle is it a slidy toggle button
570  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571  * @cfg {String} ontext text for on slidy toggle state
572  * @cfg {String} offtext text for off slidy toggle state
573  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
574  * @cfg {Boolean} removeClass remove the standard class..
575  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
576  * 
577  * @constructor
578  * Create a new button
579  * @param {Object} config The config object
580  */
581
582
583 Roo.bootstrap.Button = function(config){
584     Roo.bootstrap.Button.superclass.constructor.call(this, config);
585     this.weightClass = ["btn-default", 
586                        "btn-primary", 
587                        "btn-success", 
588                        "btn-info", 
589                        "btn-warning",
590                        "btn-danger",
591                        "btn-link"
592                       ],  
593     this.addEvents({
594         // raw events
595         /**
596          * @event click
597          * When a butotn is pressed
598          * @param {Roo.bootstrap.Button} btn
599          * @param {Roo.EventObject} e
600          */
601         "click" : true,
602          /**
603          * @event toggle
604          * After the button has been toggles
605          * @param {Roo.bootstrap.Button} btn
606          * @param {Roo.EventObject} e
607          * @param {boolean} pressed (also available as button.pressed)
608          */
609         "toggle" : true
610     });
611 };
612
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
614     html: false,
615     active: false,
616     weight: '',
617     size: '',
618     tag: 'button',
619     href: '',
620     disabled: false,
621     isClose: false,
622     glyphicon: '',
623     badge: '',
624     theme: 'default',
625     inverse: false,
626     
627     toggle: false,
628     ontext: 'ON',
629     offtext: 'OFF',
630     defaulton: true,
631     preventDefault: true,
632     removeClass: false,
633     name: false,
634     target: false,
635      
636     pressed : null,
637      
638     
639     getAutoCreate : function(){
640         
641         var cfg = {
642             tag : 'button',
643             cls : 'roo-button',
644             html: ''
645         };
646         
647         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
649             this.tag = 'button';
650         } else {
651             cfg.tag = this.tag;
652         }
653         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
654         
655         if (this.toggle == true) {
656             cfg={
657                 tag: 'div',
658                 cls: 'slider-frame roo-button',
659                 cn: [
660                     {
661                         tag: 'span',
662                         'data-on-text':'ON',
663                         'data-off-text':'OFF',
664                         cls: 'slider-button',
665                         html: this.offtext
666                     }
667                 ]
668             };
669             
670             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671                 cfg.cls += ' '+this.weight;
672             }
673             
674             return cfg;
675         }
676         
677         if (this.isClose) {
678             cfg.cls += ' close';
679             
680             cfg["aria-hidden"] = true;
681             
682             cfg.html = "&times;";
683             
684             return cfg;
685         }
686         
687          
688         if (this.theme==='default') {
689             cfg.cls = 'btn roo-button';
690             
691             //if (this.parentType != 'Navbar') {
692             this.weight = this.weight.length ?  this.weight : 'default';
693             //}
694             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
695                 
696                 cfg.cls += ' btn-' + this.weight;
697             }
698         } else if (this.theme==='glow') {
699             
700             cfg.tag = 'a';
701             cfg.cls = 'btn-glow roo-button';
702             
703             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
704                 
705                 cfg.cls += ' ' + this.weight;
706             }
707         }
708    
709         
710         if (this.inverse) {
711             this.cls += ' inverse';
712         }
713         
714         
715         if (this.active || this.pressed === true) {
716             cfg.cls += ' active';
717         }
718         
719         if (this.disabled) {
720             cfg.disabled = 'disabled';
721         }
722         
723         if (this.items) {
724             Roo.log('changing to ul' );
725             cfg.tag = 'ul';
726             this.glyphicon = 'caret';
727         }
728         
729         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
730          
731         //gsRoo.log(this.parentType);
732         if (this.parentType === 'Navbar' && !this.parent().bar) {
733             Roo.log('changing to li?');
734             
735             cfg.tag = 'li';
736             
737             cfg.cls = '';
738             cfg.cn =  [{
739                 tag : 'a',
740                 cls : 'roo-button',
741                 html : this.html,
742                 href : this.href || '#'
743             }];
744             if (this.menu) {
745                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
746                 cfg.cls += ' dropdown';
747             }   
748             
749             delete cfg.html;
750             
751         }
752         
753        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
754         
755         if (this.glyphicon) {
756             cfg.html = ' ' + cfg.html;
757             
758             cfg.cn = [
759                 {
760                     tag: 'span',
761                     cls: 'glyphicon glyphicon-' + this.glyphicon
762                 }
763             ];
764         }
765         
766         if (this.badge) {
767             cfg.html += ' ';
768             
769             cfg.tag = 'a';
770             
771 //            cfg.cls='btn roo-button';
772             
773             cfg.href=this.href;
774             
775             var value = cfg.html;
776             
777             if(this.glyphicon){
778                 value = {
779                             tag: 'span',
780                             cls: 'glyphicon glyphicon-' + this.glyphicon,
781                             html: this.html
782                         };
783                 
784             }
785             
786             cfg.cn = [
787                 value,
788                 {
789                     tag: 'span',
790                     cls: 'badge',
791                     html: this.badge
792                 }
793             ];
794             
795             cfg.html='';
796         }
797         
798         if (this.menu) {
799             cfg.cls += ' dropdown';
800             cfg.html = typeof(cfg.html) != 'undefined' ?
801                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
802         }
803         
804         if (cfg.tag !== 'a' && this.href !== '') {
805             throw "Tag must be a to set href.";
806         } else if (this.href.length > 0) {
807             cfg.href = this.href;
808         }
809         
810         if(this.removeClass){
811             cfg.cls = '';
812         }
813         
814         if(this.target){
815             cfg.target = this.target;
816         }
817         
818         return cfg;
819     },
820     initEvents: function() {
821        // Roo.log('init events?');
822 //        Roo.log(this.el.dom);
823         // add the menu...
824         
825         if (typeof (this.menu) != 'undefined') {
826             this.menu.parentType = this.xtype;
827             this.menu.triggerEl = this.el;
828             this.addxtype(Roo.apply({}, this.menu));
829         }
830
831
832        if (this.el.hasClass('roo-button')) {
833             this.el.on('click', this.onClick, this);
834        } else {
835             this.el.select('.roo-button').on('click', this.onClick, this);
836        }
837        
838        if(this.removeClass){
839            this.el.on('click', this.onClick, this);
840        }
841        
842        this.el.enableDisplayMode();
843         
844     },
845     onClick : function(e)
846     {
847         if (this.disabled) {
848             return;
849         }
850         
851         Roo.log('button on click ');
852         if(this.preventDefault){
853             e.preventDefault();
854         }
855         
856         if (this.pressed === true || this.pressed === false) {
857             this.toggleActive(e);
858         }
859         
860         
861         this.fireEvent('click', this, e);
862     },
863     
864     /**
865      * Enables this button
866      */
867     enable : function()
868     {
869         this.disabled = false;
870         this.el.removeClass('disabled');
871     },
872     
873     /**
874      * Disable this button
875      */
876     disable : function()
877     {
878         this.disabled = true;
879         this.el.addClass('disabled');
880     },
881      /**
882      * sets the active state on/off, 
883      * @param {Boolean} state (optional) Force a particular state
884      */
885     setActive : function(v) {
886         
887         this.el[v ? 'addClass' : 'removeClass']('active');
888         this.pressed = v;
889     },
890      /**
891      * toggles the current active state 
892      */
893     toggleActive : function(e)
894     {
895         this.setActive(!this.pressed);
896         this.fireEvent('toggle', this, e, !this.pressed);
897     },
898      /**
899      * get the current active state
900      * @return {boolean} true if it's active
901      */
902     isActive : function()
903     {
904         return this.el.hasClass('active');
905     },
906     /**
907      * set the text of the first selected button
908      */
909     setText : function(str)
910     {
911         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
912     },
913     /**
914      * get the text of the first selected button
915      */
916     getText : function()
917     {
918         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
919     },
920     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             
2837             var view_height = Roo.lib.Dom.getViewportHeight(true) - 60;
2838             var modal_height = this.headerEl.getHeight() + this.bodyEl.getHeight() + this.footerEl.getHeight();
2839             
2840             Roo.log(view_height);
2841             Roo.log(modal_height);
2842             
2843             if(
2844                 (
2845                     this.headerEl.getHeight() + 
2846                     this.bodyEl.getHeight() + 
2847                     this.footerEl.getHeight()
2848                 ) > view_height) {
2849             } {
2850                 this.setSize(w,view_height);
2851             }
2852         }
2853         
2854     },
2855
2856     setSize : function(w,h)
2857     {
2858         if (!w && !h) {
2859             return;
2860         }
2861         this.resizeTo(w,h);
2862     },
2863
2864     show : function() {
2865
2866         if (!this.rendered) {
2867             this.render();
2868         }
2869
2870         //this.el.setStyle('display', 'block');
2871         this.el.removeClass('hideing');        
2872         this.el.addClass('show');
2873  
2874         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2875             var _this = this;
2876             (function(){
2877                 this.el.addClass('in');
2878             }).defer(50, this);
2879         }else{
2880             this.el.addClass('in');
2881         }
2882
2883         // not sure how we can show data in here..
2884         //if (this.tmpl) {
2885         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2886         //}
2887
2888         Roo.get(document.body).addClass("x-body-masked");
2889         
2890         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2891         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2892         this.maskEl.addClass('show');
2893         
2894         this.resize();
2895         
2896         this.fireEvent('show', this);
2897
2898         // set zindex here - otherwise it appears to be ignored...
2899         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2900
2901         (function () {
2902             this.items.forEach( function(e) {
2903                 e.layout ? e.layout() : false;
2904
2905             });
2906         }).defer(100,this);
2907
2908     },
2909     hide : function()
2910     {
2911         if(this.fireEvent("beforehide", this) !== false){
2912             this.maskEl.removeClass('show');
2913             Roo.get(document.body).removeClass("x-body-masked");
2914             this.el.removeClass('in');
2915             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2916
2917             if(this.animate){ // why
2918                 this.el.addClass('hideing');
2919                 (function(){
2920                     if (!this.el.hasClass('hideing')) {
2921                         return; // it's been shown again...
2922                     }
2923                     this.el.removeClass('show');
2924                     this.el.removeClass('hideing');
2925                 }).defer(150,this);
2926                 
2927             }else{
2928                  this.el.removeClass('show');
2929             }
2930             this.fireEvent('hide', this);
2931         }
2932     },
2933     isVisible : function()
2934     {
2935         
2936         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2937         
2938     },
2939
2940     addButton : function(str, cb)
2941     {
2942
2943
2944         var b = Roo.apply({}, { html : str } );
2945         b.xns = b.xns || Roo.bootstrap;
2946         b.xtype = b.xtype || 'Button';
2947         if (typeof(b.listeners) == 'undefined') {
2948             b.listeners = { click : cb.createDelegate(this)  };
2949         }
2950
2951         var btn = Roo.factory(b);
2952
2953         btn.render(this.el.select('.modal-footer div').first());
2954
2955         return btn;
2956
2957     },
2958
2959     setDefaultButton : function(btn)
2960     {
2961         //this.el.select('.modal-footer').()
2962     },
2963     diff : false,
2964
2965     resizeTo: function(w,h)
2966     {
2967         // skip.. ?? why??
2968
2969         this.dialogEl.setWidth(w);
2970         if (this.diff === false) {
2971             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2972         }
2973
2974         this.bodyEl.setHeight(h-this.diff);
2975
2976         this.fireEvent('resize', this);
2977
2978     },
2979     setContentSize  : function(w, h)
2980     {
2981
2982     },
2983     onButtonClick: function(btn,e)
2984     {
2985         //Roo.log([a,b,c]);
2986         this.fireEvent('btnclick', btn.name, e);
2987     },
2988      /**
2989      * Set the title of the Dialog
2990      * @param {String} str new Title
2991      */
2992     setTitle: function(str) {
2993         this.titleEl.dom.innerHTML = str;
2994     },
2995     /**
2996      * Set the body of the Dialog
2997      * @param {String} str new Title
2998      */
2999     setBody: function(str) {
3000         this.bodyEl.dom.innerHTML = str;
3001     },
3002     /**
3003      * Set the body of the Dialog using the template
3004      * @param {Obj} data - apply this data to the template and replace the body contents.
3005      */
3006     applyBody: function(obj)
3007     {
3008         if (!this.tmpl) {
3009             Roo.log("Error - using apply Body without a template");
3010             //code
3011         }
3012         this.tmpl.overwrite(this.bodyEl, obj);
3013     }
3014
3015 });
3016
3017
3018 Roo.apply(Roo.bootstrap.Modal,  {
3019     /**
3020          * Button config that displays a single OK button
3021          * @type Object
3022          */
3023         OK :  [{
3024             name : 'ok',
3025             weight : 'primary',
3026             html : 'OK'
3027         }],
3028         /**
3029          * Button config that displays Yes and No buttons
3030          * @type Object
3031          */
3032         YESNO : [
3033             {
3034                 name  : 'no',
3035                 html : 'No'
3036             },
3037             {
3038                 name  :'yes',
3039                 weight : 'primary',
3040                 html : 'Yes'
3041             }
3042         ],
3043
3044         /**
3045          * Button config that displays OK and Cancel buttons
3046          * @type Object
3047          */
3048         OKCANCEL : [
3049             {
3050                name : 'cancel',
3051                 html : 'Cancel'
3052             },
3053             {
3054                 name : 'ok',
3055                 weight : 'primary',
3056                 html : 'OK'
3057             }
3058         ],
3059         /**
3060          * Button config that displays Yes, No and Cancel buttons
3061          * @type Object
3062          */
3063         YESNOCANCEL : [
3064             {
3065                 name : 'yes',
3066                 weight : 'primary',
3067                 html : 'Yes'
3068             },
3069             {
3070                 name : 'no',
3071                 html : 'No'
3072             },
3073             {
3074                 name : 'cancel',
3075                 html : 'Cancel'
3076             }
3077         ],
3078         
3079         zIndex : 10001
3080 });
3081 /*
3082  * - LGPL
3083  *
3084  * messagebox - can be used as a replace
3085  * 
3086  */
3087 /**
3088  * @class Roo.MessageBox
3089  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3090  * Example usage:
3091  *<pre><code>
3092 // Basic alert:
3093 Roo.Msg.alert('Status', 'Changes saved successfully.');
3094
3095 // Prompt for user data:
3096 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3097     if (btn == 'ok'){
3098         // process text value...
3099     }
3100 });
3101
3102 // Show a dialog using config options:
3103 Roo.Msg.show({
3104    title:'Save Changes?',
3105    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3106    buttons: Roo.Msg.YESNOCANCEL,
3107    fn: processResult,
3108    animEl: 'elId'
3109 });
3110 </code></pre>
3111  * @singleton
3112  */
3113 Roo.bootstrap.MessageBox = function(){
3114     var dlg, opt, mask, waitTimer;
3115     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3116     var buttons, activeTextEl, bwidth;
3117
3118     
3119     // private
3120     var handleButton = function(button){
3121         dlg.hide();
3122         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3123     };
3124
3125     // private
3126     var handleHide = function(){
3127         if(opt && opt.cls){
3128             dlg.el.removeClass(opt.cls);
3129         }
3130         //if(waitTimer){
3131         //    Roo.TaskMgr.stop(waitTimer);
3132         //    waitTimer = null;
3133         //}
3134     };
3135
3136     // private
3137     var updateButtons = function(b){
3138         var width = 0;
3139         if(!b){
3140             buttons["ok"].hide();
3141             buttons["cancel"].hide();
3142             buttons["yes"].hide();
3143             buttons["no"].hide();
3144             //dlg.footer.dom.style.display = 'none';
3145             return width;
3146         }
3147         dlg.footerEl.dom.style.display = '';
3148         for(var k in buttons){
3149             if(typeof buttons[k] != "function"){
3150                 if(b[k]){
3151                     buttons[k].show();
3152                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3153                     width += buttons[k].el.getWidth()+15;
3154                 }else{
3155                     buttons[k].hide();
3156                 }
3157             }
3158         }
3159         return width;
3160     };
3161
3162     // private
3163     var handleEsc = function(d, k, e){
3164         if(opt && opt.closable !== false){
3165             dlg.hide();
3166         }
3167         if(e){
3168             e.stopEvent();
3169         }
3170     };
3171
3172     return {
3173         /**
3174          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3175          * @return {Roo.BasicDialog} The BasicDialog element
3176          */
3177         getDialog : function(){
3178            if(!dlg){
3179                 dlg = new Roo.bootstrap.Modal( {
3180                     //draggable: true,
3181                     //resizable:false,
3182                     //constraintoviewport:false,
3183                     //fixedcenter:true,
3184                     //collapsible : false,
3185                     //shim:true,
3186                     //modal: true,
3187                 //    width: 'auto',
3188                   //  height:100,
3189                     //buttonAlign:"center",
3190                     closeClick : function(){
3191                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3192                             handleButton("no");
3193                         }else{
3194                             handleButton("cancel");
3195                         }
3196                     }
3197                 });
3198                 dlg.render();
3199                 dlg.on("hide", handleHide);
3200                 mask = dlg.mask;
3201                 //dlg.addKeyListener(27, handleEsc);
3202                 buttons = {};
3203                 this.buttons = buttons;
3204                 var bt = this.buttonText;
3205                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3206                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3207                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3208                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3209                 //Roo.log(buttons);
3210                 bodyEl = dlg.bodyEl.createChild({
3211
3212                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3213                         '<textarea class="roo-mb-textarea"></textarea>' +
3214                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3215                 });
3216                 msgEl = bodyEl.dom.firstChild;
3217                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3218                 textboxEl.enableDisplayMode();
3219                 textboxEl.addKeyListener([10,13], function(){
3220                     if(dlg.isVisible() && opt && opt.buttons){
3221                         if(opt.buttons.ok){
3222                             handleButton("ok");
3223                         }else if(opt.buttons.yes){
3224                             handleButton("yes");
3225                         }
3226                     }
3227                 });
3228                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3229                 textareaEl.enableDisplayMode();
3230                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3231                 progressEl.enableDisplayMode();
3232                 
3233                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3234                 var pf = progressEl.dom.firstChild;
3235                 if (pf) {
3236                     pp = Roo.get(pf.firstChild);
3237                     pp.setHeight(pf.offsetHeight);
3238                 }
3239                 
3240             }
3241             return dlg;
3242         },
3243
3244         /**
3245          * Updates the message box body text
3246          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3247          * the XHTML-compliant non-breaking space character '&amp;#160;')
3248          * @return {Roo.MessageBox} This message box
3249          */
3250         updateText : function(text)
3251         {
3252             if(!dlg.isVisible() && !opt.width){
3253                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3254                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3255             }
3256             msgEl.innerHTML = text || '&#160;';
3257       
3258             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3259             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3260             var w = Math.max(
3261                     Math.min(opt.width || cw , this.maxWidth), 
3262                     Math.max(opt.minWidth || this.minWidth, bwidth)
3263             );
3264             if(opt.prompt){
3265                 activeTextEl.setWidth(w);
3266             }
3267             if(dlg.isVisible()){
3268                 dlg.fixedcenter = false;
3269             }
3270             // to big, make it scroll. = But as usual stupid IE does not support
3271             // !important..
3272             
3273             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3274                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3275                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3276             } else {
3277                 bodyEl.dom.style.height = '';
3278                 bodyEl.dom.style.overflowY = '';
3279             }
3280             if (cw > w) {
3281                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3282             } else {
3283                 bodyEl.dom.style.overflowX = '';
3284             }
3285             
3286             dlg.setContentSize(w, bodyEl.getHeight());
3287             if(dlg.isVisible()){
3288                 dlg.fixedcenter = true;
3289             }
3290             return this;
3291         },
3292
3293         /**
3294          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3295          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3296          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3297          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3298          * @return {Roo.MessageBox} This message box
3299          */
3300         updateProgress : function(value, text){
3301             if(text){
3302                 this.updateText(text);
3303             }
3304             
3305             if (pp) { // weird bug on my firefox - for some reason this is not defined
3306                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3307                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3308             }
3309             return this;
3310         },        
3311
3312         /**
3313          * Returns true if the message box is currently displayed
3314          * @return {Boolean} True if the message box is visible, else false
3315          */
3316         isVisible : function(){
3317             return dlg && dlg.isVisible();  
3318         },
3319
3320         /**
3321          * Hides the message box if it is displayed
3322          */
3323         hide : function(){
3324             if(this.isVisible()){
3325                 dlg.hide();
3326             }  
3327         },
3328
3329         /**
3330          * Displays a new message box, or reinitializes an existing message box, based on the config options
3331          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3332          * The following config object properties are supported:
3333          * <pre>
3334 Property    Type             Description
3335 ----------  ---------------  ------------------------------------------------------------------------------------
3336 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3337                                    closes (defaults to undefined)
3338 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3339                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3340 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3341                                    progress and wait dialogs will ignore this property and always hide the
3342                                    close button as they can only be closed programmatically.
3343 cls               String           A custom CSS class to apply to the message box element
3344 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3345                                    displayed (defaults to 75)
3346 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3347                                    function will be btn (the name of the button that was clicked, if applicable,
3348                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3349                                    Progress and wait dialogs will ignore this option since they do not respond to
3350                                    user actions and can only be closed programmatically, so any required function
3351                                    should be called by the same code after it closes the dialog.
3352 icon              String           A CSS class that provides a background image to be used as an icon for
3353                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3354 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3355 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3356 modal             Boolean          False to allow user interaction with the page while the message box is
3357                                    displayed (defaults to true)
3358 msg               String           A string that will replace the existing message box body text (defaults
3359                                    to the XHTML-compliant non-breaking space character '&#160;')
3360 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3361 progress          Boolean          True to display a progress bar (defaults to false)
3362 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3363 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3364 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3365 title             String           The title text
3366 value             String           The string value to set into the active textbox element if displayed
3367 wait              Boolean          True to display a progress bar (defaults to false)
3368 width             Number           The width of the dialog in pixels
3369 </pre>
3370          *
3371          * Example usage:
3372          * <pre><code>
3373 Roo.Msg.show({
3374    title: 'Address',
3375    msg: 'Please enter your address:',
3376    width: 300,
3377    buttons: Roo.MessageBox.OKCANCEL,
3378    multiline: true,
3379    fn: saveAddress,
3380    animEl: 'addAddressBtn'
3381 });
3382 </code></pre>
3383          * @param {Object} config Configuration options
3384          * @return {Roo.MessageBox} This message box
3385          */
3386         show : function(options)
3387         {
3388             
3389             // this causes nightmares if you show one dialog after another
3390             // especially on callbacks..
3391              
3392             if(this.isVisible()){
3393                 
3394                 this.hide();
3395                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3396                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3397                 Roo.log("New Dialog Message:" +  options.msg )
3398                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3399                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3400                 
3401             }
3402             var d = this.getDialog();
3403             opt = options;
3404             d.setTitle(opt.title || "&#160;");
3405             d.closeEl.setDisplayed(opt.closable !== false);
3406             activeTextEl = textboxEl;
3407             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3408             if(opt.prompt){
3409                 if(opt.multiline){
3410                     textboxEl.hide();
3411                     textareaEl.show();
3412                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3413                         opt.multiline : this.defaultTextHeight);
3414                     activeTextEl = textareaEl;
3415                 }else{
3416                     textboxEl.show();
3417                     textareaEl.hide();
3418                 }
3419             }else{
3420                 textboxEl.hide();
3421                 textareaEl.hide();
3422             }
3423             progressEl.setDisplayed(opt.progress === true);
3424             this.updateProgress(0);
3425             activeTextEl.dom.value = opt.value || "";
3426             if(opt.prompt){
3427                 dlg.setDefaultButton(activeTextEl);
3428             }else{
3429                 var bs = opt.buttons;
3430                 var db = null;
3431                 if(bs && bs.ok){
3432                     db = buttons["ok"];
3433                 }else if(bs && bs.yes){
3434                     db = buttons["yes"];
3435                 }
3436                 dlg.setDefaultButton(db);
3437             }
3438             bwidth = updateButtons(opt.buttons);
3439             this.updateText(opt.msg);
3440             if(opt.cls){
3441                 d.el.addClass(opt.cls);
3442             }
3443             d.proxyDrag = opt.proxyDrag === true;
3444             d.modal = opt.modal !== false;
3445             d.mask = opt.modal !== false ? mask : false;
3446             if(!d.isVisible()){
3447                 // force it to the end of the z-index stack so it gets a cursor in FF
3448                 document.body.appendChild(dlg.el.dom);
3449                 d.animateTarget = null;
3450                 d.show(options.animEl);
3451             }
3452             return this;
3453         },
3454
3455         /**
3456          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3457          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3458          * and closing the message box when the process is complete.
3459          * @param {String} title The title bar text
3460          * @param {String} msg The message box body text
3461          * @return {Roo.MessageBox} This message box
3462          */
3463         progress : function(title, msg){
3464             this.show({
3465                 title : title,
3466                 msg : msg,
3467                 buttons: false,
3468                 progress:true,
3469                 closable:false,
3470                 minWidth: this.minProgressWidth,
3471                 modal : true
3472             });
3473             return this;
3474         },
3475
3476         /**
3477          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3478          * If a callback function is passed it will be called after the user clicks the button, and the
3479          * id of the button that was clicked will be passed as the only parameter to the callback
3480          * (could also be the top-right close button).
3481          * @param {String} title The title bar text
3482          * @param {String} msg The message box body text
3483          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3484          * @param {Object} scope (optional) The scope of the callback function
3485          * @return {Roo.MessageBox} This message box
3486          */
3487         alert : function(title, msg, fn, scope)
3488         {
3489             this.show({
3490                 title : title,
3491                 msg : msg,
3492                 buttons: this.OK,
3493                 fn: fn,
3494                 closable : false,
3495                 scope : scope,
3496                 modal : true
3497             });
3498             return this;
3499         },
3500
3501         /**
3502          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3503          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3504          * You are responsible for closing the message box when the process is complete.
3505          * @param {String} msg The message box body text
3506          * @param {String} title (optional) The title bar text
3507          * @return {Roo.MessageBox} This message box
3508          */
3509         wait : function(msg, title){
3510             this.show({
3511                 title : title,
3512                 msg : msg,
3513                 buttons: false,
3514                 closable:false,
3515                 progress:true,
3516                 modal:true,
3517                 width:300,
3518                 wait:true
3519             });
3520             waitTimer = Roo.TaskMgr.start({
3521                 run: function(i){
3522                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3523                 },
3524                 interval: 1000
3525             });
3526             return this;
3527         },
3528
3529         /**
3530          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3531          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3532          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3533          * @param {String} title The title bar text
3534          * @param {String} msg The message box body text
3535          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3536          * @param {Object} scope (optional) The scope of the callback function
3537          * @return {Roo.MessageBox} This message box
3538          */
3539         confirm : function(title, msg, fn, scope){
3540             this.show({
3541                 title : title,
3542                 msg : msg,
3543                 buttons: this.YESNO,
3544                 fn: fn,
3545                 scope : scope,
3546                 modal : true
3547             });
3548             return this;
3549         },
3550
3551         /**
3552          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3553          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3554          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3555          * (could also be the top-right close button) and the text that was entered will be passed as the two
3556          * parameters to the callback.
3557          * @param {String} title The title bar text
3558          * @param {String} msg The message box body text
3559          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3560          * @param {Object} scope (optional) The scope of the callback function
3561          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3562          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3563          * @return {Roo.MessageBox} This message box
3564          */
3565         prompt : function(title, msg, fn, scope, multiline){
3566             this.show({
3567                 title : title,
3568                 msg : msg,
3569                 buttons: this.OKCANCEL,
3570                 fn: fn,
3571                 minWidth:250,
3572                 scope : scope,
3573                 prompt:true,
3574                 multiline: multiline,
3575                 modal : true
3576             });
3577             return this;
3578         },
3579
3580         /**
3581          * Button config that displays a single OK button
3582          * @type Object
3583          */
3584         OK : {ok:true},
3585         /**
3586          * Button config that displays Yes and No buttons
3587          * @type Object
3588          */
3589         YESNO : {yes:true, no:true},
3590         /**
3591          * Button config that displays OK and Cancel buttons
3592          * @type Object
3593          */
3594         OKCANCEL : {ok:true, cancel:true},
3595         /**
3596          * Button config that displays Yes, No and Cancel buttons
3597          * @type Object
3598          */
3599         YESNOCANCEL : {yes:true, no:true, cancel:true},
3600
3601         /**
3602          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3603          * @type Number
3604          */
3605         defaultTextHeight : 75,
3606         /**
3607          * The maximum width in pixels of the message box (defaults to 600)
3608          * @type Number
3609          */
3610         maxWidth : 600,
3611         /**
3612          * The minimum width in pixels of the message box (defaults to 100)
3613          * @type Number
3614          */
3615         minWidth : 100,
3616         /**
3617          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3618          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3619          * @type Number
3620          */
3621         minProgressWidth : 250,
3622         /**
3623          * An object containing the default button text strings that can be overriden for localized language support.
3624          * Supported properties are: ok, cancel, yes and no.
3625          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3626          * @type Object
3627          */
3628         buttonText : {
3629             ok : "OK",
3630             cancel : "Cancel",
3631             yes : "Yes",
3632             no : "No"
3633         }
3634     };
3635 }();
3636
3637 /**
3638  * Shorthand for {@link Roo.MessageBox}
3639  */
3640 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3641 Roo.Msg = Roo.Msg || Roo.MessageBox;
3642 /*
3643  * - LGPL
3644  *
3645  * navbar
3646  * 
3647  */
3648
3649 /**
3650  * @class Roo.bootstrap.Navbar
3651  * @extends Roo.bootstrap.Component
3652  * Bootstrap Navbar class
3653
3654  * @constructor
3655  * Create a new Navbar
3656  * @param {Object} config The config object
3657  */
3658
3659
3660 Roo.bootstrap.Navbar = function(config){
3661     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3662     this.addEvents({
3663         // raw events
3664         /**
3665          * @event beforetoggle
3666          * Fire before toggle the menu
3667          * @param {Roo.EventObject} e
3668          */
3669         "beforetoggle" : true
3670     });
3671 };
3672
3673 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3674     
3675     
3676    
3677     // private
3678     navItems : false,
3679     loadMask : false,
3680     
3681     
3682     getAutoCreate : function(){
3683         
3684         
3685         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3686         
3687     },
3688     
3689     initEvents :function ()
3690     {
3691         //Roo.log(this.el.select('.navbar-toggle',true));
3692         this.el.select('.navbar-toggle',true).on('click', function() {
3693             if(this.fireEvent('beforetoggle', this) !== false){
3694                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3695             }
3696             
3697         }, this);
3698         
3699         var mark = {
3700             tag: "div",
3701             cls:"x-dlg-mask"
3702         };
3703         
3704         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3705         
3706         var size = this.el.getSize();
3707         this.maskEl.setSize(size.width, size.height);
3708         this.maskEl.enableDisplayMode("block");
3709         this.maskEl.hide();
3710         
3711         if(this.loadMask){
3712             this.maskEl.show();
3713         }
3714     },
3715     
3716     
3717     getChildContainer : function()
3718     {
3719         if (this.el.select('.collapse').getCount()) {
3720             return this.el.select('.collapse',true).first();
3721         }
3722         
3723         return this.el;
3724     },
3725     
3726     mask : function()
3727     {
3728         this.maskEl.show();
3729     },
3730     
3731     unmask : function()
3732     {
3733         this.maskEl.hide();
3734     } 
3735     
3736     
3737     
3738     
3739 });
3740
3741
3742
3743  
3744
3745  /*
3746  * - LGPL
3747  *
3748  * navbar
3749  * 
3750  */
3751
3752 /**
3753  * @class Roo.bootstrap.NavSimplebar
3754  * @extends Roo.bootstrap.Navbar
3755  * Bootstrap Sidebar class
3756  *
3757  * @cfg {Boolean} inverse is inverted color
3758  * 
3759  * @cfg {String} type (nav | pills | tabs)
3760  * @cfg {Boolean} arrangement stacked | justified
3761  * @cfg {String} align (left | right) alignment
3762  * 
3763  * @cfg {Boolean} main (true|false) main nav bar? default false
3764  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3765  * 
3766  * @cfg {String} tag (header|footer|nav|div) default is nav 
3767
3768  * 
3769  * 
3770  * 
3771  * @constructor
3772  * Create a new Sidebar
3773  * @param {Object} config The config object
3774  */
3775
3776
3777 Roo.bootstrap.NavSimplebar = function(config){
3778     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3779 };
3780
3781 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3782     
3783     inverse: false,
3784     
3785     type: false,
3786     arrangement: '',
3787     align : false,
3788     
3789     
3790     
3791     main : false,
3792     
3793     
3794     tag : false,
3795     
3796     
3797     getAutoCreate : function(){
3798         
3799         
3800         var cfg = {
3801             tag : this.tag || 'div',
3802             cls : 'navbar'
3803         };
3804           
3805         
3806         cfg.cn = [
3807             {
3808                 cls: 'nav',
3809                 tag : 'ul'
3810             }
3811         ];
3812         
3813          
3814         this.type = this.type || 'nav';
3815         if (['tabs','pills'].indexOf(this.type)!==-1) {
3816             cfg.cn[0].cls += ' nav-' + this.type
3817         
3818         
3819         } else {
3820             if (this.type!=='nav') {
3821                 Roo.log('nav type must be nav/tabs/pills')
3822             }
3823             cfg.cn[0].cls += ' navbar-nav'
3824         }
3825         
3826         
3827         
3828         
3829         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3830             cfg.cn[0].cls += ' nav-' + this.arrangement;
3831         }
3832         
3833         
3834         if (this.align === 'right') {
3835             cfg.cn[0].cls += ' navbar-right';
3836         }
3837         
3838         if (this.inverse) {
3839             cfg.cls += ' navbar-inverse';
3840             
3841         }
3842         
3843         
3844         return cfg;
3845     
3846         
3847     }
3848     
3849     
3850     
3851 });
3852
3853
3854
3855  
3856
3857  
3858        /*
3859  * - LGPL
3860  *
3861  * navbar
3862  * 
3863  */
3864
3865 /**
3866  * @class Roo.bootstrap.NavHeaderbar
3867  * @extends Roo.bootstrap.NavSimplebar
3868  * Bootstrap Sidebar class
3869  *
3870  * @cfg {String} brand what is brand
3871  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3872  * @cfg {String} brand_href href of the brand
3873  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3874  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3875  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3876  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3877  * 
3878  * @constructor
3879  * Create a new Sidebar
3880  * @param {Object} config The config object
3881  */
3882
3883
3884 Roo.bootstrap.NavHeaderbar = function(config){
3885     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3886       
3887 };
3888
3889 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3890     
3891     position: '',
3892     brand: '',
3893     brand_href: false,
3894     srButton : true,
3895     autohide : false,
3896     desktopCenter : false,
3897    
3898     
3899     getAutoCreate : function(){
3900         
3901         var   cfg = {
3902             tag: this.nav || 'nav',
3903             cls: 'navbar',
3904             role: 'navigation',
3905             cn: []
3906         };
3907         
3908         var cn = cfg.cn;
3909         if (this.desktopCenter) {
3910             cn.push({cls : 'container', cn : []});
3911             cn = cn[0].cn;
3912         }
3913         
3914         if(this.srButton){
3915             cn.push({
3916                 tag: 'div',
3917                 cls: 'navbar-header',
3918                 cn: [
3919                     {
3920                         tag: 'button',
3921                         type: 'button',
3922                         cls: 'navbar-toggle',
3923                         'data-toggle': 'collapse',
3924                         cn: [
3925                             {
3926                                 tag: 'span',
3927                                 cls: 'sr-only',
3928                                 html: 'Toggle navigation'
3929                             },
3930                             {
3931                                 tag: 'span',
3932                                 cls: 'icon-bar'
3933                             },
3934                             {
3935                                 tag: 'span',
3936                                 cls: 'icon-bar'
3937                             },
3938                             {
3939                                 tag: 'span',
3940                                 cls: 'icon-bar'
3941                             }
3942                         ]
3943                     }
3944                 ]
3945             });
3946         }
3947         
3948         cn.push({
3949             tag: 'div',
3950             cls: 'collapse navbar-collapse',
3951             cn : []
3952         });
3953         
3954         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3955         
3956         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3957             cfg.cls += ' navbar-' + this.position;
3958             
3959             // tag can override this..
3960             
3961             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3962         }
3963         
3964         if (this.brand !== '') {
3965             cn[0].cn.push({
3966                 tag: 'a',
3967                 href: this.brand_href ? this.brand_href : '#',
3968                 cls: 'navbar-brand',
3969                 cn: [
3970                 this.brand
3971                 ]
3972             });
3973         }
3974         
3975         if(this.main){
3976             cfg.cls += ' main-nav';
3977         }
3978         
3979         
3980         return cfg;
3981
3982         
3983     },
3984     getHeaderChildContainer : function()
3985     {
3986         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3987             return this.el.select('.navbar-header',true).first();
3988         }
3989         
3990         return this.getChildContainer();
3991     },
3992     
3993     
3994     initEvents : function()
3995     {
3996         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3997         
3998         if (this.autohide) {
3999             
4000             var prevScroll = 0;
4001             var ft = this.el;
4002             
4003             Roo.get(document).on('scroll',function(e) {
4004                 var ns = Roo.get(document).getScroll().top;
4005                 var os = prevScroll;
4006                 prevScroll = ns;
4007                 
4008                 if(ns > os){
4009                     ft.removeClass('slideDown');
4010                     ft.addClass('slideUp');
4011                     return;
4012                 }
4013                 ft.removeClass('slideUp');
4014                 ft.addClass('slideDown');
4015                  
4016               
4017           },this);
4018         }
4019     }    
4020     
4021 });
4022
4023
4024
4025  
4026
4027  /*
4028  * - LGPL
4029  *
4030  * navbar
4031  * 
4032  */
4033
4034 /**
4035  * @class Roo.bootstrap.NavSidebar
4036  * @extends Roo.bootstrap.Navbar
4037  * Bootstrap Sidebar class
4038  * 
4039  * @constructor
4040  * Create a new Sidebar
4041  * @param {Object} config The config object
4042  */
4043
4044
4045 Roo.bootstrap.NavSidebar = function(config){
4046     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4047 };
4048
4049 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4050     
4051     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4052     
4053     getAutoCreate : function(){
4054         
4055         
4056         return  {
4057             tag: 'div',
4058             cls: 'sidebar sidebar-nav'
4059         };
4060     
4061         
4062     }
4063     
4064     
4065     
4066 });
4067
4068
4069
4070  
4071
4072  /*
4073  * - LGPL
4074  *
4075  * nav group
4076  * 
4077  */
4078
4079 /**
4080  * @class Roo.bootstrap.NavGroup
4081  * @extends Roo.bootstrap.Component
4082  * Bootstrap NavGroup class
4083  * @cfg {String} align (left|right)
4084  * @cfg {Boolean} inverse
4085  * @cfg {String} type (nav|pills|tab) default nav
4086  * @cfg {String} navId - reference Id for navbar.
4087
4088  * 
4089  * @constructor
4090  * Create a new nav group
4091  * @param {Object} config The config object
4092  */
4093
4094 Roo.bootstrap.NavGroup = function(config){
4095     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4096     this.navItems = [];
4097    
4098     Roo.bootstrap.NavGroup.register(this);
4099      this.addEvents({
4100         /**
4101              * @event changed
4102              * Fires when the active item changes
4103              * @param {Roo.bootstrap.NavGroup} this
4104              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4105              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4106          */
4107         'changed': true
4108      });
4109     
4110 };
4111
4112 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4113     
4114     align: '',
4115     inverse: false,
4116     form: false,
4117     type: 'nav',
4118     navId : '',
4119     // private
4120     
4121     navItems : false, 
4122     
4123     getAutoCreate : function()
4124     {
4125         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4126         
4127         cfg = {
4128             tag : 'ul',
4129             cls: 'nav' 
4130         };
4131         
4132         if (['tabs','pills'].indexOf(this.type)!==-1) {
4133             cfg.cls += ' nav-' + this.type
4134         } else {
4135             if (this.type!=='nav') {
4136                 Roo.log('nav type must be nav/tabs/pills')
4137             }
4138             cfg.cls += ' navbar-nav'
4139         }
4140         
4141         if (this.parent() && this.parent().sidebar) {
4142             cfg = {
4143                 tag: 'ul',
4144                 cls: 'dashboard-menu sidebar-menu'
4145             };
4146             
4147             return cfg;
4148         }
4149         
4150         if (this.form === true) {
4151             cfg = {
4152                 tag: 'form',
4153                 cls: 'navbar-form'
4154             };
4155             
4156             if (this.align === 'right') {
4157                 cfg.cls += ' navbar-right';
4158             } else {
4159                 cfg.cls += ' navbar-left';
4160             }
4161         }
4162         
4163         if (this.align === 'right') {
4164             cfg.cls += ' navbar-right';
4165         }
4166         
4167         if (this.inverse) {
4168             cfg.cls += ' navbar-inverse';
4169             
4170         }
4171         
4172         
4173         return cfg;
4174     },
4175     /**
4176     * sets the active Navigation item
4177     * @param {Roo.bootstrap.NavItem} the new current navitem
4178     */
4179     setActiveItem : function(item)
4180     {
4181         var prev = false;
4182         Roo.each(this.navItems, function(v){
4183             if (v == item) {
4184                 return ;
4185             }
4186             if (v.isActive()) {
4187                 v.setActive(false, true);
4188                 prev = v;
4189                 
4190             }
4191             
4192         });
4193
4194         item.setActive(true, true);
4195         this.fireEvent('changed', this, item, prev);
4196         
4197         
4198     },
4199     /**
4200     * gets the active Navigation item
4201     * @return {Roo.bootstrap.NavItem} the current navitem
4202     */
4203     getActive : function()
4204     {
4205         
4206         var prev = false;
4207         Roo.each(this.navItems, function(v){
4208             
4209             if (v.isActive()) {
4210                 prev = v;
4211                 
4212             }
4213             
4214         });
4215         return prev;
4216     },
4217     
4218     indexOfNav : function()
4219     {
4220         
4221         var prev = false;
4222         Roo.each(this.navItems, function(v,i){
4223             
4224             if (v.isActive()) {
4225                 prev = i;
4226                 
4227             }
4228             
4229         });
4230         return prev;
4231     },
4232     /**
4233     * adds a Navigation item
4234     * @param {Roo.bootstrap.NavItem} the navitem to add
4235     */
4236     addItem : function(cfg)
4237     {
4238         var cn = new Roo.bootstrap.NavItem(cfg);
4239         this.register(cn);
4240         cn.parentId = this.id;
4241         cn.onRender(this.el, null);
4242         return cn;
4243     },
4244     /**
4245     * register a Navigation item
4246     * @param {Roo.bootstrap.NavItem} the navitem to add
4247     */
4248     register : function(item)
4249     {
4250         this.navItems.push( item);
4251         item.navId = this.navId;
4252     
4253     },
4254     
4255     /**
4256     * clear all the Navigation item
4257     */
4258    
4259     clearAll : function()
4260     {
4261         this.navItems = [];
4262         this.el.dom.innerHTML = '';
4263     },
4264     
4265     getNavItem: function(tabId)
4266     {
4267         var ret = false;
4268         Roo.each(this.navItems, function(e) {
4269             if (e.tabId == tabId) {
4270                ret =  e;
4271                return false;
4272             }
4273             return true;
4274             
4275         });
4276         return ret;
4277     },
4278     
4279     setActiveNext : function()
4280     {
4281         var i = this.indexOfNav(this.getActive());
4282         if (i > this.navItems.length) {
4283             return;
4284         }
4285         this.setActiveItem(this.navItems[i+1]);
4286     },
4287     setActivePrev : function()
4288     {
4289         var i = this.indexOfNav(this.getActive());
4290         if (i  < 1) {
4291             return;
4292         }
4293         this.setActiveItem(this.navItems[i-1]);
4294     },
4295     clearWasActive : function(except) {
4296         Roo.each(this.navItems, function(e) {
4297             if (e.tabId != except.tabId && e.was_active) {
4298                e.was_active = false;
4299                return false;
4300             }
4301             return true;
4302             
4303         });
4304     },
4305     getWasActive : function ()
4306     {
4307         var r = false;
4308         Roo.each(this.navItems, function(e) {
4309             if (e.was_active) {
4310                r = e;
4311                return false;
4312             }
4313             return true;
4314             
4315         });
4316         return r;
4317     }
4318     
4319     
4320 });
4321
4322  
4323 Roo.apply(Roo.bootstrap.NavGroup, {
4324     
4325     groups: {},
4326      /**
4327     * register a Navigation Group
4328     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4329     */
4330     register : function(navgrp)
4331     {
4332         this.groups[navgrp.navId] = navgrp;
4333         
4334     },
4335     /**
4336     * fetch a Navigation Group based on the navigation ID
4337     * @param {string} the navgroup to add
4338     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4339     */
4340     get: function(navId) {
4341         if (typeof(this.groups[navId]) == 'undefined') {
4342             return false;
4343             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4344         }
4345         return this.groups[navId] ;
4346     }
4347     
4348     
4349     
4350 });
4351
4352  /*
4353  * - LGPL
4354  *
4355  * row
4356  * 
4357  */
4358
4359 /**
4360  * @class Roo.bootstrap.NavItem
4361  * @extends Roo.bootstrap.Component
4362  * Bootstrap Navbar.NavItem class
4363  * @cfg {String} href  link to
4364  * @cfg {String} html content of button
4365  * @cfg {String} badge text inside badge
4366  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4367  * @cfg {String} glyphicon name of glyphicon
4368  * @cfg {String} icon name of font awesome icon
4369  * @cfg {Boolean} active Is item active
4370  * @cfg {Boolean} disabled Is item disabled
4371  
4372  * @cfg {Boolean} preventDefault (true | false) default false
4373  * @cfg {String} tabId the tab that this item activates.
4374  * @cfg {String} tagtype (a|span) render as a href or span?
4375  * @cfg {Boolean} animateRef (true|false) link to element default false  
4376   
4377  * @constructor
4378  * Create a new Navbar Item
4379  * @param {Object} config The config object
4380  */
4381 Roo.bootstrap.NavItem = function(config){
4382     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4383     this.addEvents({
4384         // raw events
4385         /**
4386          * @event click
4387          * The raw click event for the entire grid.
4388          * @param {Roo.EventObject} e
4389          */
4390         "click" : true,
4391          /**
4392             * @event changed
4393             * Fires when the active item active state changes
4394             * @param {Roo.bootstrap.NavItem} this
4395             * @param {boolean} state the new state
4396              
4397          */
4398         'changed': true,
4399         /**
4400             * @event scrollto
4401             * Fires when scroll to element
4402             * @param {Roo.bootstrap.NavItem} this
4403             * @param {Object} options
4404             * @param {Roo.EventObject} e
4405              
4406          */
4407         'scrollto': true
4408     });
4409    
4410 };
4411
4412 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4413     
4414     href: false,
4415     html: '',
4416     badge: '',
4417     icon: false,
4418     glyphicon: false,
4419     active: false,
4420     preventDefault : false,
4421     tabId : false,
4422     tagtype : 'a',
4423     disabled : false,
4424     animateRef : false,
4425     was_active : false,
4426     
4427     getAutoCreate : function(){
4428          
4429         var cfg = {
4430             tag: 'li',
4431             cls: 'nav-item'
4432             
4433         };
4434         
4435         if (this.active) {
4436             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4437         }
4438         if (this.disabled) {
4439             cfg.cls += ' disabled';
4440         }
4441         
4442         if (this.href || this.html || this.glyphicon || this.icon) {
4443             cfg.cn = [
4444                 {
4445                     tag: this.tagtype,
4446                     href : this.href || "#",
4447                     html: this.html || ''
4448                 }
4449             ];
4450             
4451             if (this.icon) {
4452                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4453             }
4454
4455             if(this.glyphicon) {
4456                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4457             }
4458             
4459             if (this.menu) {
4460                 
4461                 cfg.cn[0].html += " <span class='caret'></span>";
4462              
4463             }
4464             
4465             if (this.badge !== '') {
4466                  
4467                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4468             }
4469         }
4470         
4471         
4472         
4473         return cfg;
4474     },
4475     initEvents: function() 
4476     {
4477         if (typeof (this.menu) != 'undefined') {
4478             this.menu.parentType = this.xtype;
4479             this.menu.triggerEl = this.el;
4480             this.menu = this.addxtype(Roo.apply({}, this.menu));
4481         }
4482         
4483         this.el.select('a',true).on('click', this.onClick, this);
4484         
4485         if(this.tagtype == 'span'){
4486             this.el.select('span',true).on('click', this.onClick, this);
4487         }
4488        
4489         // at this point parent should be available..
4490         this.parent().register(this);
4491     },
4492     
4493     onClick : function(e)
4494     {
4495         if (e.getTarget('.dropdown-menu-item')) {
4496             // did you click on a menu itemm.... - then don't trigger onclick..
4497             return;
4498         }
4499         
4500         if(
4501                 this.preventDefault || 
4502                 this.href == '#' 
4503         ){
4504             Roo.log("NavItem - prevent Default?");
4505             e.preventDefault();
4506         }
4507         
4508         if (this.disabled) {
4509             return;
4510         }
4511         
4512         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4513         if (tg && tg.transition) {
4514             Roo.log("waiting for the transitionend");
4515             return;
4516         }
4517         
4518         
4519         
4520         //Roo.log("fire event clicked");
4521         if(this.fireEvent('click', this, e) === false){
4522             return;
4523         };
4524         
4525         if(this.tagtype == 'span'){
4526             return;
4527         }
4528         
4529         //Roo.log(this.href);
4530         var ael = this.el.select('a',true).first();
4531         //Roo.log(ael);
4532         
4533         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4534             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4535             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4536                 return; // ignore... - it's a 'hash' to another page.
4537             }
4538             Roo.log("NavItem - prevent Default?");
4539             e.preventDefault();
4540             this.scrollToElement(e);
4541         }
4542         
4543         
4544         var p =  this.parent();
4545    
4546         if (['tabs','pills'].indexOf(p.type)!==-1) {
4547             if (typeof(p.setActiveItem) !== 'undefined') {
4548                 p.setActiveItem(this);
4549             }
4550         }
4551         
4552         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4553         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4554             // remove the collapsed menu expand...
4555             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4556         }
4557     },
4558     
4559     isActive: function () {
4560         return this.active
4561     },
4562     setActive : function(state, fire, is_was_active)
4563     {
4564         if (this.active && !state && this.navId) {
4565             this.was_active = true;
4566             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4567             if (nv) {
4568                 nv.clearWasActive(this);
4569             }
4570             
4571         }
4572         this.active = state;
4573         
4574         if (!state ) {
4575             this.el.removeClass('active');
4576         } else if (!this.el.hasClass('active')) {
4577             this.el.addClass('active');
4578         }
4579         if (fire) {
4580             this.fireEvent('changed', this, state);
4581         }
4582         
4583         // show a panel if it's registered and related..
4584         
4585         if (!this.navId || !this.tabId || !state || is_was_active) {
4586             return;
4587         }
4588         
4589         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4590         if (!tg) {
4591             return;
4592         }
4593         var pan = tg.getPanelByName(this.tabId);
4594         if (!pan) {
4595             return;
4596         }
4597         // if we can not flip to new panel - go back to old nav highlight..
4598         if (false == tg.showPanel(pan)) {
4599             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4600             if (nv) {
4601                 var onav = nv.getWasActive();
4602                 if (onav) {
4603                     onav.setActive(true, false, true);
4604                 }
4605             }
4606             
4607         }
4608         
4609         
4610         
4611     },
4612      // this should not be here...
4613     setDisabled : function(state)
4614     {
4615         this.disabled = state;
4616         if (!state ) {
4617             this.el.removeClass('disabled');
4618         } else if (!this.el.hasClass('disabled')) {
4619             this.el.addClass('disabled');
4620         }
4621         
4622     },
4623     
4624     /**
4625      * Fetch the element to display the tooltip on.
4626      * @return {Roo.Element} defaults to this.el
4627      */
4628     tooltipEl : function()
4629     {
4630         return this.el.select('' + this.tagtype + '', true).first();
4631     },
4632     
4633     scrollToElement : function(e)
4634     {
4635         var c = document.body;
4636         
4637         /*
4638          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4639          */
4640         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4641             c = document.documentElement;
4642         }
4643         
4644         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4645         
4646         if(!target){
4647             return;
4648         }
4649
4650         var o = target.calcOffsetsTo(c);
4651         
4652         var options = {
4653             target : target,
4654             value : o[1]
4655         };
4656         
4657         this.fireEvent('scrollto', this, options, e);
4658         
4659         Roo.get(c).scrollTo('top', options.value, true);
4660         
4661         return;
4662     }
4663 });
4664  
4665
4666  /*
4667  * - LGPL
4668  *
4669  * sidebar item
4670  *
4671  *  li
4672  *    <span> icon </span>
4673  *    <span> text </span>
4674  *    <span>badge </span>
4675  */
4676
4677 /**
4678  * @class Roo.bootstrap.NavSidebarItem
4679  * @extends Roo.bootstrap.NavItem
4680  * Bootstrap Navbar.NavSidebarItem class
4681  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4682  * {Boolean} open is the menu open
4683  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4684  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4685  * {String} buttonSize (sm|md|lg)the extra classes for the button
4686  * {Boolean} showArrow show arrow next to the text (default true)
4687  * @constructor
4688  * Create a new Navbar Button
4689  * @param {Object} config The config object
4690  */
4691 Roo.bootstrap.NavSidebarItem = function(config){
4692     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4693     this.addEvents({
4694         // raw events
4695         /**
4696          * @event click
4697          * The raw click event for the entire grid.
4698          * @param {Roo.EventObject} e
4699          */
4700         "click" : true,
4701          /**
4702             * @event changed
4703             * Fires when the active item active state changes
4704             * @param {Roo.bootstrap.NavSidebarItem} this
4705             * @param {boolean} state the new state
4706              
4707          */
4708         'changed': true
4709     });
4710    
4711 };
4712
4713 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4714     
4715     badgeWeight : 'default',
4716     
4717     open: false,
4718     
4719     buttonView : false,
4720     
4721     buttonWeight : 'default',
4722     
4723     buttonSize : 'md',
4724     
4725     showArrow : true,
4726     
4727     getAutoCreate : function(){
4728         
4729         
4730         var a = {
4731                 tag: 'a',
4732                 href : this.href || '#',
4733                 cls: '',
4734                 html : '',
4735                 cn : []
4736         };
4737         
4738         if(this.buttonView){
4739             a = {
4740                 tag: 'button',
4741                 href : this.href || '#',
4742                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4743                 html : this.html,
4744                 cn : []
4745             };
4746         }
4747         
4748         var cfg = {
4749             tag: 'li',
4750             cls: '',
4751             cn: [ a ]
4752         };
4753         
4754         if (this.active) {
4755             cfg.cls += ' active';
4756         }
4757         
4758         if (this.disabled) {
4759             cfg.cls += ' disabled';
4760         }
4761         if (this.open) {
4762             cfg.cls += ' open x-open';
4763         }
4764         // left icon..
4765         if (this.glyphicon || this.icon) {
4766             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4767             a.cn.push({ tag : 'i', cls : c }) ;
4768         }
4769         
4770         if(!this.buttonView){
4771             var span = {
4772                 tag: 'span',
4773                 html : this.html || ''
4774             };
4775
4776             a.cn.push(span);
4777             
4778         }
4779         
4780         if (this.badge !== '') {
4781             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4782         }
4783         
4784         if (this.menu) {
4785             
4786             if(this.showArrow){
4787                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4788             }
4789             
4790             a.cls += ' dropdown-toggle treeview' ;
4791         }
4792         
4793         return cfg;
4794     },
4795     
4796     initEvents : function()
4797     { 
4798         if (typeof (this.menu) != 'undefined') {
4799             this.menu.parentType = this.xtype;
4800             this.menu.triggerEl = this.el;
4801             this.menu = this.addxtype(Roo.apply({}, this.menu));
4802         }
4803         
4804         this.el.on('click', this.onClick, this);
4805         
4806         if(this.badge !== ''){
4807             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4808         }
4809         
4810     },
4811     
4812     onClick : function(e)
4813     {
4814         if(this.disabled){
4815             e.preventDefault();
4816             return;
4817         }
4818         
4819         if(this.preventDefault){
4820             e.preventDefault();
4821         }
4822         
4823         this.fireEvent('click', this);
4824     },
4825     
4826     disable : function()
4827     {
4828         this.setDisabled(true);
4829     },
4830     
4831     enable : function()
4832     {
4833         this.setDisabled(false);
4834     },
4835     
4836     setDisabled : function(state)
4837     {
4838         if(this.disabled == state){
4839             return;
4840         }
4841         
4842         this.disabled = state;
4843         
4844         if (state) {
4845             this.el.addClass('disabled');
4846             return;
4847         }
4848         
4849         this.el.removeClass('disabled');
4850         
4851         return;
4852     },
4853     
4854     setActive : function(state)
4855     {
4856         if(this.active == state){
4857             return;
4858         }
4859         
4860         this.active = state;
4861         
4862         if (state) {
4863             this.el.addClass('active');
4864             return;
4865         }
4866         
4867         this.el.removeClass('active');
4868         
4869         return;
4870     },
4871     
4872     isActive: function () 
4873     {
4874         return this.active;
4875     },
4876     
4877     setBadge : function(str)
4878     {
4879         if(!this.badgeEl){
4880             return;
4881         }
4882         
4883         this.badgeEl.dom.innerHTML = str;
4884     }
4885     
4886    
4887      
4888  
4889 });
4890  
4891
4892  /*
4893  * - LGPL
4894  *
4895  * row
4896  * 
4897  */
4898
4899 /**
4900  * @class Roo.bootstrap.Row
4901  * @extends Roo.bootstrap.Component
4902  * Bootstrap Row class (contains columns...)
4903  * 
4904  * @constructor
4905  * Create a new Row
4906  * @param {Object} config The config object
4907  */
4908
4909 Roo.bootstrap.Row = function(config){
4910     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4911 };
4912
4913 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4914     
4915     getAutoCreate : function(){
4916        return {
4917             cls: 'row clearfix'
4918        };
4919     }
4920     
4921     
4922 });
4923
4924  
4925
4926  /*
4927  * - LGPL
4928  *
4929  * element
4930  * 
4931  */
4932
4933 /**
4934  * @class Roo.bootstrap.Element
4935  * @extends Roo.bootstrap.Component
4936  * Bootstrap Element class
4937  * @cfg {String} html contents of the element
4938  * @cfg {String} tag tag of the element
4939  * @cfg {String} cls class of the element
4940  * @cfg {Boolean} preventDefault (true|false) default false
4941  * @cfg {Boolean} clickable (true|false) default false
4942  * 
4943  * @constructor
4944  * Create a new Element
4945  * @param {Object} config The config object
4946  */
4947
4948 Roo.bootstrap.Element = function(config){
4949     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4950     
4951     this.addEvents({
4952         // raw events
4953         /**
4954          * @event click
4955          * When a element is chick
4956          * @param {Roo.bootstrap.Element} this
4957          * @param {Roo.EventObject} e
4958          */
4959         "click" : true
4960     });
4961 };
4962
4963 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4964     
4965     tag: 'div',
4966     cls: '',
4967     html: '',
4968     preventDefault: false, 
4969     clickable: false,
4970     
4971     getAutoCreate : function(){
4972         
4973         var cfg = {
4974             tag: this.tag,
4975             // cls: this.cls, double assign in parent class Component.js :: onRender
4976             html: this.html
4977         };
4978         
4979         return cfg;
4980     },
4981     
4982     initEvents: function() 
4983     {
4984         Roo.bootstrap.Element.superclass.initEvents.call(this);
4985         
4986         if(this.clickable){
4987             this.el.on('click', this.onClick, this);
4988         }
4989         
4990     },
4991     
4992     onClick : function(e)
4993     {
4994         if(this.preventDefault){
4995             e.preventDefault();
4996         }
4997         
4998         this.fireEvent('click', this, e);
4999     },
5000     
5001     getValue : function()
5002     {
5003         return this.el.dom.innerHTML;
5004     },
5005     
5006     setValue : function(value)
5007     {
5008         this.el.dom.innerHTML = value;
5009     }
5010    
5011 });
5012
5013  
5014
5015  /*
5016  * - LGPL
5017  *
5018  * pagination
5019  * 
5020  */
5021
5022 /**
5023  * @class Roo.bootstrap.Pagination
5024  * @extends Roo.bootstrap.Component
5025  * Bootstrap Pagination class
5026  * @cfg {String} size xs | sm | md | lg
5027  * @cfg {Boolean} inverse false | true
5028  * 
5029  * @constructor
5030  * Create a new Pagination
5031  * @param {Object} config The config object
5032  */
5033
5034 Roo.bootstrap.Pagination = function(config){
5035     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5036 };
5037
5038 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5039     
5040     cls: false,
5041     size: false,
5042     inverse: false,
5043     
5044     getAutoCreate : function(){
5045         var cfg = {
5046             tag: 'ul',
5047                 cls: 'pagination'
5048         };
5049         if (this.inverse) {
5050             cfg.cls += ' inverse';
5051         }
5052         if (this.html) {
5053             cfg.html=this.html;
5054         }
5055         if (this.cls) {
5056             cfg.cls += " " + this.cls;
5057         }
5058         return cfg;
5059     }
5060    
5061 });
5062
5063  
5064
5065  /*
5066  * - LGPL
5067  *
5068  * Pagination item
5069  * 
5070  */
5071
5072
5073 /**
5074  * @class Roo.bootstrap.PaginationItem
5075  * @extends Roo.bootstrap.Component
5076  * Bootstrap PaginationItem class
5077  * @cfg {String} html text
5078  * @cfg {String} href the link
5079  * @cfg {Boolean} preventDefault (true | false) default true
5080  * @cfg {Boolean} active (true | false) default false
5081  * @cfg {Boolean} disabled default false
5082  * 
5083  * 
5084  * @constructor
5085  * Create a new PaginationItem
5086  * @param {Object} config The config object
5087  */
5088
5089
5090 Roo.bootstrap.PaginationItem = function(config){
5091     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5092     this.addEvents({
5093         // raw events
5094         /**
5095          * @event click
5096          * The raw click event for the entire grid.
5097          * @param {Roo.EventObject} e
5098          */
5099         "click" : true
5100     });
5101 };
5102
5103 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5104     
5105     href : false,
5106     html : false,
5107     preventDefault: true,
5108     active : false,
5109     cls : false,
5110     disabled: false,
5111     
5112     getAutoCreate : function(){
5113         var cfg= {
5114             tag: 'li',
5115             cn: [
5116                 {
5117                     tag : 'a',
5118                     href : this.href ? this.href : '#',
5119                     html : this.html ? this.html : ''
5120                 }
5121             ]
5122         };
5123         
5124         if(this.cls){
5125             cfg.cls = this.cls;
5126         }
5127         
5128         if(this.disabled){
5129             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5130         }
5131         
5132         if(this.active){
5133             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5134         }
5135         
5136         return cfg;
5137     },
5138     
5139     initEvents: function() {
5140         
5141         this.el.on('click', this.onClick, this);
5142         
5143     },
5144     onClick : function(e)
5145     {
5146         Roo.log('PaginationItem on click ');
5147         if(this.preventDefault){
5148             e.preventDefault();
5149         }
5150         
5151         if(this.disabled){
5152             return;
5153         }
5154         
5155         this.fireEvent('click', this, e);
5156     }
5157    
5158 });
5159
5160  
5161
5162  /*
5163  * - LGPL
5164  *
5165  * slider
5166  * 
5167  */
5168
5169
5170 /**
5171  * @class Roo.bootstrap.Slider
5172  * @extends Roo.bootstrap.Component
5173  * Bootstrap Slider class
5174  *    
5175  * @constructor
5176  * Create a new Slider
5177  * @param {Object} config The config object
5178  */
5179
5180 Roo.bootstrap.Slider = function(config){
5181     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5182 };
5183
5184 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5185     
5186     getAutoCreate : function(){
5187         
5188         var cfg = {
5189             tag: 'div',
5190             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5191             cn: [
5192                 {
5193                     tag: 'a',
5194                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5195                 }
5196             ]
5197         };
5198         
5199         return cfg;
5200     }
5201    
5202 });
5203
5204  /*
5205  * Based on:
5206  * Ext JS Library 1.1.1
5207  * Copyright(c) 2006-2007, Ext JS, LLC.
5208  *
5209  * Originally Released Under LGPL - original licence link has changed is not relivant.
5210  *
5211  * Fork - LGPL
5212  * <script type="text/javascript">
5213  */
5214  
5215
5216 /**
5217  * @class Roo.grid.ColumnModel
5218  * @extends Roo.util.Observable
5219  * This is the default implementation of a ColumnModel used by the Grid. It defines
5220  * the columns in the grid.
5221  * <br>Usage:<br>
5222  <pre><code>
5223  var colModel = new Roo.grid.ColumnModel([
5224         {header: "Ticker", width: 60, sortable: true, locked: true},
5225         {header: "Company Name", width: 150, sortable: true},
5226         {header: "Market Cap.", width: 100, sortable: true},
5227         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5228         {header: "Employees", width: 100, sortable: true, resizable: false}
5229  ]);
5230  </code></pre>
5231  * <p>
5232  
5233  * The config options listed for this class are options which may appear in each
5234  * individual column definition.
5235  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5236  * @constructor
5237  * @param {Object} config An Array of column config objects. See this class's
5238  * config objects for details.
5239 */
5240 Roo.grid.ColumnModel = function(config){
5241         /**
5242      * The config passed into the constructor
5243      */
5244     this.config = config;
5245     this.lookup = {};
5246
5247     // if no id, create one
5248     // if the column does not have a dataIndex mapping,
5249     // map it to the order it is in the config
5250     for(var i = 0, len = config.length; i < len; i++){
5251         var c = config[i];
5252         if(typeof c.dataIndex == "undefined"){
5253             c.dataIndex = i;
5254         }
5255         if(typeof c.renderer == "string"){
5256             c.renderer = Roo.util.Format[c.renderer];
5257         }
5258         if(typeof c.id == "undefined"){
5259             c.id = Roo.id();
5260         }
5261         if(c.editor && c.editor.xtype){
5262             c.editor  = Roo.factory(c.editor, Roo.grid);
5263         }
5264         if(c.editor && c.editor.isFormField){
5265             c.editor = new Roo.grid.GridEditor(c.editor);
5266         }
5267         this.lookup[c.id] = c;
5268     }
5269
5270     /**
5271      * The width of columns which have no width specified (defaults to 100)
5272      * @type Number
5273      */
5274     this.defaultWidth = 100;
5275
5276     /**
5277      * Default sortable of columns which have no sortable specified (defaults to false)
5278      * @type Boolean
5279      */
5280     this.defaultSortable = false;
5281
5282     this.addEvents({
5283         /**
5284              * @event widthchange
5285              * Fires when the width of a column changes.
5286              * @param {ColumnModel} this
5287              * @param {Number} columnIndex The column index
5288              * @param {Number} newWidth The new width
5289              */
5290             "widthchange": true,
5291         /**
5292              * @event headerchange
5293              * Fires when the text of a header changes.
5294              * @param {ColumnModel} this
5295              * @param {Number} columnIndex The column index
5296              * @param {Number} newText The new header text
5297              */
5298             "headerchange": true,
5299         /**
5300              * @event hiddenchange
5301              * Fires when a column is hidden or "unhidden".
5302              * @param {ColumnModel} this
5303              * @param {Number} columnIndex The column index
5304              * @param {Boolean} hidden true if hidden, false otherwise
5305              */
5306             "hiddenchange": true,
5307             /**
5308          * @event columnmoved
5309          * Fires when a column is moved.
5310          * @param {ColumnModel} this
5311          * @param {Number} oldIndex
5312          * @param {Number} newIndex
5313          */
5314         "columnmoved" : true,
5315         /**
5316          * @event columlockchange
5317          * Fires when a column's locked state is changed
5318          * @param {ColumnModel} this
5319          * @param {Number} colIndex
5320          * @param {Boolean} locked true if locked
5321          */
5322         "columnlockchange" : true
5323     });
5324     Roo.grid.ColumnModel.superclass.constructor.call(this);
5325 };
5326 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5327     /**
5328      * @cfg {String} header The header text to display in the Grid view.
5329      */
5330     /**
5331      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5332      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5333      * specified, the column's index is used as an index into the Record's data Array.
5334      */
5335     /**
5336      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5337      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5338      */
5339     /**
5340      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5341      * Defaults to the value of the {@link #defaultSortable} property.
5342      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5343      */
5344     /**
5345      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5346      */
5347     /**
5348      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5349      */
5350     /**
5351      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5352      */
5353     /**
5354      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5355      */
5356     /**
5357      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5358      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5359      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5360      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5361      */
5362        /**
5363      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5364      */
5365     /**
5366      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5367      */
5368     /**
5369      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5370      */
5371     /**
5372      * @cfg {String} cursor (Optional)
5373      */
5374     /**
5375      * @cfg {String} tooltip (Optional)
5376      */
5377     /**
5378      * @cfg {Number} xs (Optional)
5379      */
5380     /**
5381      * @cfg {Number} sm (Optional)
5382      */
5383     /**
5384      * @cfg {Number} md (Optional)
5385      */
5386     /**
5387      * @cfg {Number} lg (Optional)
5388      */
5389     /**
5390      * Returns the id of the column at the specified index.
5391      * @param {Number} index The column index
5392      * @return {String} the id
5393      */
5394     getColumnId : function(index){
5395         return this.config[index].id;
5396     },
5397
5398     /**
5399      * Returns the column for a specified id.
5400      * @param {String} id The column id
5401      * @return {Object} the column
5402      */
5403     getColumnById : function(id){
5404         return this.lookup[id];
5405     },
5406
5407     
5408     /**
5409      * Returns the column for a specified dataIndex.
5410      * @param {String} dataIndex The column dataIndex
5411      * @return {Object|Boolean} the column or false if not found
5412      */
5413     getColumnByDataIndex: function(dataIndex){
5414         var index = this.findColumnIndex(dataIndex);
5415         return index > -1 ? this.config[index] : false;
5416     },
5417     
5418     /**
5419      * Returns the index for a specified column id.
5420      * @param {String} id The column id
5421      * @return {Number} the index, or -1 if not found
5422      */
5423     getIndexById : function(id){
5424         for(var i = 0, len = this.config.length; i < len; i++){
5425             if(this.config[i].id == id){
5426                 return i;
5427             }
5428         }
5429         return -1;
5430     },
5431     
5432     /**
5433      * Returns the index for a specified column dataIndex.
5434      * @param {String} dataIndex The column dataIndex
5435      * @return {Number} the index, or -1 if not found
5436      */
5437     
5438     findColumnIndex : function(dataIndex){
5439         for(var i = 0, len = this.config.length; i < len; i++){
5440             if(this.config[i].dataIndex == dataIndex){
5441                 return i;
5442             }
5443         }
5444         return -1;
5445     },
5446     
5447     
5448     moveColumn : function(oldIndex, newIndex){
5449         var c = this.config[oldIndex];
5450         this.config.splice(oldIndex, 1);
5451         this.config.splice(newIndex, 0, c);
5452         this.dataMap = null;
5453         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5454     },
5455
5456     isLocked : function(colIndex){
5457         return this.config[colIndex].locked === true;
5458     },
5459
5460     setLocked : function(colIndex, value, suppressEvent){
5461         if(this.isLocked(colIndex) == value){
5462             return;
5463         }
5464         this.config[colIndex].locked = value;
5465         if(!suppressEvent){
5466             this.fireEvent("columnlockchange", this, colIndex, value);
5467         }
5468     },
5469
5470     getTotalLockedWidth : function(){
5471         var totalWidth = 0;
5472         for(var i = 0; i < this.config.length; i++){
5473             if(this.isLocked(i) && !this.isHidden(i)){
5474                 this.totalWidth += this.getColumnWidth(i);
5475             }
5476         }
5477         return totalWidth;
5478     },
5479
5480     getLockedCount : function(){
5481         for(var i = 0, len = this.config.length; i < len; i++){
5482             if(!this.isLocked(i)){
5483                 return i;
5484             }
5485         }
5486         
5487         return this.config.length;
5488     },
5489
5490     /**
5491      * Returns the number of columns.
5492      * @return {Number}
5493      */
5494     getColumnCount : function(visibleOnly){
5495         if(visibleOnly === true){
5496             var c = 0;
5497             for(var i = 0, len = this.config.length; i < len; i++){
5498                 if(!this.isHidden(i)){
5499                     c++;
5500                 }
5501             }
5502             return c;
5503         }
5504         return this.config.length;
5505     },
5506
5507     /**
5508      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5509      * @param {Function} fn
5510      * @param {Object} scope (optional)
5511      * @return {Array} result
5512      */
5513     getColumnsBy : function(fn, scope){
5514         var r = [];
5515         for(var i = 0, len = this.config.length; i < len; i++){
5516             var c = this.config[i];
5517             if(fn.call(scope||this, c, i) === true){
5518                 r[r.length] = c;
5519             }
5520         }
5521         return r;
5522     },
5523
5524     /**
5525      * Returns true if the specified column is sortable.
5526      * @param {Number} col The column index
5527      * @return {Boolean}
5528      */
5529     isSortable : function(col){
5530         if(typeof this.config[col].sortable == "undefined"){
5531             return this.defaultSortable;
5532         }
5533         return this.config[col].sortable;
5534     },
5535
5536     /**
5537      * Returns the rendering (formatting) function defined for the column.
5538      * @param {Number} col The column index.
5539      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5540      */
5541     getRenderer : function(col){
5542         if(!this.config[col].renderer){
5543             return Roo.grid.ColumnModel.defaultRenderer;
5544         }
5545         return this.config[col].renderer;
5546     },
5547
5548     /**
5549      * Sets the rendering (formatting) function for a column.
5550      * @param {Number} col The column index
5551      * @param {Function} fn The function to use to process the cell's raw data
5552      * to return HTML markup for the grid view. The render function is called with
5553      * the following parameters:<ul>
5554      * <li>Data value.</li>
5555      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5556      * <li>css A CSS style string to apply to the table cell.</li>
5557      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5558      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5559      * <li>Row index</li>
5560      * <li>Column index</li>
5561      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5562      */
5563     setRenderer : function(col, fn){
5564         this.config[col].renderer = fn;
5565     },
5566
5567     /**
5568      * Returns the width for the specified column.
5569      * @param {Number} col The column index
5570      * @return {Number}
5571      */
5572     getColumnWidth : function(col){
5573         return this.config[col].width * 1 || this.defaultWidth;
5574     },
5575
5576     /**
5577      * Sets the width for a column.
5578      * @param {Number} col The column index
5579      * @param {Number} width The new width
5580      */
5581     setColumnWidth : function(col, width, suppressEvent){
5582         this.config[col].width = width;
5583         this.totalWidth = null;
5584         if(!suppressEvent){
5585              this.fireEvent("widthchange", this, col, width);
5586         }
5587     },
5588
5589     /**
5590      * Returns the total width of all columns.
5591      * @param {Boolean} includeHidden True to include hidden column widths
5592      * @return {Number}
5593      */
5594     getTotalWidth : function(includeHidden){
5595         if(!this.totalWidth){
5596             this.totalWidth = 0;
5597             for(var i = 0, len = this.config.length; i < len; i++){
5598                 if(includeHidden || !this.isHidden(i)){
5599                     this.totalWidth += this.getColumnWidth(i);
5600                 }
5601             }
5602         }
5603         return this.totalWidth;
5604     },
5605
5606     /**
5607      * Returns the header for the specified column.
5608      * @param {Number} col The column index
5609      * @return {String}
5610      */
5611     getColumnHeader : function(col){
5612         return this.config[col].header;
5613     },
5614
5615     /**
5616      * Sets the header for a column.
5617      * @param {Number} col The column index
5618      * @param {String} header The new header
5619      */
5620     setColumnHeader : function(col, header){
5621         this.config[col].header = header;
5622         this.fireEvent("headerchange", this, col, header);
5623     },
5624
5625     /**
5626      * Returns the tooltip for the specified column.
5627      * @param {Number} col The column index
5628      * @return {String}
5629      */
5630     getColumnTooltip : function(col){
5631             return this.config[col].tooltip;
5632     },
5633     /**
5634      * Sets the tooltip for a column.
5635      * @param {Number} col The column index
5636      * @param {String} tooltip The new tooltip
5637      */
5638     setColumnTooltip : function(col, tooltip){
5639             this.config[col].tooltip = tooltip;
5640     },
5641
5642     /**
5643      * Returns the dataIndex for the specified column.
5644      * @param {Number} col The column index
5645      * @return {Number}
5646      */
5647     getDataIndex : function(col){
5648         return this.config[col].dataIndex;
5649     },
5650
5651     /**
5652      * Sets the dataIndex for a column.
5653      * @param {Number} col The column index
5654      * @param {Number} dataIndex The new dataIndex
5655      */
5656     setDataIndex : function(col, dataIndex){
5657         this.config[col].dataIndex = dataIndex;
5658     },
5659
5660     
5661     
5662     /**
5663      * Returns true if the cell is editable.
5664      * @param {Number} colIndex The column index
5665      * @param {Number} rowIndex The row index - this is nto actually used..?
5666      * @return {Boolean}
5667      */
5668     isCellEditable : function(colIndex, rowIndex){
5669         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5670     },
5671
5672     /**
5673      * Returns the editor defined for the cell/column.
5674      * return false or null to disable editing.
5675      * @param {Number} colIndex The column index
5676      * @param {Number} rowIndex The row index
5677      * @return {Object}
5678      */
5679     getCellEditor : function(colIndex, rowIndex){
5680         return this.config[colIndex].editor;
5681     },
5682
5683     /**
5684      * Sets if a column is editable.
5685      * @param {Number} col The column index
5686      * @param {Boolean} editable True if the column is editable
5687      */
5688     setEditable : function(col, editable){
5689         this.config[col].editable = editable;
5690     },
5691
5692
5693     /**
5694      * Returns true if the column is hidden.
5695      * @param {Number} colIndex The column index
5696      * @return {Boolean}
5697      */
5698     isHidden : function(colIndex){
5699         return this.config[colIndex].hidden;
5700     },
5701
5702
5703     /**
5704      * Returns true if the column width cannot be changed
5705      */
5706     isFixed : function(colIndex){
5707         return this.config[colIndex].fixed;
5708     },
5709
5710     /**
5711      * Returns true if the column can be resized
5712      * @return {Boolean}
5713      */
5714     isResizable : function(colIndex){
5715         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5716     },
5717     /**
5718      * Sets if a column is hidden.
5719      * @param {Number} colIndex The column index
5720      * @param {Boolean} hidden True if the column is hidden
5721      */
5722     setHidden : function(colIndex, hidden){
5723         this.config[colIndex].hidden = hidden;
5724         this.totalWidth = null;
5725         this.fireEvent("hiddenchange", this, colIndex, hidden);
5726     },
5727
5728     /**
5729      * Sets the editor for a column.
5730      * @param {Number} col The column index
5731      * @param {Object} editor The editor object
5732      */
5733     setEditor : function(col, editor){
5734         this.config[col].editor = editor;
5735     }
5736 });
5737
5738 Roo.grid.ColumnModel.defaultRenderer = function(value)
5739 {
5740     if(typeof value == "object") {
5741         return value;
5742     }
5743         if(typeof value == "string" && value.length < 1){
5744             return "&#160;";
5745         }
5746     
5747         return String.format("{0}", value);
5748 };
5749
5750 // Alias for backwards compatibility
5751 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5752 /*
5753  * Based on:
5754  * Ext JS Library 1.1.1
5755  * Copyright(c) 2006-2007, Ext JS, LLC.
5756  *
5757  * Originally Released Under LGPL - original licence link has changed is not relivant.
5758  *
5759  * Fork - LGPL
5760  * <script type="text/javascript">
5761  */
5762  
5763 /**
5764  * @class Roo.LoadMask
5765  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5766  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5767  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5768  * element's UpdateManager load indicator and will be destroyed after the initial load.
5769  * @constructor
5770  * Create a new LoadMask
5771  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5772  * @param {Object} config The config object
5773  */
5774 Roo.LoadMask = function(el, config){
5775     this.el = Roo.get(el);
5776     Roo.apply(this, config);
5777     if(this.store){
5778         this.store.on('beforeload', this.onBeforeLoad, this);
5779         this.store.on('load', this.onLoad, this);
5780         this.store.on('loadexception', this.onLoadException, this);
5781         this.removeMask = false;
5782     }else{
5783         var um = this.el.getUpdateManager();
5784         um.showLoadIndicator = false; // disable the default indicator
5785         um.on('beforeupdate', this.onBeforeLoad, this);
5786         um.on('update', this.onLoad, this);
5787         um.on('failure', this.onLoad, this);
5788         this.removeMask = true;
5789     }
5790 };
5791
5792 Roo.LoadMask.prototype = {
5793     /**
5794      * @cfg {Boolean} removeMask
5795      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5796      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5797      */
5798     /**
5799      * @cfg {String} msg
5800      * The text to display in a centered loading message box (defaults to 'Loading...')
5801      */
5802     msg : 'Loading...',
5803     /**
5804      * @cfg {String} msgCls
5805      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5806      */
5807     msgCls : 'x-mask-loading',
5808
5809     /**
5810      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5811      * @type Boolean
5812      */
5813     disabled: false,
5814
5815     /**
5816      * Disables the mask to prevent it from being displayed
5817      */
5818     disable : function(){
5819        this.disabled = true;
5820     },
5821
5822     /**
5823      * Enables the mask so that it can be displayed
5824      */
5825     enable : function(){
5826         this.disabled = false;
5827     },
5828     
5829     onLoadException : function()
5830     {
5831         Roo.log(arguments);
5832         
5833         if (typeof(arguments[3]) != 'undefined') {
5834             Roo.MessageBox.alert("Error loading",arguments[3]);
5835         } 
5836         /*
5837         try {
5838             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5839                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5840             }   
5841         } catch(e) {
5842             
5843         }
5844         */
5845     
5846         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5847     },
5848     // private
5849     onLoad : function()
5850     {
5851         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5852     },
5853
5854     // private
5855     onBeforeLoad : function(){
5856         if(!this.disabled){
5857             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5858         }
5859     },
5860
5861     // private
5862     destroy : function(){
5863         if(this.store){
5864             this.store.un('beforeload', this.onBeforeLoad, this);
5865             this.store.un('load', this.onLoad, this);
5866             this.store.un('loadexception', this.onLoadException, this);
5867         }else{
5868             var um = this.el.getUpdateManager();
5869             um.un('beforeupdate', this.onBeforeLoad, this);
5870             um.un('update', this.onLoad, this);
5871             um.un('failure', this.onLoad, this);
5872         }
5873     }
5874 };/*
5875  * - LGPL
5876  *
5877  * table
5878  * 
5879  */
5880
5881 /**
5882  * @class Roo.bootstrap.Table
5883  * @extends Roo.bootstrap.Component
5884  * Bootstrap Table class
5885  * @cfg {String} cls table class
5886  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5887  * @cfg {String} bgcolor Specifies the background color for a table
5888  * @cfg {Number} border Specifies whether the table cells should have borders or not
5889  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5890  * @cfg {Number} cellspacing Specifies the space between cells
5891  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5892  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5893  * @cfg {String} sortable Specifies that the table should be sortable
5894  * @cfg {String} summary Specifies a summary of the content of a table
5895  * @cfg {Number} width Specifies the width of a table
5896  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5897  * 
5898  * @cfg {boolean} striped Should the rows be alternative striped
5899  * @cfg {boolean} bordered Add borders to the table
5900  * @cfg {boolean} hover Add hover highlighting
5901  * @cfg {boolean} condensed Format condensed
5902  * @cfg {boolean} responsive Format condensed
5903  * @cfg {Boolean} loadMask (true|false) default false
5904  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5905  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5906  * @cfg {Boolean} rowSelection (true|false) default false
5907  * @cfg {Boolean} cellSelection (true|false) default false
5908  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5909  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5910  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5911  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
5912  
5913  * 
5914  * @constructor
5915  * Create a new Table
5916  * @param {Object} config The config object
5917  */
5918
5919 Roo.bootstrap.Table = function(config){
5920     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5921     
5922   
5923     
5924     // BC...
5925     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5926     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5927     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5928     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5929     
5930     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5931     if (this.sm) {
5932         this.sm.grid = this;
5933         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5934         this.sm = this.selModel;
5935         this.sm.xmodule = this.xmodule || false;
5936     }
5937     
5938     if (this.cm && typeof(this.cm.config) == 'undefined') {
5939         this.colModel = new Roo.grid.ColumnModel(this.cm);
5940         this.cm = this.colModel;
5941         this.cm.xmodule = this.xmodule || false;
5942     }
5943     if (this.store) {
5944         this.store= Roo.factory(this.store, Roo.data);
5945         this.ds = this.store;
5946         this.ds.xmodule = this.xmodule || false;
5947          
5948     }
5949     if (this.footer && this.store) {
5950         this.footer.dataSource = this.ds;
5951         this.footer = Roo.factory(this.footer);
5952     }
5953     
5954     /** @private */
5955     this.addEvents({
5956         /**
5957          * @event cellclick
5958          * Fires when a cell is clicked
5959          * @param {Roo.bootstrap.Table} this
5960          * @param {Roo.Element} el
5961          * @param {Number} rowIndex
5962          * @param {Number} columnIndex
5963          * @param {Roo.EventObject} e
5964          */
5965         "cellclick" : true,
5966         /**
5967          * @event celldblclick
5968          * Fires when a cell is double clicked
5969          * @param {Roo.bootstrap.Table} this
5970          * @param {Roo.Element} el
5971          * @param {Number} rowIndex
5972          * @param {Number} columnIndex
5973          * @param {Roo.EventObject} e
5974          */
5975         "celldblclick" : true,
5976         /**
5977          * @event rowclick
5978          * Fires when a row is clicked
5979          * @param {Roo.bootstrap.Table} this
5980          * @param {Roo.Element} el
5981          * @param {Number} rowIndex
5982          * @param {Roo.EventObject} e
5983          */
5984         "rowclick" : true,
5985         /**
5986          * @event rowdblclick
5987          * Fires when a row is double clicked
5988          * @param {Roo.bootstrap.Table} this
5989          * @param {Roo.Element} el
5990          * @param {Number} rowIndex
5991          * @param {Roo.EventObject} e
5992          */
5993         "rowdblclick" : true,
5994         /**
5995          * @event mouseover
5996          * Fires when a mouseover occur
5997          * @param {Roo.bootstrap.Table} this
5998          * @param {Roo.Element} el
5999          * @param {Number} rowIndex
6000          * @param {Number} columnIndex
6001          * @param {Roo.EventObject} e
6002          */
6003         "mouseover" : true,
6004         /**
6005          * @event mouseout
6006          * Fires when a mouseout occur
6007          * @param {Roo.bootstrap.Table} this
6008          * @param {Roo.Element} el
6009          * @param {Number} rowIndex
6010          * @param {Number} columnIndex
6011          * @param {Roo.EventObject} e
6012          */
6013         "mouseout" : true,
6014         /**
6015          * @event rowclass
6016          * Fires when a row is rendered, so you can change add a style to it.
6017          * @param {Roo.bootstrap.Table} this
6018          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6019          */
6020         'rowclass' : true,
6021           /**
6022          * @event rowsrendered
6023          * Fires when all the  rows have been rendered
6024          * @param {Roo.bootstrap.Table} this
6025          */
6026         'rowsrendered' : true,
6027         /**
6028          * @event contextmenu
6029          * The raw contextmenu event for the entire grid.
6030          * @param {Roo.EventObject} e
6031          */
6032         "contextmenu" : true,
6033         /**
6034          * @event rowcontextmenu
6035          * Fires when a row is right clicked
6036          * @param {Roo.bootstrap.Table} this
6037          * @param {Number} rowIndex
6038          * @param {Roo.EventObject} e
6039          */
6040         "rowcontextmenu" : true,
6041         /**
6042          * @event cellcontextmenu
6043          * Fires when a cell is right clicked
6044          * @param {Roo.bootstrap.Table} this
6045          * @param {Number} rowIndex
6046          * @param {Number} cellIndex
6047          * @param {Roo.EventObject} e
6048          */
6049          "cellcontextmenu" : true,
6050          /**
6051          * @event headercontextmenu
6052          * Fires when a header is right clicked
6053          * @param {Roo.bootstrap.Table} this
6054          * @param {Number} columnIndex
6055          * @param {Roo.EventObject} e
6056          */
6057         "headercontextmenu" : true
6058     });
6059 };
6060
6061 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6062     
6063     cls: false,
6064     align: false,
6065     bgcolor: false,
6066     border: false,
6067     cellpadding: false,
6068     cellspacing: false,
6069     frame: false,
6070     rules: false,
6071     sortable: false,
6072     summary: false,
6073     width: false,
6074     striped : false,
6075     scrollBody : false,
6076     bordered: false,
6077     hover:  false,
6078     condensed : false,
6079     responsive : false,
6080     sm : false,
6081     cm : false,
6082     store : false,
6083     loadMask : false,
6084     footerShow : true,
6085     headerShow : true,
6086   
6087     rowSelection : false,
6088     cellSelection : false,
6089     layout : false,
6090     
6091     // Roo.Element - the tbody
6092     mainBody: false,
6093     // Roo.Element - thead element
6094     mainHead: false,
6095     
6096     container: false, // used by gridpanel...
6097     
6098     lazyLoad : false,
6099     
6100     CSS : Roo.util.CSS,
6101     
6102     auto_hide_footer : false,
6103     
6104     getAutoCreate : function()
6105     {
6106         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6107         
6108         cfg = {
6109             tag: 'table',
6110             cls : 'table',
6111             cn : []
6112         };
6113         if (this.scrollBody) {
6114             cfg.cls += ' table-body-fixed';
6115         }    
6116         if (this.striped) {
6117             cfg.cls += ' table-striped';
6118         }
6119         
6120         if (this.hover) {
6121             cfg.cls += ' table-hover';
6122         }
6123         if (this.bordered) {
6124             cfg.cls += ' table-bordered';
6125         }
6126         if (this.condensed) {
6127             cfg.cls += ' table-condensed';
6128         }
6129         if (this.responsive) {
6130             cfg.cls += ' table-responsive';
6131         }
6132         
6133         if (this.cls) {
6134             cfg.cls+=  ' ' +this.cls;
6135         }
6136         
6137         // this lot should be simplifed...
6138         var _t = this;
6139         var cp = [
6140             'align',
6141             'bgcolor',
6142             'border',
6143             'cellpadding',
6144             'cellspacing',
6145             'frame',
6146             'rules',
6147             'sortable',
6148             'summary',
6149             'width'
6150         ].forEach(function(k) {
6151             if (_t[k]) {
6152                 cfg[k] = _t[k];
6153             }
6154         });
6155         
6156         
6157         if (this.layout) {
6158             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6159         }
6160         
6161         if(this.store || this.cm){
6162             if(this.headerShow){
6163                 cfg.cn.push(this.renderHeader());
6164             }
6165             
6166             cfg.cn.push(this.renderBody());
6167             
6168             if(this.footerShow){
6169                 cfg.cn.push(this.renderFooter());
6170             }
6171             // where does this come from?
6172             //cfg.cls+=  ' TableGrid';
6173         }
6174         
6175         return { cn : [ cfg ] };
6176     },
6177     
6178     initEvents : function()
6179     {   
6180         if(!this.store || !this.cm){
6181             return;
6182         }
6183         if (this.selModel) {
6184             this.selModel.initEvents();
6185         }
6186         
6187         
6188         //Roo.log('initEvents with ds!!!!');
6189         
6190         this.mainBody = this.el.select('tbody', true).first();
6191         this.mainHead = this.el.select('thead', true).first();
6192         this.mainFoot = this.el.select('tfoot', true).first();
6193         
6194         
6195         
6196         var _this = this;
6197         
6198         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6199             e.on('click', _this.sort, _this);
6200         });
6201         
6202         this.mainBody.on("click", this.onClick, this);
6203         this.mainBody.on("dblclick", this.onDblClick, this);
6204         
6205         // why is this done????? = it breaks dialogs??
6206         //this.parent().el.setStyle('position', 'relative');
6207         
6208         
6209         if (this.footer) {
6210             this.footer.parentId = this.id;
6211             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6212             
6213             if(this.lazyLoad){
6214                 this.el.select('tfoot tr td').first().addClass('hide');
6215             }
6216         } 
6217         
6218         if(this.loadMask) {
6219             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6220         }
6221         
6222         this.store.on('load', this.onLoad, this);
6223         this.store.on('beforeload', this.onBeforeLoad, this);
6224         this.store.on('update', this.onUpdate, this);
6225         this.store.on('add', this.onAdd, this);
6226         this.store.on("clear", this.clear, this);
6227         
6228         this.el.on("contextmenu", this.onContextMenu, this);
6229         
6230         this.mainBody.on('scroll', this.onBodyScroll, this);
6231         
6232         this.cm.on("headerchange", this.onHeaderChange, this);
6233         
6234         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6235         
6236     },
6237     
6238     onContextMenu : function(e, t)
6239     {
6240         this.processEvent("contextmenu", e);
6241     },
6242     
6243     processEvent : function(name, e)
6244     {
6245         if (name != 'touchstart' ) {
6246             this.fireEvent(name, e);    
6247         }
6248         
6249         var t = e.getTarget();
6250         
6251         var cell = Roo.get(t);
6252         
6253         if(!cell){
6254             return;
6255         }
6256         
6257         if(cell.findParent('tfoot', false, true)){
6258             return;
6259         }
6260         
6261         if(cell.findParent('thead', false, true)){
6262             
6263             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6264                 cell = Roo.get(t).findParent('th', false, true);
6265                 if (!cell) {
6266                     Roo.log("failed to find th in thead?");
6267                     Roo.log(e.getTarget());
6268                     return;
6269                 }
6270             }
6271             
6272             var cellIndex = cell.dom.cellIndex;
6273             
6274             var ename = name == 'touchstart' ? 'click' : name;
6275             this.fireEvent("header" + ename, this, cellIndex, e);
6276             
6277             return;
6278         }
6279         
6280         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6281             cell = Roo.get(t).findParent('td', false, true);
6282             if (!cell) {
6283                 Roo.log("failed to find th in tbody?");
6284                 Roo.log(e.getTarget());
6285                 return;
6286             }
6287         }
6288         
6289         var row = cell.findParent('tr', false, true);
6290         var cellIndex = cell.dom.cellIndex;
6291         var rowIndex = row.dom.rowIndex - 1;
6292         
6293         if(row !== false){
6294             
6295             this.fireEvent("row" + name, this, rowIndex, e);
6296             
6297             if(cell !== false){
6298             
6299                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6300             }
6301         }
6302         
6303     },
6304     
6305     onMouseover : function(e, el)
6306     {
6307         var cell = Roo.get(el);
6308         
6309         if(!cell){
6310             return;
6311         }
6312         
6313         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6314             cell = cell.findParent('td', false, true);
6315         }
6316         
6317         var row = cell.findParent('tr', false, true);
6318         var cellIndex = cell.dom.cellIndex;
6319         var rowIndex = row.dom.rowIndex - 1; // start from 0
6320         
6321         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6322         
6323     },
6324     
6325     onMouseout : function(e, el)
6326     {
6327         var cell = Roo.get(el);
6328         
6329         if(!cell){
6330             return;
6331         }
6332         
6333         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6334             cell = cell.findParent('td', false, true);
6335         }
6336         
6337         var row = cell.findParent('tr', false, true);
6338         var cellIndex = cell.dom.cellIndex;
6339         var rowIndex = row.dom.rowIndex - 1; // start from 0
6340         
6341         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6342         
6343     },
6344     
6345     onClick : function(e, el)
6346     {
6347         var cell = Roo.get(el);
6348         
6349         if(!cell || (!this.cellSelection && !this.rowSelection)){
6350             return;
6351         }
6352         
6353         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6354             cell = cell.findParent('td', false, true);
6355         }
6356         
6357         if(!cell || typeof(cell) == 'undefined'){
6358             return;
6359         }
6360         
6361         var row = cell.findParent('tr', false, true);
6362         
6363         if(!row || typeof(row) == 'undefined'){
6364             return;
6365         }
6366         
6367         var cellIndex = cell.dom.cellIndex;
6368         var rowIndex = this.getRowIndex(row);
6369         
6370         // why??? - should these not be based on SelectionModel?
6371         if(this.cellSelection){
6372             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6373         }
6374         
6375         if(this.rowSelection){
6376             this.fireEvent('rowclick', this, row, rowIndex, e);
6377         }
6378         
6379         
6380     },
6381         
6382     onDblClick : function(e,el)
6383     {
6384         var cell = Roo.get(el);
6385         
6386         if(!cell || (!this.cellSelection && !this.rowSelection)){
6387             return;
6388         }
6389         
6390         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6391             cell = cell.findParent('td', false, true);
6392         }
6393         
6394         if(!cell || typeof(cell) == 'undefined'){
6395             return;
6396         }
6397         
6398         var row = cell.findParent('tr', false, true);
6399         
6400         if(!row || typeof(row) == 'undefined'){
6401             return;
6402         }
6403         
6404         var cellIndex = cell.dom.cellIndex;
6405         var rowIndex = this.getRowIndex(row);
6406         
6407         if(this.cellSelection){
6408             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6409         }
6410         
6411         if(this.rowSelection){
6412             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6413         }
6414     },
6415     
6416     sort : function(e,el)
6417     {
6418         var col = Roo.get(el);
6419         
6420         if(!col.hasClass('sortable')){
6421             return;
6422         }
6423         
6424         var sort = col.attr('sort');
6425         var dir = 'ASC';
6426         
6427         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6428             dir = 'DESC';
6429         }
6430         
6431         this.store.sortInfo = {field : sort, direction : dir};
6432         
6433         if (this.footer) {
6434             Roo.log("calling footer first");
6435             this.footer.onClick('first');
6436         } else {
6437         
6438             this.store.load({ params : { start : 0 } });
6439         }
6440     },
6441     
6442     renderHeader : function()
6443     {
6444         var header = {
6445             tag: 'thead',
6446             cn : []
6447         };
6448         
6449         var cm = this.cm;
6450         this.totalWidth = 0;
6451         
6452         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6453             
6454             var config = cm.config[i];
6455             
6456             var c = {
6457                 tag: 'th',
6458                 cls : 'x-hcol-' + i,
6459                 style : '',
6460                 html: cm.getColumnHeader(i)
6461             };
6462             
6463             var hh = '';
6464             
6465             if(typeof(config.sortable) != 'undefined' && config.sortable){
6466                 c.cls = 'sortable';
6467                 c.html = '<i class="glyphicon"></i>' + c.html;
6468             }
6469             
6470             if(typeof(config.lgHeader) != 'undefined'){
6471                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6472             }
6473             
6474             if(typeof(config.mdHeader) != 'undefined'){
6475                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6476             }
6477             
6478             if(typeof(config.smHeader) != 'undefined'){
6479                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6480             }
6481             
6482             if(typeof(config.xsHeader) != 'undefined'){
6483                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6484             }
6485             
6486             if(hh.length){
6487                 c.html = hh;
6488             }
6489             
6490             if(typeof(config.tooltip) != 'undefined'){
6491                 c.tooltip = config.tooltip;
6492             }
6493             
6494             if(typeof(config.colspan) != 'undefined'){
6495                 c.colspan = config.colspan;
6496             }
6497             
6498             if(typeof(config.hidden) != 'undefined' && config.hidden){
6499                 c.style += ' display:none;';
6500             }
6501             
6502             if(typeof(config.dataIndex) != 'undefined'){
6503                 c.sort = config.dataIndex;
6504             }
6505             
6506            
6507             
6508             if(typeof(config.align) != 'undefined' && config.align.length){
6509                 c.style += ' text-align:' + config.align + ';';
6510             }
6511             
6512             if(typeof(config.width) != 'undefined'){
6513                 c.style += ' width:' + config.width + 'px;';
6514                 this.totalWidth += config.width;
6515             } else {
6516                 this.totalWidth += 100; // assume minimum of 100 per column?
6517             }
6518             
6519             if(typeof(config.cls) != 'undefined'){
6520                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6521             }
6522             
6523             ['xs','sm','md','lg'].map(function(size){
6524                 
6525                 if(typeof(config[size]) == 'undefined'){
6526                     return;
6527                 }
6528                 
6529                 if (!config[size]) { // 0 = hidden
6530                     c.cls += ' hidden-' + size;
6531                     return;
6532                 }
6533                 
6534                 c.cls += ' col-' + size + '-' + config[size];
6535
6536             });
6537             
6538             header.cn.push(c)
6539         }
6540         
6541         return header;
6542     },
6543     
6544     renderBody : function()
6545     {
6546         var body = {
6547             tag: 'tbody',
6548             cn : [
6549                 {
6550                     tag: 'tr',
6551                     cn : [
6552                         {
6553                             tag : 'td',
6554                             colspan :  this.cm.getColumnCount()
6555                         }
6556                     ]
6557                 }
6558             ]
6559         };
6560         
6561         return body;
6562     },
6563     
6564     renderFooter : function()
6565     {
6566         var footer = {
6567             tag: 'tfoot',
6568             cn : [
6569                 {
6570                     tag: 'tr',
6571                     cn : [
6572                         {
6573                             tag : 'td',
6574                             colspan :  this.cm.getColumnCount()
6575                         }
6576                     ]
6577                 }
6578             ]
6579         };
6580         
6581         return footer;
6582     },
6583     
6584     
6585     
6586     onLoad : function()
6587     {
6588 //        Roo.log('ds onload');
6589         this.clear();
6590         
6591         var _this = this;
6592         var cm = this.cm;
6593         var ds = this.store;
6594         
6595         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6596             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6597             if (_this.store.sortInfo) {
6598                     
6599                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6600                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6601                 }
6602                 
6603                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6604                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6605                 }
6606             }
6607         });
6608         
6609         var tbody =  this.mainBody;
6610               
6611         if(ds.getCount() > 0){
6612             ds.data.each(function(d,rowIndex){
6613                 var row =  this.renderRow(cm, ds, rowIndex);
6614                 
6615                 tbody.createChild(row);
6616                 
6617                 var _this = this;
6618                 
6619                 if(row.cellObjects.length){
6620                     Roo.each(row.cellObjects, function(r){
6621                         _this.renderCellObject(r);
6622                     })
6623                 }
6624                 
6625             }, this);
6626         }
6627         
6628         var tfoot = this.el.select('tfoot', true).first();
6629         
6630         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6631             
6632             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6633             
6634             var total = this.ds.getTotalCount();
6635             
6636             if(this.footer.pageSize < total){
6637                 this.mainFoot.show();
6638             }
6639         }
6640         
6641         Roo.each(this.el.select('tbody td', true).elements, function(e){
6642             e.on('mouseover', _this.onMouseover, _this);
6643         });
6644         
6645         Roo.each(this.el.select('tbody td', true).elements, function(e){
6646             e.on('mouseout', _this.onMouseout, _this);
6647         });
6648         this.fireEvent('rowsrendered', this);
6649         
6650         this.autoSize();
6651     },
6652     
6653     
6654     onUpdate : function(ds,record)
6655     {
6656         this.refreshRow(record);
6657         this.autoSize();
6658     },
6659     
6660     onRemove : function(ds, record, index, isUpdate){
6661         if(isUpdate !== true){
6662             this.fireEvent("beforerowremoved", this, index, record);
6663         }
6664         var bt = this.mainBody.dom;
6665         
6666         var rows = this.el.select('tbody > tr', true).elements;
6667         
6668         if(typeof(rows[index]) != 'undefined'){
6669             bt.removeChild(rows[index].dom);
6670         }
6671         
6672 //        if(bt.rows[index]){
6673 //            bt.removeChild(bt.rows[index]);
6674 //        }
6675         
6676         if(isUpdate !== true){
6677             //this.stripeRows(index);
6678             //this.syncRowHeights(index, index);
6679             //this.layout();
6680             this.fireEvent("rowremoved", this, index, record);
6681         }
6682     },
6683     
6684     onAdd : function(ds, records, rowIndex)
6685     {
6686         //Roo.log('on Add called');
6687         // - note this does not handle multiple adding very well..
6688         var bt = this.mainBody.dom;
6689         for (var i =0 ; i < records.length;i++) {
6690             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6691             //Roo.log(records[i]);
6692             //Roo.log(this.store.getAt(rowIndex+i));
6693             this.insertRow(this.store, rowIndex + i, false);
6694             return;
6695         }
6696         
6697     },
6698     
6699     
6700     refreshRow : function(record){
6701         var ds = this.store, index;
6702         if(typeof record == 'number'){
6703             index = record;
6704             record = ds.getAt(index);
6705         }else{
6706             index = ds.indexOf(record);
6707         }
6708         this.insertRow(ds, index, true);
6709         this.autoSize();
6710         this.onRemove(ds, record, index+1, true);
6711         this.autoSize();
6712         //this.syncRowHeights(index, index);
6713         //this.layout();
6714         this.fireEvent("rowupdated", this, index, record);
6715     },
6716     
6717     insertRow : function(dm, rowIndex, isUpdate){
6718         
6719         if(!isUpdate){
6720             this.fireEvent("beforerowsinserted", this, rowIndex);
6721         }
6722             //var s = this.getScrollState();
6723         var row = this.renderRow(this.cm, this.store, rowIndex);
6724         // insert before rowIndex..
6725         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6726         
6727         var _this = this;
6728                 
6729         if(row.cellObjects.length){
6730             Roo.each(row.cellObjects, function(r){
6731                 _this.renderCellObject(r);
6732             })
6733         }
6734             
6735         if(!isUpdate){
6736             this.fireEvent("rowsinserted", this, rowIndex);
6737             //this.syncRowHeights(firstRow, lastRow);
6738             //this.stripeRows(firstRow);
6739             //this.layout();
6740         }
6741         
6742     },
6743     
6744     
6745     getRowDom : function(rowIndex)
6746     {
6747         var rows = this.el.select('tbody > tr', true).elements;
6748         
6749         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6750         
6751     },
6752     // returns the object tree for a tr..
6753   
6754     
6755     renderRow : function(cm, ds, rowIndex) 
6756     {
6757         var d = ds.getAt(rowIndex);
6758         
6759         var row = {
6760             tag : 'tr',
6761             cls : 'x-row-' + rowIndex,
6762             cn : []
6763         };
6764             
6765         var cellObjects = [];
6766         
6767         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6768             var config = cm.config[i];
6769             
6770             var renderer = cm.getRenderer(i);
6771             var value = '';
6772             var id = false;
6773             
6774             if(typeof(renderer) !== 'undefined'){
6775                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6776             }
6777             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6778             // and are rendered into the cells after the row is rendered - using the id for the element.
6779             
6780             if(typeof(value) === 'object'){
6781                 id = Roo.id();
6782                 cellObjects.push({
6783                     container : id,
6784                     cfg : value 
6785                 })
6786             }
6787             
6788             var rowcfg = {
6789                 record: d,
6790                 rowIndex : rowIndex,
6791                 colIndex : i,
6792                 rowClass : ''
6793             };
6794
6795             this.fireEvent('rowclass', this, rowcfg);
6796             
6797             var td = {
6798                 tag: 'td',
6799                 cls : rowcfg.rowClass + ' x-col-' + i,
6800                 style: '',
6801                 html: (typeof(value) === 'object') ? '' : value
6802             };
6803             
6804             if (id) {
6805                 td.id = id;
6806             }
6807             
6808             if(typeof(config.colspan) != 'undefined'){
6809                 td.colspan = config.colspan;
6810             }
6811             
6812             if(typeof(config.hidden) != 'undefined' && config.hidden){
6813                 td.style += ' display:none;';
6814             }
6815             
6816             if(typeof(config.align) != 'undefined' && config.align.length){
6817                 td.style += ' text-align:' + config.align + ';';
6818             }
6819             if(typeof(config.valign) != 'undefined' && config.valign.length){
6820                 td.style += ' vertical-align:' + config.valign + ';';
6821             }
6822             
6823             if(typeof(config.width) != 'undefined'){
6824                 td.style += ' width:' +  config.width + 'px;';
6825             }
6826             
6827             if(typeof(config.cursor) != 'undefined'){
6828                 td.style += ' cursor:' +  config.cursor + ';';
6829             }
6830             
6831             if(typeof(config.cls) != 'undefined'){
6832                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6833             }
6834             
6835             ['xs','sm','md','lg'].map(function(size){
6836                 
6837                 if(typeof(config[size]) == 'undefined'){
6838                     return;
6839                 }
6840                 
6841                 if (!config[size]) { // 0 = hidden
6842                     td.cls += ' hidden-' + size;
6843                     return;
6844                 }
6845                 
6846                 td.cls += ' col-' + size + '-' + config[size];
6847
6848             });
6849             
6850             row.cn.push(td);
6851            
6852         }
6853         
6854         row.cellObjects = cellObjects;
6855         
6856         return row;
6857           
6858     },
6859     
6860     
6861     
6862     onBeforeLoad : function()
6863     {
6864         
6865     },
6866      /**
6867      * Remove all rows
6868      */
6869     clear : function()
6870     {
6871         this.el.select('tbody', true).first().dom.innerHTML = '';
6872     },
6873     /**
6874      * Show or hide a row.
6875      * @param {Number} rowIndex to show or hide
6876      * @param {Boolean} state hide
6877      */
6878     setRowVisibility : function(rowIndex, state)
6879     {
6880         var bt = this.mainBody.dom;
6881         
6882         var rows = this.el.select('tbody > tr', true).elements;
6883         
6884         if(typeof(rows[rowIndex]) == 'undefined'){
6885             return;
6886         }
6887         rows[rowIndex].dom.style.display = state ? '' : 'none';
6888     },
6889     
6890     
6891     getSelectionModel : function(){
6892         if(!this.selModel){
6893             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6894         }
6895         return this.selModel;
6896     },
6897     /*
6898      * Render the Roo.bootstrap object from renderder
6899      */
6900     renderCellObject : function(r)
6901     {
6902         var _this = this;
6903         
6904         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6905         
6906         var t = r.cfg.render(r.container);
6907         
6908         if(r.cfg.cn){
6909             Roo.each(r.cfg.cn, function(c){
6910                 var child = {
6911                     container: t.getChildContainer(),
6912                     cfg: c
6913                 };
6914                 _this.renderCellObject(child);
6915             })
6916         }
6917     },
6918     
6919     getRowIndex : function(row)
6920     {
6921         var rowIndex = -1;
6922         
6923         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6924             if(el != row){
6925                 return;
6926             }
6927             
6928             rowIndex = index;
6929         });
6930         
6931         return rowIndex;
6932     },
6933      /**
6934      * Returns the grid's underlying element = used by panel.Grid
6935      * @return {Element} The element
6936      */
6937     getGridEl : function(){
6938         return this.el;
6939     },
6940      /**
6941      * Forces a resize - used by panel.Grid
6942      * @return {Element} The element
6943      */
6944     autoSize : function()
6945     {
6946         //var ctr = Roo.get(this.container.dom.parentElement);
6947         var ctr = Roo.get(this.el.dom);
6948         
6949         var thd = this.getGridEl().select('thead',true).first();
6950         var tbd = this.getGridEl().select('tbody', true).first();
6951         var tfd = this.getGridEl().select('tfoot', true).first();
6952         
6953         var cw = ctr.getWidth();
6954         
6955         if (tbd) {
6956             
6957             tbd.setSize(ctr.getWidth(),
6958                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6959             );
6960             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6961             cw -= barsize;
6962         }
6963         cw = Math.max(cw, this.totalWidth);
6964         this.getGridEl().select('tr',true).setWidth(cw);
6965         // resize 'expandable coloumn?
6966         
6967         return; // we doe not have a view in this design..
6968         
6969     },
6970     onBodyScroll: function()
6971     {
6972         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6973         if(this.mainHead){
6974             this.mainHead.setStyle({
6975                 'position' : 'relative',
6976                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6977             });
6978         }
6979         
6980         if(this.lazyLoad){
6981             
6982             var scrollHeight = this.mainBody.dom.scrollHeight;
6983             
6984             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6985             
6986             var height = this.mainBody.getHeight();
6987             
6988             if(scrollHeight - height == scrollTop) {
6989                 
6990                 var total = this.ds.getTotalCount();
6991                 
6992                 if(this.footer.cursor + this.footer.pageSize < total){
6993                     
6994                     this.footer.ds.load({
6995                         params : {
6996                             start : this.footer.cursor + this.footer.pageSize,
6997                             limit : this.footer.pageSize
6998                         },
6999                         add : true
7000                     });
7001                 }
7002             }
7003             
7004         }
7005     },
7006     
7007     onHeaderChange : function()
7008     {
7009         var header = this.renderHeader();
7010         var table = this.el.select('table', true).first();
7011         
7012         this.mainHead.remove();
7013         this.mainHead = table.createChild(header, this.mainBody, false);
7014     },
7015     
7016     onHiddenChange : function(colModel, colIndex, hidden)
7017     {
7018         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7019         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7020         
7021         this.CSS.updateRule(thSelector, "display", "");
7022         this.CSS.updateRule(tdSelector, "display", "");
7023         
7024         if(hidden){
7025             this.CSS.updateRule(thSelector, "display", "none");
7026             this.CSS.updateRule(tdSelector, "display", "none");
7027         }
7028         
7029         this.onHeaderChange();
7030         this.onLoad();
7031         
7032     }
7033     
7034 });
7035
7036  
7037
7038  /*
7039  * - LGPL
7040  *
7041  * table cell
7042  * 
7043  */
7044
7045 /**
7046  * @class Roo.bootstrap.TableCell
7047  * @extends Roo.bootstrap.Component
7048  * Bootstrap TableCell class
7049  * @cfg {String} html cell contain text
7050  * @cfg {String} cls cell class
7051  * @cfg {String} tag cell tag (td|th) default td
7052  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7053  * @cfg {String} align Aligns the content in a cell
7054  * @cfg {String} axis Categorizes cells
7055  * @cfg {String} bgcolor Specifies the background color of a cell
7056  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7057  * @cfg {Number} colspan Specifies the number of columns a cell should span
7058  * @cfg {String} headers Specifies one or more header cells a cell is related to
7059  * @cfg {Number} height Sets the height of a cell
7060  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7061  * @cfg {Number} rowspan Sets the number of rows a cell should span
7062  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7063  * @cfg {String} valign Vertical aligns the content in a cell
7064  * @cfg {Number} width Specifies the width of a cell
7065  * 
7066  * @constructor
7067  * Create a new TableCell
7068  * @param {Object} config The config object
7069  */
7070
7071 Roo.bootstrap.TableCell = function(config){
7072     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7073 };
7074
7075 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7076     
7077     html: false,
7078     cls: false,
7079     tag: false,
7080     abbr: false,
7081     align: false,
7082     axis: false,
7083     bgcolor: false,
7084     charoff: false,
7085     colspan: false,
7086     headers: false,
7087     height: false,
7088     nowrap: false,
7089     rowspan: false,
7090     scope: false,
7091     valign: false,
7092     width: false,
7093     
7094     
7095     getAutoCreate : function(){
7096         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7097         
7098         cfg = {
7099             tag: 'td'
7100         };
7101         
7102         if(this.tag){
7103             cfg.tag = this.tag;
7104         }
7105         
7106         if (this.html) {
7107             cfg.html=this.html
7108         }
7109         if (this.cls) {
7110             cfg.cls=this.cls
7111         }
7112         if (this.abbr) {
7113             cfg.abbr=this.abbr
7114         }
7115         if (this.align) {
7116             cfg.align=this.align
7117         }
7118         if (this.axis) {
7119             cfg.axis=this.axis
7120         }
7121         if (this.bgcolor) {
7122             cfg.bgcolor=this.bgcolor
7123         }
7124         if (this.charoff) {
7125             cfg.charoff=this.charoff
7126         }
7127         if (this.colspan) {
7128             cfg.colspan=this.colspan
7129         }
7130         if (this.headers) {
7131             cfg.headers=this.headers
7132         }
7133         if (this.height) {
7134             cfg.height=this.height
7135         }
7136         if (this.nowrap) {
7137             cfg.nowrap=this.nowrap
7138         }
7139         if (this.rowspan) {
7140             cfg.rowspan=this.rowspan
7141         }
7142         if (this.scope) {
7143             cfg.scope=this.scope
7144         }
7145         if (this.valign) {
7146             cfg.valign=this.valign
7147         }
7148         if (this.width) {
7149             cfg.width=this.width
7150         }
7151         
7152         
7153         return cfg;
7154     }
7155    
7156 });
7157
7158  
7159
7160  /*
7161  * - LGPL
7162  *
7163  * table row
7164  * 
7165  */
7166
7167 /**
7168  * @class Roo.bootstrap.TableRow
7169  * @extends Roo.bootstrap.Component
7170  * Bootstrap TableRow class
7171  * @cfg {String} cls row class
7172  * @cfg {String} align Aligns the content in a table row
7173  * @cfg {String} bgcolor Specifies a background color for a table row
7174  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7175  * @cfg {String} valign Vertical aligns the content in a table row
7176  * 
7177  * @constructor
7178  * Create a new TableRow
7179  * @param {Object} config The config object
7180  */
7181
7182 Roo.bootstrap.TableRow = function(config){
7183     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7184 };
7185
7186 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7187     
7188     cls: false,
7189     align: false,
7190     bgcolor: false,
7191     charoff: false,
7192     valign: false,
7193     
7194     getAutoCreate : function(){
7195         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7196         
7197         cfg = {
7198             tag: 'tr'
7199         };
7200             
7201         if(this.cls){
7202             cfg.cls = this.cls;
7203         }
7204         if(this.align){
7205             cfg.align = this.align;
7206         }
7207         if(this.bgcolor){
7208             cfg.bgcolor = this.bgcolor;
7209         }
7210         if(this.charoff){
7211             cfg.charoff = this.charoff;
7212         }
7213         if(this.valign){
7214             cfg.valign = this.valign;
7215         }
7216         
7217         return cfg;
7218     }
7219    
7220 });
7221
7222  
7223
7224  /*
7225  * - LGPL
7226  *
7227  * table body
7228  * 
7229  */
7230
7231 /**
7232  * @class Roo.bootstrap.TableBody
7233  * @extends Roo.bootstrap.Component
7234  * Bootstrap TableBody class
7235  * @cfg {String} cls element class
7236  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7237  * @cfg {String} align Aligns the content inside the element
7238  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7239  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7240  * 
7241  * @constructor
7242  * Create a new TableBody
7243  * @param {Object} config The config object
7244  */
7245
7246 Roo.bootstrap.TableBody = function(config){
7247     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7248 };
7249
7250 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7251     
7252     cls: false,
7253     tag: false,
7254     align: false,
7255     charoff: false,
7256     valign: false,
7257     
7258     getAutoCreate : function(){
7259         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7260         
7261         cfg = {
7262             tag: 'tbody'
7263         };
7264             
7265         if (this.cls) {
7266             cfg.cls=this.cls
7267         }
7268         if(this.tag){
7269             cfg.tag = this.tag;
7270         }
7271         
7272         if(this.align){
7273             cfg.align = this.align;
7274         }
7275         if(this.charoff){
7276             cfg.charoff = this.charoff;
7277         }
7278         if(this.valign){
7279             cfg.valign = this.valign;
7280         }
7281         
7282         return cfg;
7283     }
7284     
7285     
7286 //    initEvents : function()
7287 //    {
7288 //        
7289 //        if(!this.store){
7290 //            return;
7291 //        }
7292 //        
7293 //        this.store = Roo.factory(this.store, Roo.data);
7294 //        this.store.on('load', this.onLoad, this);
7295 //        
7296 //        this.store.load();
7297 //        
7298 //    },
7299 //    
7300 //    onLoad: function () 
7301 //    {   
7302 //        this.fireEvent('load', this);
7303 //    }
7304 //    
7305 //   
7306 });
7307
7308  
7309
7310  /*
7311  * Based on:
7312  * Ext JS Library 1.1.1
7313  * Copyright(c) 2006-2007, Ext JS, LLC.
7314  *
7315  * Originally Released Under LGPL - original licence link has changed is not relivant.
7316  *
7317  * Fork - LGPL
7318  * <script type="text/javascript">
7319  */
7320
7321 // as we use this in bootstrap.
7322 Roo.namespace('Roo.form');
7323  /**
7324  * @class Roo.form.Action
7325  * Internal Class used to handle form actions
7326  * @constructor
7327  * @param {Roo.form.BasicForm} el The form element or its id
7328  * @param {Object} config Configuration options
7329  */
7330
7331  
7332  
7333 // define the action interface
7334 Roo.form.Action = function(form, options){
7335     this.form = form;
7336     this.options = options || {};
7337 };
7338 /**
7339  * Client Validation Failed
7340  * @const 
7341  */
7342 Roo.form.Action.CLIENT_INVALID = 'client';
7343 /**
7344  * Server Validation Failed
7345  * @const 
7346  */
7347 Roo.form.Action.SERVER_INVALID = 'server';
7348  /**
7349  * Connect to Server Failed
7350  * @const 
7351  */
7352 Roo.form.Action.CONNECT_FAILURE = 'connect';
7353 /**
7354  * Reading Data from Server Failed
7355  * @const 
7356  */
7357 Roo.form.Action.LOAD_FAILURE = 'load';
7358
7359 Roo.form.Action.prototype = {
7360     type : 'default',
7361     failureType : undefined,
7362     response : undefined,
7363     result : undefined,
7364
7365     // interface method
7366     run : function(options){
7367
7368     },
7369
7370     // interface method
7371     success : function(response){
7372
7373     },
7374
7375     // interface method
7376     handleResponse : function(response){
7377
7378     },
7379
7380     // default connection failure
7381     failure : function(response){
7382         
7383         this.response = response;
7384         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7385         this.form.afterAction(this, false);
7386     },
7387
7388     processResponse : function(response){
7389         this.response = response;
7390         if(!response.responseText){
7391             return true;
7392         }
7393         this.result = this.handleResponse(response);
7394         return this.result;
7395     },
7396
7397     // utility functions used internally
7398     getUrl : function(appendParams){
7399         var url = this.options.url || this.form.url || this.form.el.dom.action;
7400         if(appendParams){
7401             var p = this.getParams();
7402             if(p){
7403                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7404             }
7405         }
7406         return url;
7407     },
7408
7409     getMethod : function(){
7410         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7411     },
7412
7413     getParams : function(){
7414         var bp = this.form.baseParams;
7415         var p = this.options.params;
7416         if(p){
7417             if(typeof p == "object"){
7418                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7419             }else if(typeof p == 'string' && bp){
7420                 p += '&' + Roo.urlEncode(bp);
7421             }
7422         }else if(bp){
7423             p = Roo.urlEncode(bp);
7424         }
7425         return p;
7426     },
7427
7428     createCallback : function(){
7429         return {
7430             success: this.success,
7431             failure: this.failure,
7432             scope: this,
7433             timeout: (this.form.timeout*1000),
7434             upload: this.form.fileUpload ? this.success : undefined
7435         };
7436     }
7437 };
7438
7439 Roo.form.Action.Submit = function(form, options){
7440     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7441 };
7442
7443 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7444     type : 'submit',
7445
7446     haveProgress : false,
7447     uploadComplete : false,
7448     
7449     // uploadProgress indicator.
7450     uploadProgress : function()
7451     {
7452         if (!this.form.progressUrl) {
7453             return;
7454         }
7455         
7456         if (!this.haveProgress) {
7457             Roo.MessageBox.progress("Uploading", "Uploading");
7458         }
7459         if (this.uploadComplete) {
7460            Roo.MessageBox.hide();
7461            return;
7462         }
7463         
7464         this.haveProgress = true;
7465    
7466         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7467         
7468         var c = new Roo.data.Connection();
7469         c.request({
7470             url : this.form.progressUrl,
7471             params: {
7472                 id : uid
7473             },
7474             method: 'GET',
7475             success : function(req){
7476                //console.log(data);
7477                 var rdata = false;
7478                 var edata;
7479                 try  {
7480                    rdata = Roo.decode(req.responseText)
7481                 } catch (e) {
7482                     Roo.log("Invalid data from server..");
7483                     Roo.log(edata);
7484                     return;
7485                 }
7486                 if (!rdata || !rdata.success) {
7487                     Roo.log(rdata);
7488                     Roo.MessageBox.alert(Roo.encode(rdata));
7489                     return;
7490                 }
7491                 var data = rdata.data;
7492                 
7493                 if (this.uploadComplete) {
7494                    Roo.MessageBox.hide();
7495                    return;
7496                 }
7497                    
7498                 if (data){
7499                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7500                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7501                     );
7502                 }
7503                 this.uploadProgress.defer(2000,this);
7504             },
7505        
7506             failure: function(data) {
7507                 Roo.log('progress url failed ');
7508                 Roo.log(data);
7509             },
7510             scope : this
7511         });
7512            
7513     },
7514     
7515     
7516     run : function()
7517     {
7518         // run get Values on the form, so it syncs any secondary forms.
7519         this.form.getValues();
7520         
7521         var o = this.options;
7522         var method = this.getMethod();
7523         var isPost = method == 'POST';
7524         if(o.clientValidation === false || this.form.isValid()){
7525             
7526             if (this.form.progressUrl) {
7527                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7528                     (new Date() * 1) + '' + Math.random());
7529                     
7530             } 
7531             
7532             
7533             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7534                 form:this.form.el.dom,
7535                 url:this.getUrl(!isPost),
7536                 method: method,
7537                 params:isPost ? this.getParams() : null,
7538                 isUpload: this.form.fileUpload
7539             }));
7540             
7541             this.uploadProgress();
7542
7543         }else if (o.clientValidation !== false){ // client validation failed
7544             this.failureType = Roo.form.Action.CLIENT_INVALID;
7545             this.form.afterAction(this, false);
7546         }
7547     },
7548
7549     success : function(response)
7550     {
7551         this.uploadComplete= true;
7552         if (this.haveProgress) {
7553             Roo.MessageBox.hide();
7554         }
7555         
7556         
7557         var result = this.processResponse(response);
7558         if(result === true || result.success){
7559             this.form.afterAction(this, true);
7560             return;
7561         }
7562         if(result.errors){
7563             this.form.markInvalid(result.errors);
7564             this.failureType = Roo.form.Action.SERVER_INVALID;
7565         }
7566         this.form.afterAction(this, false);
7567     },
7568     failure : function(response)
7569     {
7570         this.uploadComplete= true;
7571         if (this.haveProgress) {
7572             Roo.MessageBox.hide();
7573         }
7574         
7575         this.response = response;
7576         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7577         this.form.afterAction(this, false);
7578     },
7579     
7580     handleResponse : function(response){
7581         if(this.form.errorReader){
7582             var rs = this.form.errorReader.read(response);
7583             var errors = [];
7584             if(rs.records){
7585                 for(var i = 0, len = rs.records.length; i < len; i++) {
7586                     var r = rs.records[i];
7587                     errors[i] = r.data;
7588                 }
7589             }
7590             if(errors.length < 1){
7591                 errors = null;
7592             }
7593             return {
7594                 success : rs.success,
7595                 errors : errors
7596             };
7597         }
7598         var ret = false;
7599         try {
7600             ret = Roo.decode(response.responseText);
7601         } catch (e) {
7602             ret = {
7603                 success: false,
7604                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7605                 errors : []
7606             };
7607         }
7608         return ret;
7609         
7610     }
7611 });
7612
7613
7614 Roo.form.Action.Load = function(form, options){
7615     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7616     this.reader = this.form.reader;
7617 };
7618
7619 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7620     type : 'load',
7621
7622     run : function(){
7623         
7624         Roo.Ajax.request(Roo.apply(
7625                 this.createCallback(), {
7626                     method:this.getMethod(),
7627                     url:this.getUrl(false),
7628                     params:this.getParams()
7629         }));
7630     },
7631
7632     success : function(response){
7633         
7634         var result = this.processResponse(response);
7635         if(result === true || !result.success || !result.data){
7636             this.failureType = Roo.form.Action.LOAD_FAILURE;
7637             this.form.afterAction(this, false);
7638             return;
7639         }
7640         this.form.clearInvalid();
7641         this.form.setValues(result.data);
7642         this.form.afterAction(this, true);
7643     },
7644
7645     handleResponse : function(response){
7646         if(this.form.reader){
7647             var rs = this.form.reader.read(response);
7648             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7649             return {
7650                 success : rs.success,
7651                 data : data
7652             };
7653         }
7654         return Roo.decode(response.responseText);
7655     }
7656 });
7657
7658 Roo.form.Action.ACTION_TYPES = {
7659     'load' : Roo.form.Action.Load,
7660     'submit' : Roo.form.Action.Submit
7661 };/*
7662  * - LGPL
7663  *
7664  * form
7665  *
7666  */
7667
7668 /**
7669  * @class Roo.bootstrap.Form
7670  * @extends Roo.bootstrap.Component
7671  * Bootstrap Form class
7672  * @cfg {String} method  GET | POST (default POST)
7673  * @cfg {String} labelAlign top | left (default top)
7674  * @cfg {String} align left  | right - for navbars
7675  * @cfg {Boolean} loadMask load mask when submit (default true)
7676
7677  *
7678  * @constructor
7679  * Create a new Form
7680  * @param {Object} config The config object
7681  */
7682
7683
7684 Roo.bootstrap.Form = function(config){
7685     
7686     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7687     
7688     Roo.bootstrap.Form.popover.apply();
7689     
7690     this.addEvents({
7691         /**
7692          * @event clientvalidation
7693          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7694          * @param {Form} this
7695          * @param {Boolean} valid true if the form has passed client-side validation
7696          */
7697         clientvalidation: true,
7698         /**
7699          * @event beforeaction
7700          * Fires before any action is performed. Return false to cancel the action.
7701          * @param {Form} this
7702          * @param {Action} action The action to be performed
7703          */
7704         beforeaction: true,
7705         /**
7706          * @event actionfailed
7707          * Fires when an action fails.
7708          * @param {Form} this
7709          * @param {Action} action The action that failed
7710          */
7711         actionfailed : true,
7712         /**
7713          * @event actioncomplete
7714          * Fires when an action is completed.
7715          * @param {Form} this
7716          * @param {Action} action The action that completed
7717          */
7718         actioncomplete : true
7719     });
7720 };
7721
7722 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7723
7724      /**
7725      * @cfg {String} method
7726      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7727      */
7728     method : 'POST',
7729     /**
7730      * @cfg {String} url
7731      * The URL to use for form actions if one isn't supplied in the action options.
7732      */
7733     /**
7734      * @cfg {Boolean} fileUpload
7735      * Set to true if this form is a file upload.
7736      */
7737
7738     /**
7739      * @cfg {Object} baseParams
7740      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7741      */
7742
7743     /**
7744      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7745      */
7746     timeout: 30,
7747     /**
7748      * @cfg {Sting} align (left|right) for navbar forms
7749      */
7750     align : 'left',
7751
7752     // private
7753     activeAction : null,
7754
7755     /**
7756      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7757      * element by passing it or its id or mask the form itself by passing in true.
7758      * @type Mixed
7759      */
7760     waitMsgTarget : false,
7761
7762     loadMask : true,
7763     
7764     /**
7765      * @cfg {Boolean} errorMask (true|false) default false
7766      */
7767     errorMask : false,
7768     
7769     /**
7770      * @cfg {Number} maskOffset Default 100
7771      */
7772     maskOffset : 100,
7773     
7774     /**
7775      * @cfg {Boolean} maskBody
7776      */
7777     maskBody : false,
7778
7779     getAutoCreate : function(){
7780
7781         var cfg = {
7782             tag: 'form',
7783             method : this.method || 'POST',
7784             id : this.id || Roo.id(),
7785             cls : ''
7786         };
7787         if (this.parent().xtype.match(/^Nav/)) {
7788             cfg.cls = 'navbar-form navbar-' + this.align;
7789
7790         }
7791
7792         if (this.labelAlign == 'left' ) {
7793             cfg.cls += ' form-horizontal';
7794         }
7795
7796
7797         return cfg;
7798     },
7799     initEvents : function()
7800     {
7801         this.el.on('submit', this.onSubmit, this);
7802         // this was added as random key presses on the form where triggering form submit.
7803         this.el.on('keypress', function(e) {
7804             if (e.getCharCode() != 13) {
7805                 return true;
7806             }
7807             // we might need to allow it for textareas.. and some other items.
7808             // check e.getTarget().
7809
7810             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7811                 return true;
7812             }
7813
7814             Roo.log("keypress blocked");
7815
7816             e.preventDefault();
7817             return false;
7818         });
7819         
7820     },
7821     // private
7822     onSubmit : function(e){
7823         e.stopEvent();
7824     },
7825
7826      /**
7827      * Returns true if client-side validation on the form is successful.
7828      * @return Boolean
7829      */
7830     isValid : function(){
7831         var items = this.getItems();
7832         var valid = true;
7833         var target = false;
7834         
7835         items.each(function(f){
7836             
7837             if(f.validate()){
7838                 return;
7839             }
7840             
7841             valid = false;
7842
7843             if(!target && f.el.isVisible(true)){
7844                 target = f;
7845             }
7846            
7847         });
7848         
7849         if(this.errorMask && !valid){
7850             Roo.bootstrap.Form.popover.mask(this, target);
7851         }
7852         
7853         return valid;
7854     },
7855     
7856     /**
7857      * Returns true if any fields in this form have changed since their original load.
7858      * @return Boolean
7859      */
7860     isDirty : function(){
7861         var dirty = false;
7862         var items = this.getItems();
7863         items.each(function(f){
7864            if(f.isDirty()){
7865                dirty = true;
7866                return false;
7867            }
7868            return true;
7869         });
7870         return dirty;
7871     },
7872      /**
7873      * Performs a predefined action (submit or load) or custom actions you define on this form.
7874      * @param {String} actionName The name of the action type
7875      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7876      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7877      * accept other config options):
7878      * <pre>
7879 Property          Type             Description
7880 ----------------  ---------------  ----------------------------------------------------------------------------------
7881 url               String           The url for the action (defaults to the form's url)
7882 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7883 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7884 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7885                                    validate the form on the client (defaults to false)
7886      * </pre>
7887      * @return {BasicForm} this
7888      */
7889     doAction : function(action, options){
7890         if(typeof action == 'string'){
7891             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7892         }
7893         if(this.fireEvent('beforeaction', this, action) !== false){
7894             this.beforeAction(action);
7895             action.run.defer(100, action);
7896         }
7897         return this;
7898     },
7899
7900     // private
7901     beforeAction : function(action){
7902         var o = action.options;
7903         
7904         if(this.loadMask){
7905             
7906             if(this.maskBody){
7907                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7908             } else {
7909                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7910             }
7911         }
7912         // not really supported yet.. ??
7913
7914         //if(this.waitMsgTarget === true){
7915         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7916         //}else if(this.waitMsgTarget){
7917         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7918         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7919         //}else {
7920         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7921        // }
7922
7923     },
7924
7925     // private
7926     afterAction : function(action, success){
7927         this.activeAction = null;
7928         var o = action.options;
7929
7930         if(this.loadMask){
7931             
7932             if(this.maskBody){
7933                 Roo.get(document.body).unmask();
7934             } else {
7935                 this.el.unmask();
7936             }
7937         }
7938         
7939         //if(this.waitMsgTarget === true){
7940 //            this.el.unmask();
7941         //}else if(this.waitMsgTarget){
7942         //    this.waitMsgTarget.unmask();
7943         //}else{
7944         //    Roo.MessageBox.updateProgress(1);
7945         //    Roo.MessageBox.hide();
7946        // }
7947         //
7948         if(success){
7949             if(o.reset){
7950                 this.reset();
7951             }
7952             Roo.callback(o.success, o.scope, [this, action]);
7953             this.fireEvent('actioncomplete', this, action);
7954
7955         }else{
7956
7957             // failure condition..
7958             // we have a scenario where updates need confirming.
7959             // eg. if a locking scenario exists..
7960             // we look for { errors : { needs_confirm : true }} in the response.
7961             if (
7962                 (typeof(action.result) != 'undefined')  &&
7963                 (typeof(action.result.errors) != 'undefined')  &&
7964                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7965            ){
7966                 var _t = this;
7967                 Roo.log("not supported yet");
7968                  /*
7969
7970                 Roo.MessageBox.confirm(
7971                     "Change requires confirmation",
7972                     action.result.errorMsg,
7973                     function(r) {
7974                         if (r != 'yes') {
7975                             return;
7976                         }
7977                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7978                     }
7979
7980                 );
7981                 */
7982
7983
7984                 return;
7985             }
7986
7987             Roo.callback(o.failure, o.scope, [this, action]);
7988             // show an error message if no failed handler is set..
7989             if (!this.hasListener('actionfailed')) {
7990                 Roo.log("need to add dialog support");
7991                 /*
7992                 Roo.MessageBox.alert("Error",
7993                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7994                         action.result.errorMsg :
7995                         "Saving Failed, please check your entries or try again"
7996                 );
7997                 */
7998             }
7999
8000             this.fireEvent('actionfailed', this, action);
8001         }
8002
8003     },
8004     /**
8005      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8006      * @param {String} id The value to search for
8007      * @return Field
8008      */
8009     findField : function(id){
8010         var items = this.getItems();
8011         var field = items.get(id);
8012         if(!field){
8013              items.each(function(f){
8014                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8015                     field = f;
8016                     return false;
8017                 }
8018                 return true;
8019             });
8020         }
8021         return field || null;
8022     },
8023      /**
8024      * Mark fields in this form invalid in bulk.
8025      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8026      * @return {BasicForm} this
8027      */
8028     markInvalid : function(errors){
8029         if(errors instanceof Array){
8030             for(var i = 0, len = errors.length; i < len; i++){
8031                 var fieldError = errors[i];
8032                 var f = this.findField(fieldError.id);
8033                 if(f){
8034                     f.markInvalid(fieldError.msg);
8035                 }
8036             }
8037         }else{
8038             var field, id;
8039             for(id in errors){
8040                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8041                     field.markInvalid(errors[id]);
8042                 }
8043             }
8044         }
8045         //Roo.each(this.childForms || [], function (f) {
8046         //    f.markInvalid(errors);
8047         //});
8048
8049         return this;
8050     },
8051
8052     /**
8053      * Set values for fields in this form in bulk.
8054      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8055      * @return {BasicForm} this
8056      */
8057     setValues : function(values){
8058         if(values instanceof Array){ // array of objects
8059             for(var i = 0, len = values.length; i < len; i++){
8060                 var v = values[i];
8061                 var f = this.findField(v.id);
8062                 if(f){
8063                     f.setValue(v.value);
8064                     if(this.trackResetOnLoad){
8065                         f.originalValue = f.getValue();
8066                     }
8067                 }
8068             }
8069         }else{ // object hash
8070             var field, id;
8071             for(id in values){
8072                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8073
8074                     if (field.setFromData &&
8075                         field.valueField &&
8076                         field.displayField &&
8077                         // combos' with local stores can
8078                         // be queried via setValue()
8079                         // to set their value..
8080                         (field.store && !field.store.isLocal)
8081                         ) {
8082                         // it's a combo
8083                         var sd = { };
8084                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8085                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8086                         field.setFromData(sd);
8087
8088                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8089                         
8090                         field.setFromData(values);
8091                         
8092                     } else {
8093                         field.setValue(values[id]);
8094                     }
8095
8096
8097                     if(this.trackResetOnLoad){
8098                         field.originalValue = field.getValue();
8099                     }
8100                 }
8101             }
8102         }
8103
8104         //Roo.each(this.childForms || [], function (f) {
8105         //    f.setValues(values);
8106         //});
8107
8108         return this;
8109     },
8110
8111     /**
8112      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8113      * they are returned as an array.
8114      * @param {Boolean} asString
8115      * @return {Object}
8116      */
8117     getValues : function(asString){
8118         //if (this.childForms) {
8119             // copy values from the child forms
8120         //    Roo.each(this.childForms, function (f) {
8121         //        this.setValues(f.getValues());
8122         //    }, this);
8123         //}
8124
8125
8126
8127         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8128         if(asString === true){
8129             return fs;
8130         }
8131         return Roo.urlDecode(fs);
8132     },
8133
8134     /**
8135      * Returns the fields in this form as an object with key/value pairs.
8136      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8137      * @return {Object}
8138      */
8139     getFieldValues : function(with_hidden)
8140     {
8141         var items = this.getItems();
8142         var ret = {};
8143         items.each(function(f){
8144             
8145             if (!f.getName()) {
8146                 return;
8147             }
8148             
8149             var v = f.getValue();
8150             
8151             if (f.inputType =='radio') {
8152                 if (typeof(ret[f.getName()]) == 'undefined') {
8153                     ret[f.getName()] = ''; // empty..
8154                 }
8155
8156                 if (!f.el.dom.checked) {
8157                     return;
8158
8159                 }
8160                 v = f.el.dom.value;
8161
8162             }
8163             
8164             if(f.xtype == 'MoneyField'){
8165                 ret[f.currencyName] = f.getCurrency();
8166             }
8167
8168             // not sure if this supported any more..
8169             if ((typeof(v) == 'object') && f.getRawValue) {
8170                 v = f.getRawValue() ; // dates..
8171             }
8172             // combo boxes where name != hiddenName...
8173             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8174                 ret[f.name] = f.getRawValue();
8175             }
8176             ret[f.getName()] = v;
8177         });
8178
8179         return ret;
8180     },
8181
8182     /**
8183      * Clears all invalid messages in this form.
8184      * @return {BasicForm} this
8185      */
8186     clearInvalid : function(){
8187         var items = this.getItems();
8188
8189         items.each(function(f){
8190            f.clearInvalid();
8191         });
8192
8193         return this;
8194     },
8195
8196     /**
8197      * Resets this form.
8198      * @return {BasicForm} this
8199      */
8200     reset : function(){
8201         var items = this.getItems();
8202         items.each(function(f){
8203             f.reset();
8204         });
8205
8206         Roo.each(this.childForms || [], function (f) {
8207             f.reset();
8208         });
8209
8210
8211         return this;
8212     },
8213     
8214     getItems : function()
8215     {
8216         var r=new Roo.util.MixedCollection(false, function(o){
8217             return o.id || (o.id = Roo.id());
8218         });
8219         var iter = function(el) {
8220             if (el.inputEl) {
8221                 r.add(el);
8222             }
8223             if (!el.items) {
8224                 return;
8225             }
8226             Roo.each(el.items,function(e) {
8227                 iter(e);
8228             });
8229         };
8230
8231         iter(this);
8232         return r;
8233     },
8234     
8235     hideFields : function(items)
8236     {
8237         Roo.each(items, function(i){
8238             
8239             var f = this.findField(i);
8240             
8241             if(!f){
8242                 return;
8243             }
8244             
8245             if(f.xtype == 'DateField'){
8246                 f.setVisible(false);
8247                 return;
8248             }
8249             
8250             f.hide();
8251             
8252         }, this);
8253     },
8254     
8255     showFields : function(items)
8256     {
8257         Roo.each(items, function(i){
8258             
8259             var f = this.findField(i);
8260             
8261             if(!f){
8262                 return;
8263             }
8264             
8265             if(f.xtype == 'DateField'){
8266                 f.setVisible(true);
8267                 return;
8268             }
8269             
8270             f.show();
8271             
8272         }, this);
8273     }
8274
8275 });
8276
8277 Roo.apply(Roo.bootstrap.Form, {
8278     
8279     popover : {
8280         
8281         padding : 5,
8282         
8283         isApplied : false,
8284         
8285         isMasked : false,
8286         
8287         form : false,
8288         
8289         target : false,
8290         
8291         toolTip : false,
8292         
8293         intervalID : false,
8294         
8295         maskEl : false,
8296         
8297         apply : function()
8298         {
8299             if(this.isApplied){
8300                 return;
8301             }
8302             
8303             this.maskEl = {
8304                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8305                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8306                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8307                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8308             };
8309             
8310             this.maskEl.top.enableDisplayMode("block");
8311             this.maskEl.left.enableDisplayMode("block");
8312             this.maskEl.bottom.enableDisplayMode("block");
8313             this.maskEl.right.enableDisplayMode("block");
8314             
8315             this.toolTip = new Roo.bootstrap.Tooltip({
8316                 cls : 'roo-form-error-popover',
8317                 alignment : {
8318                     'left' : ['r-l', [-2,0], 'right'],
8319                     'right' : ['l-r', [2,0], 'left'],
8320                     'bottom' : ['tl-bl', [0,2], 'top'],
8321                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8322                 }
8323             });
8324             
8325             this.toolTip.render(Roo.get(document.body));
8326
8327             this.toolTip.el.enableDisplayMode("block");
8328             
8329             Roo.get(document.body).on('click', function(){
8330                 this.unmask();
8331             }, this);
8332             
8333             Roo.get(document.body).on('touchstart', function(){
8334                 this.unmask();
8335             }, this);
8336             
8337             this.isApplied = true
8338         },
8339         
8340         mask : function(form, target)
8341         {
8342             this.form = form;
8343             
8344             this.target = target;
8345             
8346             if(!this.form.errorMask || !target.el){
8347                 return;
8348             }
8349             
8350             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8351             
8352             Roo.log(scrollable);
8353             
8354             var ot = this.target.el.calcOffsetsTo(scrollable);
8355             
8356             var scrollTo = ot[1] - this.form.maskOffset;
8357             
8358             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8359             
8360             scrollable.scrollTo('top', scrollTo);
8361             
8362             var box = this.target.el.getBox();
8363             Roo.log(box);
8364             var zIndex = Roo.bootstrap.Modal.zIndex++;
8365
8366             
8367             this.maskEl.top.setStyle('position', 'absolute');
8368             this.maskEl.top.setStyle('z-index', zIndex);
8369             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8370             this.maskEl.top.setLeft(0);
8371             this.maskEl.top.setTop(0);
8372             this.maskEl.top.show();
8373             
8374             this.maskEl.left.setStyle('position', 'absolute');
8375             this.maskEl.left.setStyle('z-index', zIndex);
8376             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8377             this.maskEl.left.setLeft(0);
8378             this.maskEl.left.setTop(box.y - this.padding);
8379             this.maskEl.left.show();
8380
8381             this.maskEl.bottom.setStyle('position', 'absolute');
8382             this.maskEl.bottom.setStyle('z-index', zIndex);
8383             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8384             this.maskEl.bottom.setLeft(0);
8385             this.maskEl.bottom.setTop(box.bottom + this.padding);
8386             this.maskEl.bottom.show();
8387
8388             this.maskEl.right.setStyle('position', 'absolute');
8389             this.maskEl.right.setStyle('z-index', zIndex);
8390             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8391             this.maskEl.right.setLeft(box.right + this.padding);
8392             this.maskEl.right.setTop(box.y - this.padding);
8393             this.maskEl.right.show();
8394
8395             this.toolTip.bindEl = this.target.el;
8396
8397             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8398
8399             var tip = this.target.blankText;
8400
8401             if(this.target.getValue() !== '' ) {
8402                 
8403                 if (this.target.invalidText.length) {
8404                     tip = this.target.invalidText;
8405                 } else if (this.target.regexText.length){
8406                     tip = this.target.regexText;
8407                 }
8408             }
8409
8410             this.toolTip.show(tip);
8411
8412             this.intervalID = window.setInterval(function() {
8413                 Roo.bootstrap.Form.popover.unmask();
8414             }, 10000);
8415
8416             window.onwheel = function(){ return false;};
8417             
8418             (function(){ this.isMasked = true; }).defer(500, this);
8419             
8420         },
8421         
8422         unmask : function()
8423         {
8424             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8425                 return;
8426             }
8427             
8428             this.maskEl.top.setStyle('position', 'absolute');
8429             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8430             this.maskEl.top.hide();
8431
8432             this.maskEl.left.setStyle('position', 'absolute');
8433             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8434             this.maskEl.left.hide();
8435
8436             this.maskEl.bottom.setStyle('position', 'absolute');
8437             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8438             this.maskEl.bottom.hide();
8439
8440             this.maskEl.right.setStyle('position', 'absolute');
8441             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8442             this.maskEl.right.hide();
8443             
8444             this.toolTip.hide();
8445             
8446             this.toolTip.el.hide();
8447             
8448             window.onwheel = function(){ return true;};
8449             
8450             if(this.intervalID){
8451                 window.clearInterval(this.intervalID);
8452                 this.intervalID = false;
8453             }
8454             
8455             this.isMasked = false;
8456             
8457         }
8458         
8459     }
8460     
8461 });
8462
8463 /*
8464  * Based on:
8465  * Ext JS Library 1.1.1
8466  * Copyright(c) 2006-2007, Ext JS, LLC.
8467  *
8468  * Originally Released Under LGPL - original licence link has changed is not relivant.
8469  *
8470  * Fork - LGPL
8471  * <script type="text/javascript">
8472  */
8473 /**
8474  * @class Roo.form.VTypes
8475  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8476  * @singleton
8477  */
8478 Roo.form.VTypes = function(){
8479     // closure these in so they are only created once.
8480     var alpha = /^[a-zA-Z_]+$/;
8481     var alphanum = /^[a-zA-Z0-9_]+$/;
8482     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8483     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8484
8485     // All these messages and functions are configurable
8486     return {
8487         /**
8488          * The function used to validate email addresses
8489          * @param {String} value The email address
8490          */
8491         'email' : function(v){
8492             return email.test(v);
8493         },
8494         /**
8495          * The error text to display when the email validation function returns false
8496          * @type String
8497          */
8498         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8499         /**
8500          * The keystroke filter mask to be applied on email input
8501          * @type RegExp
8502          */
8503         'emailMask' : /[a-z0-9_\.\-@]/i,
8504
8505         /**
8506          * The function used to validate URLs
8507          * @param {String} value The URL
8508          */
8509         'url' : function(v){
8510             return url.test(v);
8511         },
8512         /**
8513          * The error text to display when the url validation function returns false
8514          * @type String
8515          */
8516         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8517         
8518         /**
8519          * The function used to validate alpha values
8520          * @param {String} value The value
8521          */
8522         'alpha' : function(v){
8523             return alpha.test(v);
8524         },
8525         /**
8526          * The error text to display when the alpha validation function returns false
8527          * @type String
8528          */
8529         'alphaText' : 'This field should only contain letters and _',
8530         /**
8531          * The keystroke filter mask to be applied on alpha input
8532          * @type RegExp
8533          */
8534         'alphaMask' : /[a-z_]/i,
8535
8536         /**
8537          * The function used to validate alphanumeric values
8538          * @param {String} value The value
8539          */
8540         'alphanum' : function(v){
8541             return alphanum.test(v);
8542         },
8543         /**
8544          * The error text to display when the alphanumeric validation function returns false
8545          * @type String
8546          */
8547         'alphanumText' : 'This field should only contain letters, numbers and _',
8548         /**
8549          * The keystroke filter mask to be applied on alphanumeric input
8550          * @type RegExp
8551          */
8552         'alphanumMask' : /[a-z0-9_]/i
8553     };
8554 }();/*
8555  * - LGPL
8556  *
8557  * Input
8558  * 
8559  */
8560
8561 /**
8562  * @class Roo.bootstrap.Input
8563  * @extends Roo.bootstrap.Component
8564  * Bootstrap Input class
8565  * @cfg {Boolean} disabled is it disabled
8566  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8567  * @cfg {String} name name of the input
8568  * @cfg {string} fieldLabel - the label associated
8569  * @cfg {string} placeholder - placeholder to put in text.
8570  * @cfg {string}  before - input group add on before
8571  * @cfg {string} after - input group add on after
8572  * @cfg {string} size - (lg|sm) or leave empty..
8573  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8574  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8575  * @cfg {Number} md colspan out of 12 for computer-sized screens
8576  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8577  * @cfg {string} value default value of the input
8578  * @cfg {Number} labelWidth set the width of label 
8579  * @cfg {Number} labellg set the width of label (1-12)
8580  * @cfg {Number} labelmd set the width of label (1-12)
8581  * @cfg {Number} labelsm set the width of label (1-12)
8582  * @cfg {Number} labelxs set the width of label (1-12)
8583  * @cfg {String} labelAlign (top|left)
8584  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8585  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8586  * @cfg {String} indicatorpos (left|right) default left
8587  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8588  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8589
8590  * @cfg {String} align (left|center|right) Default left
8591  * @cfg {Boolean} forceFeedback (true|false) Default false
8592  * 
8593  * @constructor
8594  * Create a new Input
8595  * @param {Object} config The config object
8596  */
8597
8598 Roo.bootstrap.Input = function(config){
8599     
8600     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8601     
8602     this.addEvents({
8603         /**
8604          * @event focus
8605          * Fires when this field receives input focus.
8606          * @param {Roo.form.Field} this
8607          */
8608         focus : true,
8609         /**
8610          * @event blur
8611          * Fires when this field loses input focus.
8612          * @param {Roo.form.Field} this
8613          */
8614         blur : true,
8615         /**
8616          * @event specialkey
8617          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8618          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8619          * @param {Roo.form.Field} this
8620          * @param {Roo.EventObject} e The event object
8621          */
8622         specialkey : true,
8623         /**
8624          * @event change
8625          * Fires just before the field blurs if the field value has changed.
8626          * @param {Roo.form.Field} this
8627          * @param {Mixed} newValue The new value
8628          * @param {Mixed} oldValue The original value
8629          */
8630         change : true,
8631         /**
8632          * @event invalid
8633          * Fires after the field has been marked as invalid.
8634          * @param {Roo.form.Field} this
8635          * @param {String} msg The validation message
8636          */
8637         invalid : true,
8638         /**
8639          * @event valid
8640          * Fires after the field has been validated with no errors.
8641          * @param {Roo.form.Field} this
8642          */
8643         valid : true,
8644          /**
8645          * @event keyup
8646          * Fires after the key up
8647          * @param {Roo.form.Field} this
8648          * @param {Roo.EventObject}  e The event Object
8649          */
8650         keyup : true
8651     });
8652 };
8653
8654 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8655      /**
8656      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8657       automatic validation (defaults to "keyup").
8658      */
8659     validationEvent : "keyup",
8660      /**
8661      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8662      */
8663     validateOnBlur : true,
8664     /**
8665      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8666      */
8667     validationDelay : 250,
8668      /**
8669      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8670      */
8671     focusClass : "x-form-focus",  // not needed???
8672     
8673        
8674     /**
8675      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8676      */
8677     invalidClass : "has-warning",
8678     
8679     /**
8680      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8681      */
8682     validClass : "has-success",
8683     
8684     /**
8685      * @cfg {Boolean} hasFeedback (true|false) default true
8686      */
8687     hasFeedback : true,
8688     
8689     /**
8690      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8691      */
8692     invalidFeedbackClass : "glyphicon-warning-sign",
8693     
8694     /**
8695      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8696      */
8697     validFeedbackClass : "glyphicon-ok",
8698     
8699     /**
8700      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8701      */
8702     selectOnFocus : false,
8703     
8704      /**
8705      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8706      */
8707     maskRe : null,
8708        /**
8709      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8710      */
8711     vtype : null,
8712     
8713       /**
8714      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8715      */
8716     disableKeyFilter : false,
8717     
8718        /**
8719      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8720      */
8721     disabled : false,
8722      /**
8723      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8724      */
8725     allowBlank : true,
8726     /**
8727      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8728      */
8729     blankText : "Please complete this mandatory field",
8730     
8731      /**
8732      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8733      */
8734     minLength : 0,
8735     /**
8736      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8737      */
8738     maxLength : Number.MAX_VALUE,
8739     /**
8740      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8741      */
8742     minLengthText : "The minimum length for this field is {0}",
8743     /**
8744      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8745      */
8746     maxLengthText : "The maximum length for this field is {0}",
8747   
8748     
8749     /**
8750      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8751      * If available, this function will be called only after the basic validators all return true, and will be passed the
8752      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8753      */
8754     validator : null,
8755     /**
8756      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8757      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8758      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8759      */
8760     regex : null,
8761     /**
8762      * @cfg {String} regexText -- Depricated - use Invalid Text
8763      */
8764     regexText : "",
8765     
8766     /**
8767      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8768      */
8769     invalidText : "",
8770     
8771     
8772     
8773     autocomplete: false,
8774     
8775     
8776     fieldLabel : '',
8777     inputType : 'text',
8778     
8779     name : false,
8780     placeholder: false,
8781     before : false,
8782     after : false,
8783     size : false,
8784     hasFocus : false,
8785     preventMark: false,
8786     isFormField : true,
8787     value : '',
8788     labelWidth : 2,
8789     labelAlign : false,
8790     readOnly : false,
8791     align : false,
8792     formatedValue : false,
8793     forceFeedback : false,
8794     
8795     indicatorpos : 'left',
8796     
8797     labellg : 0,
8798     labelmd : 0,
8799     labelsm : 0,
8800     labelxs : 0,
8801     
8802     capture : '',
8803     accept : '',
8804     
8805     parentLabelAlign : function()
8806     {
8807         var parent = this;
8808         while (parent.parent()) {
8809             parent = parent.parent();
8810             if (typeof(parent.labelAlign) !='undefined') {
8811                 return parent.labelAlign;
8812             }
8813         }
8814         return 'left';
8815         
8816     },
8817     
8818     getAutoCreate : function()
8819     {
8820         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8821         
8822         var id = Roo.id();
8823         
8824         var cfg = {};
8825         
8826         if(this.inputType != 'hidden'){
8827             cfg.cls = 'form-group' //input-group
8828         }
8829         
8830         var input =  {
8831             tag: 'input',
8832             id : id,
8833             type : this.inputType,
8834             value : this.value,
8835             cls : 'form-control',
8836             placeholder : this.placeholder || '',
8837             autocomplete : this.autocomplete || 'new-password'
8838         };
8839         
8840         if(this.capture.length){
8841             input.capture = this.capture;
8842         }
8843         
8844         if(this.accept.length){
8845             input.accept = this.accept + "/*";
8846         }
8847         
8848         if(this.align){
8849             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8850         }
8851         
8852         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8853             input.maxLength = this.maxLength;
8854         }
8855         
8856         if (this.disabled) {
8857             input.disabled=true;
8858         }
8859         
8860         if (this.readOnly) {
8861             input.readonly=true;
8862         }
8863         
8864         if (this.name) {
8865             input.name = this.name;
8866         }
8867         
8868         if (this.size) {
8869             input.cls += ' input-' + this.size;
8870         }
8871         
8872         var settings=this;
8873         ['xs','sm','md','lg'].map(function(size){
8874             if (settings[size]) {
8875                 cfg.cls += ' col-' + size + '-' + settings[size];
8876             }
8877         });
8878         
8879         var inputblock = input;
8880         
8881         var feedback = {
8882             tag: 'span',
8883             cls: 'glyphicon form-control-feedback'
8884         };
8885             
8886         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8887             
8888             inputblock = {
8889                 cls : 'has-feedback',
8890                 cn :  [
8891                     input,
8892                     feedback
8893                 ] 
8894             };  
8895         }
8896         
8897         if (this.before || this.after) {
8898             
8899             inputblock = {
8900                 cls : 'input-group',
8901                 cn :  [] 
8902             };
8903             
8904             if (this.before && typeof(this.before) == 'string') {
8905                 
8906                 inputblock.cn.push({
8907                     tag :'span',
8908                     cls : 'roo-input-before input-group-addon',
8909                     html : this.before
8910                 });
8911             }
8912             if (this.before && typeof(this.before) == 'object') {
8913                 this.before = Roo.factory(this.before);
8914                 
8915                 inputblock.cn.push({
8916                     tag :'span',
8917                     cls : 'roo-input-before input-group-' +
8918                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8919                 });
8920             }
8921             
8922             inputblock.cn.push(input);
8923             
8924             if (this.after && typeof(this.after) == 'string') {
8925                 inputblock.cn.push({
8926                     tag :'span',
8927                     cls : 'roo-input-after input-group-addon',
8928                     html : this.after
8929                 });
8930             }
8931             if (this.after && typeof(this.after) == 'object') {
8932                 this.after = Roo.factory(this.after);
8933                 
8934                 inputblock.cn.push({
8935                     tag :'span',
8936                     cls : 'roo-input-after input-group-' +
8937                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8938                 });
8939             }
8940             
8941             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8942                 inputblock.cls += ' has-feedback';
8943                 inputblock.cn.push(feedback);
8944             }
8945         };
8946         
8947         if (align ==='left' && this.fieldLabel.length) {
8948             
8949             cfg.cls += ' roo-form-group-label-left';
8950             
8951             cfg.cn = [
8952                 {
8953                     tag : 'i',
8954                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8955                     tooltip : 'This field is required'
8956                 },
8957                 {
8958                     tag: 'label',
8959                     'for' :  id,
8960                     cls : 'control-label',
8961                     html : this.fieldLabel
8962
8963                 },
8964                 {
8965                     cls : "", 
8966                     cn: [
8967                         inputblock
8968                     ]
8969                 }
8970             ];
8971             
8972             var labelCfg = cfg.cn[1];
8973             var contentCfg = cfg.cn[2];
8974             
8975             if(this.indicatorpos == 'right'){
8976                 cfg.cn = [
8977                     {
8978                         tag: 'label',
8979                         'for' :  id,
8980                         cls : 'control-label',
8981                         cn : [
8982                             {
8983                                 tag : 'span',
8984                                 html : this.fieldLabel
8985                             },
8986                             {
8987                                 tag : 'i',
8988                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8989                                 tooltip : 'This field is required'
8990                             }
8991                         ]
8992                     },
8993                     {
8994                         cls : "",
8995                         cn: [
8996                             inputblock
8997                         ]
8998                     }
8999
9000                 ];
9001                 
9002                 labelCfg = cfg.cn[0];
9003                 contentCfg = cfg.cn[1];
9004             
9005             }
9006             
9007             if(this.labelWidth > 12){
9008                 labelCfg.style = "width: " + this.labelWidth + 'px';
9009             }
9010             
9011             if(this.labelWidth < 13 && this.labelmd == 0){
9012                 this.labelmd = this.labelWidth;
9013             }
9014             
9015             if(this.labellg > 0){
9016                 labelCfg.cls += ' col-lg-' + this.labellg;
9017                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9018             }
9019             
9020             if(this.labelmd > 0){
9021                 labelCfg.cls += ' col-md-' + this.labelmd;
9022                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9023             }
9024             
9025             if(this.labelsm > 0){
9026                 labelCfg.cls += ' col-sm-' + this.labelsm;
9027                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9028             }
9029             
9030             if(this.labelxs > 0){
9031                 labelCfg.cls += ' col-xs-' + this.labelxs;
9032                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9033             }
9034             
9035             
9036         } else if ( this.fieldLabel.length) {
9037                 
9038             cfg.cn = [
9039                 {
9040                     tag : 'i',
9041                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9042                     tooltip : 'This field is required'
9043                 },
9044                 {
9045                     tag: 'label',
9046                    //cls : 'input-group-addon',
9047                     html : this.fieldLabel
9048
9049                 },
9050
9051                inputblock
9052
9053            ];
9054            
9055            if(this.indicatorpos == 'right'){
9056                 
9057                 cfg.cn = [
9058                     {
9059                         tag: 'label',
9060                        //cls : 'input-group-addon',
9061                         html : this.fieldLabel
9062
9063                     },
9064                     {
9065                         tag : 'i',
9066                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9067                         tooltip : 'This field is required'
9068                     },
9069
9070                    inputblock
9071
9072                ];
9073
9074             }
9075
9076         } else {
9077             
9078             cfg.cn = [
9079
9080                     inputblock
9081
9082             ];
9083                 
9084                 
9085         };
9086         
9087         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9088            cfg.cls += ' navbar-form';
9089         }
9090         
9091         if (this.parentType === 'NavGroup') {
9092            cfg.cls += ' navbar-form';
9093            cfg.tag = 'li';
9094         }
9095         
9096         return cfg;
9097         
9098     },
9099     /**
9100      * return the real input element.
9101      */
9102     inputEl: function ()
9103     {
9104         return this.el.select('input.form-control',true).first();
9105     },
9106     
9107     tooltipEl : function()
9108     {
9109         return this.inputEl();
9110     },
9111     
9112     indicatorEl : function()
9113     {
9114         var indicator = this.el.select('i.roo-required-indicator',true).first();
9115         
9116         if(!indicator){
9117             return false;
9118         }
9119         
9120         return indicator;
9121         
9122     },
9123     
9124     setDisabled : function(v)
9125     {
9126         var i  = this.inputEl().dom;
9127         if (!v) {
9128             i.removeAttribute('disabled');
9129             return;
9130             
9131         }
9132         i.setAttribute('disabled','true');
9133     },
9134     initEvents : function()
9135     {
9136           
9137         this.inputEl().on("keydown" , this.fireKey,  this);
9138         this.inputEl().on("focus", this.onFocus,  this);
9139         this.inputEl().on("blur", this.onBlur,  this);
9140         
9141         this.inputEl().relayEvent('keyup', this);
9142         
9143         this.indicator = this.indicatorEl();
9144         
9145         if(this.indicator){
9146             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9147         }
9148  
9149         // reference to original value for reset
9150         this.originalValue = this.getValue();
9151         //Roo.form.TextField.superclass.initEvents.call(this);
9152         if(this.validationEvent == 'keyup'){
9153             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9154             this.inputEl().on('keyup', this.filterValidation, this);
9155         }
9156         else if(this.validationEvent !== false){
9157             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9158         }
9159         
9160         if(this.selectOnFocus){
9161             this.on("focus", this.preFocus, this);
9162             
9163         }
9164         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9165             this.inputEl().on("keypress", this.filterKeys, this);
9166         } else {
9167             this.inputEl().relayEvent('keypress', this);
9168         }
9169        /* if(this.grow){
9170             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9171             this.el.on("click", this.autoSize,  this);
9172         }
9173         */
9174         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9175             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9176         }
9177         
9178         if (typeof(this.before) == 'object') {
9179             this.before.render(this.el.select('.roo-input-before',true).first());
9180         }
9181         if (typeof(this.after) == 'object') {
9182             this.after.render(this.el.select('.roo-input-after',true).first());
9183         }
9184         
9185         this.inputEl().on('change', this.onChange, this);
9186         
9187     },
9188     filterValidation : function(e){
9189         if(!e.isNavKeyPress()){
9190             this.validationTask.delay(this.validationDelay);
9191         }
9192     },
9193      /**
9194      * Validates the field value
9195      * @return {Boolean} True if the value is valid, else false
9196      */
9197     validate : function(){
9198         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9199         if(this.disabled || this.validateValue(this.getRawValue())){
9200             this.markValid();
9201             return true;
9202         }
9203         
9204         this.markInvalid();
9205         return false;
9206     },
9207     
9208     
9209     /**
9210      * Validates a value according to the field's validation rules and marks the field as invalid
9211      * if the validation fails
9212      * @param {Mixed} value The value to validate
9213      * @return {Boolean} True if the value is valid, else false
9214      */
9215     validateValue : function(value)
9216     {
9217         if(this.getVisibilityEl().hasClass('hidden')){
9218             return true;
9219         }
9220         
9221         if(value.length < 1)  { // if it's blank
9222             if(this.allowBlank){
9223                 return true;
9224             }
9225             return false;
9226         }
9227         
9228         if(value.length < this.minLength){
9229             return false;
9230         }
9231         if(value.length > this.maxLength){
9232             return false;
9233         }
9234         if(this.vtype){
9235             var vt = Roo.form.VTypes;
9236             if(!vt[this.vtype](value, this)){
9237                 return false;
9238             }
9239         }
9240         if(typeof this.validator == "function"){
9241             var msg = this.validator(value);
9242             if(msg !== true){
9243                 return false;
9244             }
9245             if (typeof(msg) == 'string') {
9246                 this.invalidText = msg;
9247             }
9248         }
9249         
9250         if(this.regex && !this.regex.test(value)){
9251             return false;
9252         }
9253         
9254         return true;
9255     },
9256     
9257      // private
9258     fireKey : function(e){
9259         //Roo.log('field ' + e.getKey());
9260         if(e.isNavKeyPress()){
9261             this.fireEvent("specialkey", this, e);
9262         }
9263     },
9264     focus : function (selectText){
9265         if(this.rendered){
9266             this.inputEl().focus();
9267             if(selectText === true){
9268                 this.inputEl().dom.select();
9269             }
9270         }
9271         return this;
9272     } ,
9273     
9274     onFocus : function(){
9275         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9276            // this.el.addClass(this.focusClass);
9277         }
9278         if(!this.hasFocus){
9279             this.hasFocus = true;
9280             this.startValue = this.getValue();
9281             this.fireEvent("focus", this);
9282         }
9283     },
9284     
9285     beforeBlur : Roo.emptyFn,
9286
9287     
9288     // private
9289     onBlur : function(){
9290         this.beforeBlur();
9291         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9292             //this.el.removeClass(this.focusClass);
9293         }
9294         this.hasFocus = false;
9295         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9296             this.validate();
9297         }
9298         var v = this.getValue();
9299         if(String(v) !== String(this.startValue)){
9300             this.fireEvent('change', this, v, this.startValue);
9301         }
9302         this.fireEvent("blur", this);
9303     },
9304     
9305     onChange : function(e)
9306     {
9307         var v = this.getValue();
9308         if(String(v) !== String(this.startValue)){
9309             this.fireEvent('change', this, v, this.startValue);
9310         }
9311         
9312     },
9313     
9314     /**
9315      * Resets the current field value to the originally loaded value and clears any validation messages
9316      */
9317     reset : function(){
9318         this.setValue(this.originalValue);
9319         this.validate();
9320     },
9321      /**
9322      * Returns the name of the field
9323      * @return {Mixed} name The name field
9324      */
9325     getName: function(){
9326         return this.name;
9327     },
9328      /**
9329      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9330      * @return {Mixed} value The field value
9331      */
9332     getValue : function(){
9333         
9334         var v = this.inputEl().getValue();
9335         
9336         return v;
9337     },
9338     /**
9339      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9340      * @return {Mixed} value The field value
9341      */
9342     getRawValue : function(){
9343         var v = this.inputEl().getValue();
9344         
9345         return v;
9346     },
9347     
9348     /**
9349      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9350      * @param {Mixed} value The value to set
9351      */
9352     setRawValue : function(v){
9353         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9354     },
9355     
9356     selectText : function(start, end){
9357         var v = this.getRawValue();
9358         if(v.length > 0){
9359             start = start === undefined ? 0 : start;
9360             end = end === undefined ? v.length : end;
9361             var d = this.inputEl().dom;
9362             if(d.setSelectionRange){
9363                 d.setSelectionRange(start, end);
9364             }else if(d.createTextRange){
9365                 var range = d.createTextRange();
9366                 range.moveStart("character", start);
9367                 range.moveEnd("character", v.length-end);
9368                 range.select();
9369             }
9370         }
9371     },
9372     
9373     /**
9374      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9375      * @param {Mixed} value The value to set
9376      */
9377     setValue : function(v){
9378         this.value = v;
9379         if(this.rendered){
9380             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9381             this.validate();
9382         }
9383     },
9384     
9385     /*
9386     processValue : function(value){
9387         if(this.stripCharsRe){
9388             var newValue = value.replace(this.stripCharsRe, '');
9389             if(newValue !== value){
9390                 this.setRawValue(newValue);
9391                 return newValue;
9392             }
9393         }
9394         return value;
9395     },
9396   */
9397     preFocus : function(){
9398         
9399         if(this.selectOnFocus){
9400             this.inputEl().dom.select();
9401         }
9402     },
9403     filterKeys : function(e){
9404         var k = e.getKey();
9405         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9406             return;
9407         }
9408         var c = e.getCharCode(), cc = String.fromCharCode(c);
9409         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9410             return;
9411         }
9412         if(!this.maskRe.test(cc)){
9413             e.stopEvent();
9414         }
9415     },
9416      /**
9417      * Clear any invalid styles/messages for this field
9418      */
9419     clearInvalid : function(){
9420         
9421         if(!this.el || this.preventMark){ // not rendered
9422             return;
9423         }
9424         
9425      
9426         this.el.removeClass(this.invalidClass);
9427         
9428         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9429             
9430             var feedback = this.el.select('.form-control-feedback', true).first();
9431             
9432             if(feedback){
9433                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9434             }
9435             
9436         }
9437         
9438         if(this.indicator){
9439             this.indicator.removeClass('visible');
9440             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9441         }
9442         
9443         this.fireEvent('valid', this);
9444     },
9445     
9446      /**
9447      * Mark this field as valid
9448      */
9449     markValid : function()
9450     {
9451         if(!this.el  || this.preventMark){ // not rendered...
9452             return;
9453         }
9454         
9455         this.el.removeClass([this.invalidClass, this.validClass]);
9456         
9457         var feedback = this.el.select('.form-control-feedback', true).first();
9458             
9459         if(feedback){
9460             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9461         }
9462         
9463         if(this.indicator){
9464             this.indicator.removeClass('visible');
9465             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9466         }
9467         
9468         if(this.disabled){
9469             return;
9470         }
9471         
9472         if(this.allowBlank && !this.getRawValue().length){
9473             return;
9474         }
9475         
9476         this.el.addClass(this.validClass);
9477         
9478         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9479             
9480             var feedback = this.el.select('.form-control-feedback', true).first();
9481             
9482             if(feedback){
9483                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9484                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9485             }
9486             
9487         }
9488         
9489         this.fireEvent('valid', this);
9490     },
9491     
9492      /**
9493      * Mark this field as invalid
9494      * @param {String} msg The validation message
9495      */
9496     markInvalid : function(msg)
9497     {
9498         if(!this.el  || this.preventMark){ // not rendered
9499             return;
9500         }
9501         
9502         this.el.removeClass([this.invalidClass, this.validClass]);
9503         
9504         var feedback = this.el.select('.form-control-feedback', true).first();
9505             
9506         if(feedback){
9507             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9508         }
9509
9510         if(this.disabled){
9511             return;
9512         }
9513         
9514         if(this.allowBlank && !this.getRawValue().length){
9515             return;
9516         }
9517         
9518         if(this.indicator){
9519             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9520             this.indicator.addClass('visible');
9521         }
9522         
9523         this.el.addClass(this.invalidClass);
9524         
9525         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9526             
9527             var feedback = this.el.select('.form-control-feedback', true).first();
9528             
9529             if(feedback){
9530                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9531                 
9532                 if(this.getValue().length || this.forceFeedback){
9533                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9534                 }
9535                 
9536             }
9537             
9538         }
9539         
9540         this.fireEvent('invalid', this, msg);
9541     },
9542     // private
9543     SafariOnKeyDown : function(event)
9544     {
9545         // this is a workaround for a password hang bug on chrome/ webkit.
9546         if (this.inputEl().dom.type != 'password') {
9547             return;
9548         }
9549         
9550         var isSelectAll = false;
9551         
9552         if(this.inputEl().dom.selectionEnd > 0){
9553             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9554         }
9555         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9556             event.preventDefault();
9557             this.setValue('');
9558             return;
9559         }
9560         
9561         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9562             
9563             event.preventDefault();
9564             // this is very hacky as keydown always get's upper case.
9565             //
9566             var cc = String.fromCharCode(event.getCharCode());
9567             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9568             
9569         }
9570     },
9571     adjustWidth : function(tag, w){
9572         tag = tag.toLowerCase();
9573         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9574             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9575                 if(tag == 'input'){
9576                     return w + 2;
9577                 }
9578                 if(tag == 'textarea'){
9579                     return w-2;
9580                 }
9581             }else if(Roo.isOpera){
9582                 if(tag == 'input'){
9583                     return w + 2;
9584                 }
9585                 if(tag == 'textarea'){
9586                     return w-2;
9587                 }
9588             }
9589         }
9590         return w;
9591     },
9592     
9593     setFieldLabel : function(v)
9594     {
9595         if(!this.rendered){
9596             return;
9597         }
9598         
9599         if(this.indicator){
9600             var ar = this.el.select('label > span',true);
9601             
9602             if (ar.elements.length) {
9603                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9604                 this.fieldLabel = v;
9605                 return;
9606             }
9607             
9608             var br = this.el.select('label',true);
9609             
9610             if(br.elements.length) {
9611                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9612                 this.fieldLabel = v;
9613                 return;
9614             }
9615             
9616             Roo.log('Cannot Found any of label > span || label in input');
9617             return;
9618         }
9619         
9620         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9621         this.fieldLabel = v;
9622         
9623         
9624     }
9625 });
9626
9627  
9628 /*
9629  * - LGPL
9630  *
9631  * Input
9632  * 
9633  */
9634
9635 /**
9636  * @class Roo.bootstrap.TextArea
9637  * @extends Roo.bootstrap.Input
9638  * Bootstrap TextArea class
9639  * @cfg {Number} cols Specifies the visible width of a text area
9640  * @cfg {Number} rows Specifies the visible number of lines in a text area
9641  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9642  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9643  * @cfg {string} html text
9644  * 
9645  * @constructor
9646  * Create a new TextArea
9647  * @param {Object} config The config object
9648  */
9649
9650 Roo.bootstrap.TextArea = function(config){
9651     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9652    
9653 };
9654
9655 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9656      
9657     cols : false,
9658     rows : 5,
9659     readOnly : false,
9660     warp : 'soft',
9661     resize : false,
9662     value: false,
9663     html: false,
9664     
9665     getAutoCreate : function(){
9666         
9667         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9668         
9669         var id = Roo.id();
9670         
9671         var cfg = {};
9672         
9673         if(this.inputType != 'hidden'){
9674             cfg.cls = 'form-group' //input-group
9675         }
9676         
9677         var input =  {
9678             tag: 'textarea',
9679             id : id,
9680             warp : this.warp,
9681             rows : this.rows,
9682             value : this.value || '',
9683             html: this.html || '',
9684             cls : 'form-control',
9685             placeholder : this.placeholder || '' 
9686             
9687         };
9688         
9689         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9690             input.maxLength = this.maxLength;
9691         }
9692         
9693         if(this.resize){
9694             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9695         }
9696         
9697         if(this.cols){
9698             input.cols = this.cols;
9699         }
9700         
9701         if (this.readOnly) {
9702             input.readonly = true;
9703         }
9704         
9705         if (this.name) {
9706             input.name = this.name;
9707         }
9708         
9709         if (this.size) {
9710             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9711         }
9712         
9713         var settings=this;
9714         ['xs','sm','md','lg'].map(function(size){
9715             if (settings[size]) {
9716                 cfg.cls += ' col-' + size + '-' + settings[size];
9717             }
9718         });
9719         
9720         var inputblock = input;
9721         
9722         if(this.hasFeedback && !this.allowBlank){
9723             
9724             var feedback = {
9725                 tag: 'span',
9726                 cls: 'glyphicon form-control-feedback'
9727             };
9728
9729             inputblock = {
9730                 cls : 'has-feedback',
9731                 cn :  [
9732                     input,
9733                     feedback
9734                 ] 
9735             };  
9736         }
9737         
9738         
9739         if (this.before || this.after) {
9740             
9741             inputblock = {
9742                 cls : 'input-group',
9743                 cn :  [] 
9744             };
9745             if (this.before) {
9746                 inputblock.cn.push({
9747                     tag :'span',
9748                     cls : 'input-group-addon',
9749                     html : this.before
9750                 });
9751             }
9752             
9753             inputblock.cn.push(input);
9754             
9755             if(this.hasFeedback && !this.allowBlank){
9756                 inputblock.cls += ' has-feedback';
9757                 inputblock.cn.push(feedback);
9758             }
9759             
9760             if (this.after) {
9761                 inputblock.cn.push({
9762                     tag :'span',
9763                     cls : 'input-group-addon',
9764                     html : this.after
9765                 });
9766             }
9767             
9768         }
9769         
9770         if (align ==='left' && this.fieldLabel.length) {
9771             cfg.cn = [
9772                 {
9773                     tag: 'label',
9774                     'for' :  id,
9775                     cls : 'control-label',
9776                     html : this.fieldLabel
9777                 },
9778                 {
9779                     cls : "",
9780                     cn: [
9781                         inputblock
9782                     ]
9783                 }
9784
9785             ];
9786             
9787             if(this.labelWidth > 12){
9788                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9789             }
9790
9791             if(this.labelWidth < 13 && this.labelmd == 0){
9792                 this.labelmd = this.labelWidth;
9793             }
9794
9795             if(this.labellg > 0){
9796                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9797                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9798             }
9799
9800             if(this.labelmd > 0){
9801                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9802                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9803             }
9804
9805             if(this.labelsm > 0){
9806                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9807                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9808             }
9809
9810             if(this.labelxs > 0){
9811                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9812                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9813             }
9814             
9815         } else if ( this.fieldLabel.length) {
9816             cfg.cn = [
9817
9818                {
9819                    tag: 'label',
9820                    //cls : 'input-group-addon',
9821                    html : this.fieldLabel
9822
9823                },
9824
9825                inputblock
9826
9827            ];
9828
9829         } else {
9830
9831             cfg.cn = [
9832
9833                 inputblock
9834
9835             ];
9836                 
9837         }
9838         
9839         if (this.disabled) {
9840             input.disabled=true;
9841         }
9842         
9843         return cfg;
9844         
9845     },
9846     /**
9847      * return the real textarea element.
9848      */
9849     inputEl: function ()
9850     {
9851         return this.el.select('textarea.form-control',true).first();
9852     },
9853     
9854     /**
9855      * Clear any invalid styles/messages for this field
9856      */
9857     clearInvalid : function()
9858     {
9859         
9860         if(!this.el || this.preventMark){ // not rendered
9861             return;
9862         }
9863         
9864         var label = this.el.select('label', true).first();
9865         var icon = this.el.select('i.fa-star', true).first();
9866         
9867         if(label && icon){
9868             icon.remove();
9869         }
9870         
9871         this.el.removeClass(this.invalidClass);
9872         
9873         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9874             
9875             var feedback = this.el.select('.form-control-feedback', true).first();
9876             
9877             if(feedback){
9878                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9879             }
9880             
9881         }
9882         
9883         this.fireEvent('valid', this);
9884     },
9885     
9886      /**
9887      * Mark this field as valid
9888      */
9889     markValid : function()
9890     {
9891         if(!this.el  || this.preventMark){ // not rendered
9892             return;
9893         }
9894         
9895         this.el.removeClass([this.invalidClass, this.validClass]);
9896         
9897         var feedback = this.el.select('.form-control-feedback', true).first();
9898             
9899         if(feedback){
9900             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9901         }
9902
9903         if(this.disabled || this.allowBlank){
9904             return;
9905         }
9906         
9907         var label = this.el.select('label', true).first();
9908         var icon = this.el.select('i.fa-star', true).first();
9909         
9910         if(label && icon){
9911             icon.remove();
9912         }
9913         
9914         this.el.addClass(this.validClass);
9915         
9916         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9917             
9918             var feedback = this.el.select('.form-control-feedback', true).first();
9919             
9920             if(feedback){
9921                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9922                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9923             }
9924             
9925         }
9926         
9927         this.fireEvent('valid', this);
9928     },
9929     
9930      /**
9931      * Mark this field as invalid
9932      * @param {String} msg The validation message
9933      */
9934     markInvalid : function(msg)
9935     {
9936         if(!this.el  || this.preventMark){ // not rendered
9937             return;
9938         }
9939         
9940         this.el.removeClass([this.invalidClass, this.validClass]);
9941         
9942         var feedback = this.el.select('.form-control-feedback', true).first();
9943             
9944         if(feedback){
9945             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9946         }
9947
9948         if(this.disabled || this.allowBlank){
9949             return;
9950         }
9951         
9952         var label = this.el.select('label', true).first();
9953         var icon = this.el.select('i.fa-star', true).first();
9954         
9955         if(!this.getValue().length && label && !icon){
9956             this.el.createChild({
9957                 tag : 'i',
9958                 cls : 'text-danger fa fa-lg fa-star',
9959                 tooltip : 'This field is required',
9960                 style : 'margin-right:5px;'
9961             }, label, true);
9962         }
9963
9964         this.el.addClass(this.invalidClass);
9965         
9966         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9967             
9968             var feedback = this.el.select('.form-control-feedback', true).first();
9969             
9970             if(feedback){
9971                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9972                 
9973                 if(this.getValue().length || this.forceFeedback){
9974                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9975                 }
9976                 
9977             }
9978             
9979         }
9980         
9981         this.fireEvent('invalid', this, msg);
9982     }
9983 });
9984
9985  
9986 /*
9987  * - LGPL
9988  *
9989  * trigger field - base class for combo..
9990  * 
9991  */
9992  
9993 /**
9994  * @class Roo.bootstrap.TriggerField
9995  * @extends Roo.bootstrap.Input
9996  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9997  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9998  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9999  * for which you can provide a custom implementation.  For example:
10000  * <pre><code>
10001 var trigger = new Roo.bootstrap.TriggerField();
10002 trigger.onTriggerClick = myTriggerFn;
10003 trigger.applyTo('my-field');
10004 </code></pre>
10005  *
10006  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10007  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10008  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10009  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10010  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10011
10012  * @constructor
10013  * Create a new TriggerField.
10014  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10015  * to the base TextField)
10016  */
10017 Roo.bootstrap.TriggerField = function(config){
10018     this.mimicing = false;
10019     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10020 };
10021
10022 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10023     /**
10024      * @cfg {String} triggerClass A CSS class to apply to the trigger
10025      */
10026      /**
10027      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10028      */
10029     hideTrigger:false,
10030
10031     /**
10032      * @cfg {Boolean} removable (true|false) special filter default false
10033      */
10034     removable : false,
10035     
10036     /** @cfg {Boolean} grow @hide */
10037     /** @cfg {Number} growMin @hide */
10038     /** @cfg {Number} growMax @hide */
10039
10040     /**
10041      * @hide 
10042      * @method
10043      */
10044     autoSize: Roo.emptyFn,
10045     // private
10046     monitorTab : true,
10047     // private
10048     deferHeight : true,
10049
10050     
10051     actionMode : 'wrap',
10052     
10053     caret : false,
10054     
10055     
10056     getAutoCreate : function(){
10057        
10058         var align = this.labelAlign || this.parentLabelAlign();
10059         
10060         var id = Roo.id();
10061         
10062         var cfg = {
10063             cls: 'form-group' //input-group
10064         };
10065         
10066         
10067         var input =  {
10068             tag: 'input',
10069             id : id,
10070             type : this.inputType,
10071             cls : 'form-control',
10072             autocomplete: 'new-password',
10073             placeholder : this.placeholder || '' 
10074             
10075         };
10076         if (this.name) {
10077             input.name = this.name;
10078         }
10079         if (this.size) {
10080             input.cls += ' input-' + this.size;
10081         }
10082         
10083         if (this.disabled) {
10084             input.disabled=true;
10085         }
10086         
10087         var inputblock = input;
10088         
10089         if(this.hasFeedback && !this.allowBlank){
10090             
10091             var feedback = {
10092                 tag: 'span',
10093                 cls: 'glyphicon form-control-feedback'
10094             };
10095             
10096             if(this.removable && !this.editable && !this.tickable){
10097                 inputblock = {
10098                     cls : 'has-feedback',
10099                     cn :  [
10100                         inputblock,
10101                         {
10102                             tag: 'button',
10103                             html : 'x',
10104                             cls : 'roo-combo-removable-btn close'
10105                         },
10106                         feedback
10107                     ] 
10108                 };
10109             } else {
10110                 inputblock = {
10111                     cls : 'has-feedback',
10112                     cn :  [
10113                         inputblock,
10114                         feedback
10115                     ] 
10116                 };
10117             }
10118
10119         } else {
10120             if(this.removable && !this.editable && !this.tickable){
10121                 inputblock = {
10122                     cls : 'roo-removable',
10123                     cn :  [
10124                         inputblock,
10125                         {
10126                             tag: 'button',
10127                             html : 'x',
10128                             cls : 'roo-combo-removable-btn close'
10129                         }
10130                     ] 
10131                 };
10132             }
10133         }
10134         
10135         if (this.before || this.after) {
10136             
10137             inputblock = {
10138                 cls : 'input-group',
10139                 cn :  [] 
10140             };
10141             if (this.before) {
10142                 inputblock.cn.push({
10143                     tag :'span',
10144                     cls : 'input-group-addon',
10145                     html : this.before
10146                 });
10147             }
10148             
10149             inputblock.cn.push(input);
10150             
10151             if(this.hasFeedback && !this.allowBlank){
10152                 inputblock.cls += ' has-feedback';
10153                 inputblock.cn.push(feedback);
10154             }
10155             
10156             if (this.after) {
10157                 inputblock.cn.push({
10158                     tag :'span',
10159                     cls : 'input-group-addon',
10160                     html : this.after
10161                 });
10162             }
10163             
10164         };
10165         
10166         var box = {
10167             tag: 'div',
10168             cn: [
10169                 {
10170                     tag: 'input',
10171                     type : 'hidden',
10172                     cls: 'form-hidden-field'
10173                 },
10174                 inputblock
10175             ]
10176             
10177         };
10178         
10179         if(this.multiple){
10180             box = {
10181                 tag: 'div',
10182                 cn: [
10183                     {
10184                         tag: 'input',
10185                         type : 'hidden',
10186                         cls: 'form-hidden-field'
10187                     },
10188                     {
10189                         tag: 'ul',
10190                         cls: 'roo-select2-choices',
10191                         cn:[
10192                             {
10193                                 tag: 'li',
10194                                 cls: 'roo-select2-search-field',
10195                                 cn: [
10196
10197                                     inputblock
10198                                 ]
10199                             }
10200                         ]
10201                     }
10202                 ]
10203             }
10204         };
10205         
10206         var combobox = {
10207             cls: 'roo-select2-container input-group',
10208             cn: [
10209                 box
10210 //                {
10211 //                    tag: 'ul',
10212 //                    cls: 'typeahead typeahead-long dropdown-menu',
10213 //                    style: 'display:none'
10214 //                }
10215             ]
10216         };
10217         
10218         if(!this.multiple && this.showToggleBtn){
10219             
10220             var caret = {
10221                         tag: 'span',
10222                         cls: 'caret'
10223              };
10224             if (this.caret != false) {
10225                 caret = {
10226                      tag: 'i',
10227                      cls: 'fa fa-' + this.caret
10228                 };
10229                 
10230             }
10231             
10232             combobox.cn.push({
10233                 tag :'span',
10234                 cls : 'input-group-addon btn dropdown-toggle',
10235                 cn : [
10236                     caret,
10237                     {
10238                         tag: 'span',
10239                         cls: 'combobox-clear',
10240                         cn  : [
10241                             {
10242                                 tag : 'i',
10243                                 cls: 'icon-remove'
10244                             }
10245                         ]
10246                     }
10247                 ]
10248
10249             })
10250         }
10251         
10252         if(this.multiple){
10253             combobox.cls += ' roo-select2-container-multi';
10254         }
10255         
10256         if (align ==='left' && this.fieldLabel.length) {
10257             
10258             cfg.cls += ' roo-form-group-label-left';
10259
10260             cfg.cn = [
10261                 {
10262                     tag : 'i',
10263                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10264                     tooltip : 'This field is required'
10265                 },
10266                 {
10267                     tag: 'label',
10268                     'for' :  id,
10269                     cls : 'control-label',
10270                     html : this.fieldLabel
10271
10272                 },
10273                 {
10274                     cls : "", 
10275                     cn: [
10276                         combobox
10277                     ]
10278                 }
10279
10280             ];
10281             
10282             var labelCfg = cfg.cn[1];
10283             var contentCfg = cfg.cn[2];
10284             
10285             if(this.indicatorpos == 'right'){
10286                 cfg.cn = [
10287                     {
10288                         tag: 'label',
10289                         'for' :  id,
10290                         cls : 'control-label',
10291                         cn : [
10292                             {
10293                                 tag : 'span',
10294                                 html : this.fieldLabel
10295                             },
10296                             {
10297                                 tag : 'i',
10298                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10299                                 tooltip : 'This field is required'
10300                             }
10301                         ]
10302                     },
10303                     {
10304                         cls : "", 
10305                         cn: [
10306                             combobox
10307                         ]
10308                     }
10309
10310                 ];
10311                 
10312                 labelCfg = cfg.cn[0];
10313                 contentCfg = cfg.cn[1];
10314             }
10315             
10316             if(this.labelWidth > 12){
10317                 labelCfg.style = "width: " + this.labelWidth + 'px';
10318             }
10319             
10320             if(this.labelWidth < 13 && this.labelmd == 0){
10321                 this.labelmd = this.labelWidth;
10322             }
10323             
10324             if(this.labellg > 0){
10325                 labelCfg.cls += ' col-lg-' + this.labellg;
10326                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10327             }
10328             
10329             if(this.labelmd > 0){
10330                 labelCfg.cls += ' col-md-' + this.labelmd;
10331                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10332             }
10333             
10334             if(this.labelsm > 0){
10335                 labelCfg.cls += ' col-sm-' + this.labelsm;
10336                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10337             }
10338             
10339             if(this.labelxs > 0){
10340                 labelCfg.cls += ' col-xs-' + this.labelxs;
10341                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10342             }
10343             
10344         } else if ( this.fieldLabel.length) {
10345 //                Roo.log(" label");
10346             cfg.cn = [
10347                 {
10348                    tag : 'i',
10349                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10350                    tooltip : 'This field is required'
10351                },
10352                {
10353                    tag: 'label',
10354                    //cls : 'input-group-addon',
10355                    html : this.fieldLabel
10356
10357                },
10358
10359                combobox
10360
10361             ];
10362             
10363             if(this.indicatorpos == 'right'){
10364                 
10365                 cfg.cn = [
10366                     {
10367                        tag: 'label',
10368                        cn : [
10369                            {
10370                                tag : 'span',
10371                                html : this.fieldLabel
10372                            },
10373                            {
10374                               tag : 'i',
10375                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10376                               tooltip : 'This field is required'
10377                            }
10378                        ]
10379
10380                     },
10381                     combobox
10382
10383                 ];
10384
10385             }
10386
10387         } else {
10388             
10389 //                Roo.log(" no label && no align");
10390                 cfg = combobox
10391                      
10392                 
10393         }
10394         
10395         var settings=this;
10396         ['xs','sm','md','lg'].map(function(size){
10397             if (settings[size]) {
10398                 cfg.cls += ' col-' + size + '-' + settings[size];
10399             }
10400         });
10401         
10402         return cfg;
10403         
10404     },
10405     
10406     
10407     
10408     // private
10409     onResize : function(w, h){
10410 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10411 //        if(typeof w == 'number'){
10412 //            var x = w - this.trigger.getWidth();
10413 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10414 //            this.trigger.setStyle('left', x+'px');
10415 //        }
10416     },
10417
10418     // private
10419     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10420
10421     // private
10422     getResizeEl : function(){
10423         return this.inputEl();
10424     },
10425
10426     // private
10427     getPositionEl : function(){
10428         return this.inputEl();
10429     },
10430
10431     // private
10432     alignErrorIcon : function(){
10433         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10434     },
10435
10436     // private
10437     initEvents : function(){
10438         
10439         this.createList();
10440         
10441         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10442         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10443         if(!this.multiple && this.showToggleBtn){
10444             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10445             if(this.hideTrigger){
10446                 this.trigger.setDisplayed(false);
10447             }
10448             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10449         }
10450         
10451         if(this.multiple){
10452             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10453         }
10454         
10455         if(this.removable && !this.editable && !this.tickable){
10456             var close = this.closeTriggerEl();
10457             
10458             if(close){
10459                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10460                 close.on('click', this.removeBtnClick, this, close);
10461             }
10462         }
10463         
10464         //this.trigger.addClassOnOver('x-form-trigger-over');
10465         //this.trigger.addClassOnClick('x-form-trigger-click');
10466         
10467         //if(!this.width){
10468         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10469         //}
10470     },
10471     
10472     closeTriggerEl : function()
10473     {
10474         var close = this.el.select('.roo-combo-removable-btn', true).first();
10475         return close ? close : false;
10476     },
10477     
10478     removeBtnClick : function(e, h, el)
10479     {
10480         e.preventDefault();
10481         
10482         if(this.fireEvent("remove", this) !== false){
10483             this.reset();
10484             this.fireEvent("afterremove", this)
10485         }
10486     },
10487     
10488     createList : function()
10489     {
10490         this.list = Roo.get(document.body).createChild({
10491             tag: 'ul',
10492             cls: 'typeahead typeahead-long dropdown-menu',
10493             style: 'display:none'
10494         });
10495         
10496         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10497         
10498     },
10499
10500     // private
10501     initTrigger : function(){
10502        
10503     },
10504
10505     // private
10506     onDestroy : function(){
10507         if(this.trigger){
10508             this.trigger.removeAllListeners();
10509           //  this.trigger.remove();
10510         }
10511         //if(this.wrap){
10512         //    this.wrap.remove();
10513         //}
10514         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10515     },
10516
10517     // private
10518     onFocus : function(){
10519         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10520         /*
10521         if(!this.mimicing){
10522             this.wrap.addClass('x-trigger-wrap-focus');
10523             this.mimicing = true;
10524             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10525             if(this.monitorTab){
10526                 this.el.on("keydown", this.checkTab, this);
10527             }
10528         }
10529         */
10530     },
10531
10532     // private
10533     checkTab : function(e){
10534         if(e.getKey() == e.TAB){
10535             this.triggerBlur();
10536         }
10537     },
10538
10539     // private
10540     onBlur : function(){
10541         // do nothing
10542     },
10543
10544     // private
10545     mimicBlur : function(e, t){
10546         /*
10547         if(!this.wrap.contains(t) && this.validateBlur()){
10548             this.triggerBlur();
10549         }
10550         */
10551     },
10552
10553     // private
10554     triggerBlur : function(){
10555         this.mimicing = false;
10556         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10557         if(this.monitorTab){
10558             this.el.un("keydown", this.checkTab, this);
10559         }
10560         //this.wrap.removeClass('x-trigger-wrap-focus');
10561         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10562     },
10563
10564     // private
10565     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10566     validateBlur : function(e, t){
10567         return true;
10568     },
10569
10570     // private
10571     onDisable : function(){
10572         this.inputEl().dom.disabled = true;
10573         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10574         //if(this.wrap){
10575         //    this.wrap.addClass('x-item-disabled');
10576         //}
10577     },
10578
10579     // private
10580     onEnable : function(){
10581         this.inputEl().dom.disabled = false;
10582         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10583         //if(this.wrap){
10584         //    this.el.removeClass('x-item-disabled');
10585         //}
10586     },
10587
10588     // private
10589     onShow : function(){
10590         var ae = this.getActionEl();
10591         
10592         if(ae){
10593             ae.dom.style.display = '';
10594             ae.dom.style.visibility = 'visible';
10595         }
10596     },
10597
10598     // private
10599     
10600     onHide : function(){
10601         var ae = this.getActionEl();
10602         ae.dom.style.display = 'none';
10603     },
10604
10605     /**
10606      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10607      * by an implementing function.
10608      * @method
10609      * @param {EventObject} e
10610      */
10611     onTriggerClick : Roo.emptyFn
10612 });
10613  /*
10614  * Based on:
10615  * Ext JS Library 1.1.1
10616  * Copyright(c) 2006-2007, Ext JS, LLC.
10617  *
10618  * Originally Released Under LGPL - original licence link has changed is not relivant.
10619  *
10620  * Fork - LGPL
10621  * <script type="text/javascript">
10622  */
10623
10624
10625 /**
10626  * @class Roo.data.SortTypes
10627  * @singleton
10628  * Defines the default sorting (casting?) comparison functions used when sorting data.
10629  */
10630 Roo.data.SortTypes = {
10631     /**
10632      * Default sort that does nothing
10633      * @param {Mixed} s The value being converted
10634      * @return {Mixed} The comparison value
10635      */
10636     none : function(s){
10637         return s;
10638     },
10639     
10640     /**
10641      * The regular expression used to strip tags
10642      * @type {RegExp}
10643      * @property
10644      */
10645     stripTagsRE : /<\/?[^>]+>/gi,
10646     
10647     /**
10648      * Strips all HTML tags to sort on text only
10649      * @param {Mixed} s The value being converted
10650      * @return {String} The comparison value
10651      */
10652     asText : function(s){
10653         return String(s).replace(this.stripTagsRE, "");
10654     },
10655     
10656     /**
10657      * Strips all HTML tags to sort on text only - Case insensitive
10658      * @param {Mixed} s The value being converted
10659      * @return {String} The comparison value
10660      */
10661     asUCText : function(s){
10662         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10663     },
10664     
10665     /**
10666      * Case insensitive string
10667      * @param {Mixed} s The value being converted
10668      * @return {String} The comparison value
10669      */
10670     asUCString : function(s) {
10671         return String(s).toUpperCase();
10672     },
10673     
10674     /**
10675      * Date sorting
10676      * @param {Mixed} s The value being converted
10677      * @return {Number} The comparison value
10678      */
10679     asDate : function(s) {
10680         if(!s){
10681             return 0;
10682         }
10683         if(s instanceof Date){
10684             return s.getTime();
10685         }
10686         return Date.parse(String(s));
10687     },
10688     
10689     /**
10690      * Float sorting
10691      * @param {Mixed} s The value being converted
10692      * @return {Float} The comparison value
10693      */
10694     asFloat : function(s) {
10695         var val = parseFloat(String(s).replace(/,/g, ""));
10696         if(isNaN(val)) {
10697             val = 0;
10698         }
10699         return val;
10700     },
10701     
10702     /**
10703      * Integer sorting
10704      * @param {Mixed} s The value being converted
10705      * @return {Number} The comparison value
10706      */
10707     asInt : function(s) {
10708         var val = parseInt(String(s).replace(/,/g, ""));
10709         if(isNaN(val)) {
10710             val = 0;
10711         }
10712         return val;
10713     }
10714 };/*
10715  * Based on:
10716  * Ext JS Library 1.1.1
10717  * Copyright(c) 2006-2007, Ext JS, LLC.
10718  *
10719  * Originally Released Under LGPL - original licence link has changed is not relivant.
10720  *
10721  * Fork - LGPL
10722  * <script type="text/javascript">
10723  */
10724
10725 /**
10726 * @class Roo.data.Record
10727  * Instances of this class encapsulate both record <em>definition</em> information, and record
10728  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10729  * to access Records cached in an {@link Roo.data.Store} object.<br>
10730  * <p>
10731  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10732  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10733  * objects.<br>
10734  * <p>
10735  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10736  * @constructor
10737  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10738  * {@link #create}. The parameters are the same.
10739  * @param {Array} data An associative Array of data values keyed by the field name.
10740  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10741  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10742  * not specified an integer id is generated.
10743  */
10744 Roo.data.Record = function(data, id){
10745     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10746     this.data = data;
10747 };
10748
10749 /**
10750  * Generate a constructor for a specific record layout.
10751  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10752  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10753  * Each field definition object may contain the following properties: <ul>
10754  * <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,
10755  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10756  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10757  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10758  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10759  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10760  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10761  * this may be omitted.</p></li>
10762  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10763  * <ul><li>auto (Default, implies no conversion)</li>
10764  * <li>string</li>
10765  * <li>int</li>
10766  * <li>float</li>
10767  * <li>boolean</li>
10768  * <li>date</li></ul></p></li>
10769  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10770  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10771  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10772  * by the Reader into an object that will be stored in the Record. It is passed the
10773  * following parameters:<ul>
10774  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10775  * </ul></p></li>
10776  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10777  * </ul>
10778  * <br>usage:<br><pre><code>
10779 var TopicRecord = Roo.data.Record.create(
10780     {name: 'title', mapping: 'topic_title'},
10781     {name: 'author', mapping: 'username'},
10782     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10783     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10784     {name: 'lastPoster', mapping: 'user2'},
10785     {name: 'excerpt', mapping: 'post_text'}
10786 );
10787
10788 var myNewRecord = new TopicRecord({
10789     title: 'Do my job please',
10790     author: 'noobie',
10791     totalPosts: 1,
10792     lastPost: new Date(),
10793     lastPoster: 'Animal',
10794     excerpt: 'No way dude!'
10795 });
10796 myStore.add(myNewRecord);
10797 </code></pre>
10798  * @method create
10799  * @static
10800  */
10801 Roo.data.Record.create = function(o){
10802     var f = function(){
10803         f.superclass.constructor.apply(this, arguments);
10804     };
10805     Roo.extend(f, Roo.data.Record);
10806     var p = f.prototype;
10807     p.fields = new Roo.util.MixedCollection(false, function(field){
10808         return field.name;
10809     });
10810     for(var i = 0, len = o.length; i < len; i++){
10811         p.fields.add(new Roo.data.Field(o[i]));
10812     }
10813     f.getField = function(name){
10814         return p.fields.get(name);  
10815     };
10816     return f;
10817 };
10818
10819 Roo.data.Record.AUTO_ID = 1000;
10820 Roo.data.Record.EDIT = 'edit';
10821 Roo.data.Record.REJECT = 'reject';
10822 Roo.data.Record.COMMIT = 'commit';
10823
10824 Roo.data.Record.prototype = {
10825     /**
10826      * Readonly flag - true if this record has been modified.
10827      * @type Boolean
10828      */
10829     dirty : false,
10830     editing : false,
10831     error: null,
10832     modified: null,
10833
10834     // private
10835     join : function(store){
10836         this.store = store;
10837     },
10838
10839     /**
10840      * Set the named field to the specified value.
10841      * @param {String} name The name of the field to set.
10842      * @param {Object} value The value to set the field to.
10843      */
10844     set : function(name, value){
10845         if(this.data[name] == value){
10846             return;
10847         }
10848         this.dirty = true;
10849         if(!this.modified){
10850             this.modified = {};
10851         }
10852         if(typeof this.modified[name] == 'undefined'){
10853             this.modified[name] = this.data[name];
10854         }
10855         this.data[name] = value;
10856         if(!this.editing && this.store){
10857             this.store.afterEdit(this);
10858         }       
10859     },
10860
10861     /**
10862      * Get the value of the named field.
10863      * @param {String} name The name of the field to get the value of.
10864      * @return {Object} The value of the field.
10865      */
10866     get : function(name){
10867         return this.data[name]; 
10868     },
10869
10870     // private
10871     beginEdit : function(){
10872         this.editing = true;
10873         this.modified = {}; 
10874     },
10875
10876     // private
10877     cancelEdit : function(){
10878         this.editing = false;
10879         delete this.modified;
10880     },
10881
10882     // private
10883     endEdit : function(){
10884         this.editing = false;
10885         if(this.dirty && this.store){
10886             this.store.afterEdit(this);
10887         }
10888     },
10889
10890     /**
10891      * Usually called by the {@link Roo.data.Store} which owns the Record.
10892      * Rejects all changes made to the Record since either creation, or the last commit operation.
10893      * Modified fields are reverted to their original values.
10894      * <p>
10895      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10896      * of reject operations.
10897      */
10898     reject : function(){
10899         var m = this.modified;
10900         for(var n in m){
10901             if(typeof m[n] != "function"){
10902                 this.data[n] = m[n];
10903             }
10904         }
10905         this.dirty = false;
10906         delete this.modified;
10907         this.editing = false;
10908         if(this.store){
10909             this.store.afterReject(this);
10910         }
10911     },
10912
10913     /**
10914      * Usually called by the {@link Roo.data.Store} which owns the Record.
10915      * Commits all changes made to the Record since either creation, or the last commit operation.
10916      * <p>
10917      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10918      * of commit operations.
10919      */
10920     commit : function(){
10921         this.dirty = false;
10922         delete this.modified;
10923         this.editing = false;
10924         if(this.store){
10925             this.store.afterCommit(this);
10926         }
10927     },
10928
10929     // private
10930     hasError : function(){
10931         return this.error != null;
10932     },
10933
10934     // private
10935     clearError : function(){
10936         this.error = null;
10937     },
10938
10939     /**
10940      * Creates a copy of this record.
10941      * @param {String} id (optional) A new record id if you don't want to use this record's id
10942      * @return {Record}
10943      */
10944     copy : function(newId) {
10945         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10946     }
10947 };/*
10948  * Based on:
10949  * Ext JS Library 1.1.1
10950  * Copyright(c) 2006-2007, Ext JS, LLC.
10951  *
10952  * Originally Released Under LGPL - original licence link has changed is not relivant.
10953  *
10954  * Fork - LGPL
10955  * <script type="text/javascript">
10956  */
10957
10958
10959
10960 /**
10961  * @class Roo.data.Store
10962  * @extends Roo.util.Observable
10963  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10964  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10965  * <p>
10966  * 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
10967  * has no knowledge of the format of the data returned by the Proxy.<br>
10968  * <p>
10969  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10970  * instances from the data object. These records are cached and made available through accessor functions.
10971  * @constructor
10972  * Creates a new Store.
10973  * @param {Object} config A config object containing the objects needed for the Store to access data,
10974  * and read the data into Records.
10975  */
10976 Roo.data.Store = function(config){
10977     this.data = new Roo.util.MixedCollection(false);
10978     this.data.getKey = function(o){
10979         return o.id;
10980     };
10981     this.baseParams = {};
10982     // private
10983     this.paramNames = {
10984         "start" : "start",
10985         "limit" : "limit",
10986         "sort" : "sort",
10987         "dir" : "dir",
10988         "multisort" : "_multisort"
10989     };
10990
10991     if(config && config.data){
10992         this.inlineData = config.data;
10993         delete config.data;
10994     }
10995
10996     Roo.apply(this, config);
10997     
10998     if(this.reader){ // reader passed
10999         this.reader = Roo.factory(this.reader, Roo.data);
11000         this.reader.xmodule = this.xmodule || false;
11001         if(!this.recordType){
11002             this.recordType = this.reader.recordType;
11003         }
11004         if(this.reader.onMetaChange){
11005             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11006         }
11007     }
11008
11009     if(this.recordType){
11010         this.fields = this.recordType.prototype.fields;
11011     }
11012     this.modified = [];
11013
11014     this.addEvents({
11015         /**
11016          * @event datachanged
11017          * Fires when the data cache has changed, and a widget which is using this Store
11018          * as a Record cache should refresh its view.
11019          * @param {Store} this
11020          */
11021         datachanged : true,
11022         /**
11023          * @event metachange
11024          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11025          * @param {Store} this
11026          * @param {Object} meta The JSON metadata
11027          */
11028         metachange : true,
11029         /**
11030          * @event add
11031          * Fires when Records have been added to the Store
11032          * @param {Store} this
11033          * @param {Roo.data.Record[]} records The array of Records added
11034          * @param {Number} index The index at which the record(s) were added
11035          */
11036         add : true,
11037         /**
11038          * @event remove
11039          * Fires when a Record has been removed from the Store
11040          * @param {Store} this
11041          * @param {Roo.data.Record} record The Record that was removed
11042          * @param {Number} index The index at which the record was removed
11043          */
11044         remove : true,
11045         /**
11046          * @event update
11047          * Fires when a Record has been updated
11048          * @param {Store} this
11049          * @param {Roo.data.Record} record The Record that was updated
11050          * @param {String} operation The update operation being performed.  Value may be one of:
11051          * <pre><code>
11052  Roo.data.Record.EDIT
11053  Roo.data.Record.REJECT
11054  Roo.data.Record.COMMIT
11055          * </code></pre>
11056          */
11057         update : true,
11058         /**
11059          * @event clear
11060          * Fires when the data cache has been cleared.
11061          * @param {Store} this
11062          */
11063         clear : true,
11064         /**
11065          * @event beforeload
11066          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11067          * the load action will be canceled.
11068          * @param {Store} this
11069          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11070          */
11071         beforeload : true,
11072         /**
11073          * @event beforeloadadd
11074          * Fires after a new set of Records has been loaded.
11075          * @param {Store} this
11076          * @param {Roo.data.Record[]} records The Records that were loaded
11077          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11078          */
11079         beforeloadadd : true,
11080         /**
11081          * @event load
11082          * Fires after a new set of Records has been loaded, before they are added to the store.
11083          * @param {Store} this
11084          * @param {Roo.data.Record[]} records The Records that were loaded
11085          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11086          * @params {Object} return from reader
11087          */
11088         load : true,
11089         /**
11090          * @event loadexception
11091          * Fires if an exception occurs in the Proxy during loading.
11092          * Called with the signature of the Proxy's "loadexception" event.
11093          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11094          * 
11095          * @param {Proxy} 
11096          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11097          * @param {Object} load options 
11098          * @param {Object} jsonData from your request (normally this contains the Exception)
11099          */
11100         loadexception : true
11101     });
11102     
11103     if(this.proxy){
11104         this.proxy = Roo.factory(this.proxy, Roo.data);
11105         this.proxy.xmodule = this.xmodule || false;
11106         this.relayEvents(this.proxy,  ["loadexception"]);
11107     }
11108     this.sortToggle = {};
11109     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11110
11111     Roo.data.Store.superclass.constructor.call(this);
11112
11113     if(this.inlineData){
11114         this.loadData(this.inlineData);
11115         delete this.inlineData;
11116     }
11117 };
11118
11119 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11120      /**
11121     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11122     * without a remote query - used by combo/forms at present.
11123     */
11124     
11125     /**
11126     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11127     */
11128     /**
11129     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11130     */
11131     /**
11132     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11133     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11134     */
11135     /**
11136     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11137     * on any HTTP request
11138     */
11139     /**
11140     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11141     */
11142     /**
11143     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11144     */
11145     multiSort: false,
11146     /**
11147     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11148     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11149     */
11150     remoteSort : false,
11151
11152     /**
11153     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11154      * loaded or when a record is removed. (defaults to false).
11155     */
11156     pruneModifiedRecords : false,
11157
11158     // private
11159     lastOptions : null,
11160
11161     /**
11162      * Add Records to the Store and fires the add event.
11163      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11164      */
11165     add : function(records){
11166         records = [].concat(records);
11167         for(var i = 0, len = records.length; i < len; i++){
11168             records[i].join(this);
11169         }
11170         var index = this.data.length;
11171         this.data.addAll(records);
11172         this.fireEvent("add", this, records, index);
11173     },
11174
11175     /**
11176      * Remove a Record from the Store and fires the remove event.
11177      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11178      */
11179     remove : function(record){
11180         var index = this.data.indexOf(record);
11181         this.data.removeAt(index);
11182  
11183         if(this.pruneModifiedRecords){
11184             this.modified.remove(record);
11185         }
11186         this.fireEvent("remove", this, record, index);
11187     },
11188
11189     /**
11190      * Remove all Records from the Store and fires the clear event.
11191      */
11192     removeAll : function(){
11193         this.data.clear();
11194         if(this.pruneModifiedRecords){
11195             this.modified = [];
11196         }
11197         this.fireEvent("clear", this);
11198     },
11199
11200     /**
11201      * Inserts Records to the Store at the given index and fires the add event.
11202      * @param {Number} index The start index at which to insert the passed Records.
11203      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11204      */
11205     insert : function(index, records){
11206         records = [].concat(records);
11207         for(var i = 0, len = records.length; i < len; i++){
11208             this.data.insert(index, records[i]);
11209             records[i].join(this);
11210         }
11211         this.fireEvent("add", this, records, index);
11212     },
11213
11214     /**
11215      * Get the index within the cache of the passed Record.
11216      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11217      * @return {Number} The index of the passed Record. Returns -1 if not found.
11218      */
11219     indexOf : function(record){
11220         return this.data.indexOf(record);
11221     },
11222
11223     /**
11224      * Get the index within the cache of the Record with the passed id.
11225      * @param {String} id The id of the Record to find.
11226      * @return {Number} The index of the Record. Returns -1 if not found.
11227      */
11228     indexOfId : function(id){
11229         return this.data.indexOfKey(id);
11230     },
11231
11232     /**
11233      * Get the Record with the specified id.
11234      * @param {String} id The id of the Record to find.
11235      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11236      */
11237     getById : function(id){
11238         return this.data.key(id);
11239     },
11240
11241     /**
11242      * Get the Record at the specified index.
11243      * @param {Number} index The index of the Record to find.
11244      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11245      */
11246     getAt : function(index){
11247         return this.data.itemAt(index);
11248     },
11249
11250     /**
11251      * Returns a range of Records between specified indices.
11252      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11253      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11254      * @return {Roo.data.Record[]} An array of Records
11255      */
11256     getRange : function(start, end){
11257         return this.data.getRange(start, end);
11258     },
11259
11260     // private
11261     storeOptions : function(o){
11262         o = Roo.apply({}, o);
11263         delete o.callback;
11264         delete o.scope;
11265         this.lastOptions = o;
11266     },
11267
11268     /**
11269      * Loads the Record cache from the configured Proxy using the configured Reader.
11270      * <p>
11271      * If using remote paging, then the first load call must specify the <em>start</em>
11272      * and <em>limit</em> properties in the options.params property to establish the initial
11273      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11274      * <p>
11275      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11276      * and this call will return before the new data has been loaded. Perform any post-processing
11277      * in a callback function, or in a "load" event handler.</strong>
11278      * <p>
11279      * @param {Object} options An object containing properties which control loading options:<ul>
11280      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11281      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11282      * passed the following arguments:<ul>
11283      * <li>r : Roo.data.Record[]</li>
11284      * <li>options: Options object from the load call</li>
11285      * <li>success: Boolean success indicator</li></ul></li>
11286      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11287      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11288      * </ul>
11289      */
11290     load : function(options){
11291         options = options || {};
11292         if(this.fireEvent("beforeload", this, options) !== false){
11293             this.storeOptions(options);
11294             var p = Roo.apply(options.params || {}, this.baseParams);
11295             // if meta was not loaded from remote source.. try requesting it.
11296             if (!this.reader.metaFromRemote) {
11297                 p._requestMeta = 1;
11298             }
11299             if(this.sortInfo && this.remoteSort){
11300                 var pn = this.paramNames;
11301                 p[pn["sort"]] = this.sortInfo.field;
11302                 p[pn["dir"]] = this.sortInfo.direction;
11303             }
11304             if (this.multiSort) {
11305                 var pn = this.paramNames;
11306                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11307             }
11308             
11309             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11310         }
11311     },
11312
11313     /**
11314      * Reloads the Record cache from the configured Proxy using the configured Reader and
11315      * the options from the last load operation performed.
11316      * @param {Object} options (optional) An object containing properties which may override the options
11317      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11318      * the most recently used options are reused).
11319      */
11320     reload : function(options){
11321         this.load(Roo.applyIf(options||{}, this.lastOptions));
11322     },
11323
11324     // private
11325     // Called as a callback by the Reader during a load operation.
11326     loadRecords : function(o, options, success){
11327         if(!o || success === false){
11328             if(success !== false){
11329                 this.fireEvent("load", this, [], options, o);
11330             }
11331             if(options.callback){
11332                 options.callback.call(options.scope || this, [], options, false);
11333             }
11334             return;
11335         }
11336         // if data returned failure - throw an exception.
11337         if (o.success === false) {
11338             // show a message if no listener is registered.
11339             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11340                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11341             }
11342             // loadmask wil be hooked into this..
11343             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11344             return;
11345         }
11346         var r = o.records, t = o.totalRecords || r.length;
11347         
11348         this.fireEvent("beforeloadadd", this, r, options, o);
11349         
11350         if(!options || options.add !== true){
11351             if(this.pruneModifiedRecords){
11352                 this.modified = [];
11353             }
11354             for(var i = 0, len = r.length; i < len; i++){
11355                 r[i].join(this);
11356             }
11357             if(this.snapshot){
11358                 this.data = this.snapshot;
11359                 delete this.snapshot;
11360             }
11361             this.data.clear();
11362             this.data.addAll(r);
11363             this.totalLength = t;
11364             this.applySort();
11365             this.fireEvent("datachanged", this);
11366         }else{
11367             this.totalLength = Math.max(t, this.data.length+r.length);
11368             this.add(r);
11369         }
11370         
11371         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11372                 
11373             var e = new Roo.data.Record({});
11374
11375             e.set(this.parent.displayField, this.parent.emptyTitle);
11376             e.set(this.parent.valueField, '');
11377
11378             this.insert(0, e);
11379         }
11380             
11381         this.fireEvent("load", this, r, options, o);
11382         if(options.callback){
11383             options.callback.call(options.scope || this, r, options, true);
11384         }
11385     },
11386
11387
11388     /**
11389      * Loads data from a passed data block. A Reader which understands the format of the data
11390      * must have been configured in the constructor.
11391      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11392      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11393      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11394      */
11395     loadData : function(o, append){
11396         var r = this.reader.readRecords(o);
11397         this.loadRecords(r, {add: append}, true);
11398     },
11399
11400     /**
11401      * Gets the number of cached records.
11402      * <p>
11403      * <em>If using paging, this may not be the total size of the dataset. If the data object
11404      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11405      * the data set size</em>
11406      */
11407     getCount : function(){
11408         return this.data.length || 0;
11409     },
11410
11411     /**
11412      * Gets the total number of records in the dataset as returned by the server.
11413      * <p>
11414      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11415      * the dataset size</em>
11416      */
11417     getTotalCount : function(){
11418         return this.totalLength || 0;
11419     },
11420
11421     /**
11422      * Returns the sort state of the Store as an object with two properties:
11423      * <pre><code>
11424  field {String} The name of the field by which the Records are sorted
11425  direction {String} The sort order, "ASC" or "DESC"
11426      * </code></pre>
11427      */
11428     getSortState : function(){
11429         return this.sortInfo;
11430     },
11431
11432     // private
11433     applySort : function(){
11434         if(this.sortInfo && !this.remoteSort){
11435             var s = this.sortInfo, f = s.field;
11436             var st = this.fields.get(f).sortType;
11437             var fn = function(r1, r2){
11438                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11439                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11440             };
11441             this.data.sort(s.direction, fn);
11442             if(this.snapshot && this.snapshot != this.data){
11443                 this.snapshot.sort(s.direction, fn);
11444             }
11445         }
11446     },
11447
11448     /**
11449      * Sets the default sort column and order to be used by the next load operation.
11450      * @param {String} fieldName The name of the field to sort by.
11451      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11452      */
11453     setDefaultSort : function(field, dir){
11454         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11455     },
11456
11457     /**
11458      * Sort the Records.
11459      * If remote sorting is used, the sort is performed on the server, and the cache is
11460      * reloaded. If local sorting is used, the cache is sorted internally.
11461      * @param {String} fieldName The name of the field to sort by.
11462      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11463      */
11464     sort : function(fieldName, dir){
11465         var f = this.fields.get(fieldName);
11466         if(!dir){
11467             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11468             
11469             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11470                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11471             }else{
11472                 dir = f.sortDir;
11473             }
11474         }
11475         this.sortToggle[f.name] = dir;
11476         this.sortInfo = {field: f.name, direction: dir};
11477         if(!this.remoteSort){
11478             this.applySort();
11479             this.fireEvent("datachanged", this);
11480         }else{
11481             this.load(this.lastOptions);
11482         }
11483     },
11484
11485     /**
11486      * Calls the specified function for each of the Records in the cache.
11487      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11488      * Returning <em>false</em> aborts and exits the iteration.
11489      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11490      */
11491     each : function(fn, scope){
11492         this.data.each(fn, scope);
11493     },
11494
11495     /**
11496      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11497      * (e.g., during paging).
11498      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11499      */
11500     getModifiedRecords : function(){
11501         return this.modified;
11502     },
11503
11504     // private
11505     createFilterFn : function(property, value, anyMatch){
11506         if(!value.exec){ // not a regex
11507             value = String(value);
11508             if(value.length == 0){
11509                 return false;
11510             }
11511             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11512         }
11513         return function(r){
11514             return value.test(r.data[property]);
11515         };
11516     },
11517
11518     /**
11519      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11520      * @param {String} property A field on your records
11521      * @param {Number} start The record index to start at (defaults to 0)
11522      * @param {Number} end The last record index to include (defaults to length - 1)
11523      * @return {Number} The sum
11524      */
11525     sum : function(property, start, end){
11526         var rs = this.data.items, v = 0;
11527         start = start || 0;
11528         end = (end || end === 0) ? end : rs.length-1;
11529
11530         for(var i = start; i <= end; i++){
11531             v += (rs[i].data[property] || 0);
11532         }
11533         return v;
11534     },
11535
11536     /**
11537      * Filter the records by a specified property.
11538      * @param {String} field A field on your records
11539      * @param {String/RegExp} value Either a string that the field
11540      * should start with or a RegExp to test against the field
11541      * @param {Boolean} anyMatch True to match any part not just the beginning
11542      */
11543     filter : function(property, value, anyMatch){
11544         var fn = this.createFilterFn(property, value, anyMatch);
11545         return fn ? this.filterBy(fn) : this.clearFilter();
11546     },
11547
11548     /**
11549      * Filter by a function. The specified function will be called with each
11550      * record in this data source. If the function returns true the record is included,
11551      * otherwise it is filtered.
11552      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11553      * @param {Object} scope (optional) The scope of the function (defaults to this)
11554      */
11555     filterBy : function(fn, scope){
11556         this.snapshot = this.snapshot || this.data;
11557         this.data = this.queryBy(fn, scope||this);
11558         this.fireEvent("datachanged", this);
11559     },
11560
11561     /**
11562      * Query the records by a specified property.
11563      * @param {String} field A field on your records
11564      * @param {String/RegExp} value Either a string that the field
11565      * should start with or a RegExp to test against the field
11566      * @param {Boolean} anyMatch True to match any part not just the beginning
11567      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11568      */
11569     query : function(property, value, anyMatch){
11570         var fn = this.createFilterFn(property, value, anyMatch);
11571         return fn ? this.queryBy(fn) : this.data.clone();
11572     },
11573
11574     /**
11575      * Query by a function. The specified function will be called with each
11576      * record in this data source. If the function returns true the record is included
11577      * in the results.
11578      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11579      * @param {Object} scope (optional) The scope of the function (defaults to this)
11580       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11581      **/
11582     queryBy : function(fn, scope){
11583         var data = this.snapshot || this.data;
11584         return data.filterBy(fn, scope||this);
11585     },
11586
11587     /**
11588      * Collects unique values for a particular dataIndex from this store.
11589      * @param {String} dataIndex The property to collect
11590      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11591      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11592      * @return {Array} An array of the unique values
11593      **/
11594     collect : function(dataIndex, allowNull, bypassFilter){
11595         var d = (bypassFilter === true && this.snapshot) ?
11596                 this.snapshot.items : this.data.items;
11597         var v, sv, r = [], l = {};
11598         for(var i = 0, len = d.length; i < len; i++){
11599             v = d[i].data[dataIndex];
11600             sv = String(v);
11601             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11602                 l[sv] = true;
11603                 r[r.length] = v;
11604             }
11605         }
11606         return r;
11607     },
11608
11609     /**
11610      * Revert to a view of the Record cache with no filtering applied.
11611      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11612      */
11613     clearFilter : function(suppressEvent){
11614         if(this.snapshot && this.snapshot != this.data){
11615             this.data = this.snapshot;
11616             delete this.snapshot;
11617             if(suppressEvent !== true){
11618                 this.fireEvent("datachanged", this);
11619             }
11620         }
11621     },
11622
11623     // private
11624     afterEdit : function(record){
11625         if(this.modified.indexOf(record) == -1){
11626             this.modified.push(record);
11627         }
11628         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11629     },
11630     
11631     // private
11632     afterReject : function(record){
11633         this.modified.remove(record);
11634         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11635     },
11636
11637     // private
11638     afterCommit : function(record){
11639         this.modified.remove(record);
11640         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11641     },
11642
11643     /**
11644      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11645      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11646      */
11647     commitChanges : function(){
11648         var m = this.modified.slice(0);
11649         this.modified = [];
11650         for(var i = 0, len = m.length; i < len; i++){
11651             m[i].commit();
11652         }
11653     },
11654
11655     /**
11656      * Cancel outstanding changes on all changed records.
11657      */
11658     rejectChanges : function(){
11659         var m = this.modified.slice(0);
11660         this.modified = [];
11661         for(var i = 0, len = m.length; i < len; i++){
11662             m[i].reject();
11663         }
11664     },
11665
11666     onMetaChange : function(meta, rtype, o){
11667         this.recordType = rtype;
11668         this.fields = rtype.prototype.fields;
11669         delete this.snapshot;
11670         this.sortInfo = meta.sortInfo || this.sortInfo;
11671         this.modified = [];
11672         this.fireEvent('metachange', this, this.reader.meta);
11673     },
11674     
11675     moveIndex : function(data, type)
11676     {
11677         var index = this.indexOf(data);
11678         
11679         var newIndex = index + type;
11680         
11681         this.remove(data);
11682         
11683         this.insert(newIndex, data);
11684         
11685     }
11686 });/*
11687  * Based on:
11688  * Ext JS Library 1.1.1
11689  * Copyright(c) 2006-2007, Ext JS, LLC.
11690  *
11691  * Originally Released Under LGPL - original licence link has changed is not relivant.
11692  *
11693  * Fork - LGPL
11694  * <script type="text/javascript">
11695  */
11696
11697 /**
11698  * @class Roo.data.SimpleStore
11699  * @extends Roo.data.Store
11700  * Small helper class to make creating Stores from Array data easier.
11701  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11702  * @cfg {Array} fields An array of field definition objects, or field name strings.
11703  * @cfg {Array} data The multi-dimensional array of data
11704  * @constructor
11705  * @param {Object} config
11706  */
11707 Roo.data.SimpleStore = function(config){
11708     Roo.data.SimpleStore.superclass.constructor.call(this, {
11709         isLocal : true,
11710         reader: new Roo.data.ArrayReader({
11711                 id: config.id
11712             },
11713             Roo.data.Record.create(config.fields)
11714         ),
11715         proxy : new Roo.data.MemoryProxy(config.data)
11716     });
11717     this.load();
11718 };
11719 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11720  * Based on:
11721  * Ext JS Library 1.1.1
11722  * Copyright(c) 2006-2007, Ext JS, LLC.
11723  *
11724  * Originally Released Under LGPL - original licence link has changed is not relivant.
11725  *
11726  * Fork - LGPL
11727  * <script type="text/javascript">
11728  */
11729
11730 /**
11731 /**
11732  * @extends Roo.data.Store
11733  * @class Roo.data.JsonStore
11734  * Small helper class to make creating Stores for JSON data easier. <br/>
11735 <pre><code>
11736 var store = new Roo.data.JsonStore({
11737     url: 'get-images.php',
11738     root: 'images',
11739     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11740 });
11741 </code></pre>
11742  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11743  * JsonReader and HttpProxy (unless inline data is provided).</b>
11744  * @cfg {Array} fields An array of field definition objects, or field name strings.
11745  * @constructor
11746  * @param {Object} config
11747  */
11748 Roo.data.JsonStore = function(c){
11749     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11750         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11751         reader: new Roo.data.JsonReader(c, c.fields)
11752     }));
11753 };
11754 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11755  * Based on:
11756  * Ext JS Library 1.1.1
11757  * Copyright(c) 2006-2007, Ext JS, LLC.
11758  *
11759  * Originally Released Under LGPL - original licence link has changed is not relivant.
11760  *
11761  * Fork - LGPL
11762  * <script type="text/javascript">
11763  */
11764
11765  
11766 Roo.data.Field = function(config){
11767     if(typeof config == "string"){
11768         config = {name: config};
11769     }
11770     Roo.apply(this, config);
11771     
11772     if(!this.type){
11773         this.type = "auto";
11774     }
11775     
11776     var st = Roo.data.SortTypes;
11777     // named sortTypes are supported, here we look them up
11778     if(typeof this.sortType == "string"){
11779         this.sortType = st[this.sortType];
11780     }
11781     
11782     // set default sortType for strings and dates
11783     if(!this.sortType){
11784         switch(this.type){
11785             case "string":
11786                 this.sortType = st.asUCString;
11787                 break;
11788             case "date":
11789                 this.sortType = st.asDate;
11790                 break;
11791             default:
11792                 this.sortType = st.none;
11793         }
11794     }
11795
11796     // define once
11797     var stripRe = /[\$,%]/g;
11798
11799     // prebuilt conversion function for this field, instead of
11800     // switching every time we're reading a value
11801     if(!this.convert){
11802         var cv, dateFormat = this.dateFormat;
11803         switch(this.type){
11804             case "":
11805             case "auto":
11806             case undefined:
11807                 cv = function(v){ return v; };
11808                 break;
11809             case "string":
11810                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11811                 break;
11812             case "int":
11813                 cv = function(v){
11814                     return v !== undefined && v !== null && v !== '' ?
11815                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11816                     };
11817                 break;
11818             case "float":
11819                 cv = function(v){
11820                     return v !== undefined && v !== null && v !== '' ?
11821                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11822                     };
11823                 break;
11824             case "bool":
11825             case "boolean":
11826                 cv = function(v){ return v === true || v === "true" || v == 1; };
11827                 break;
11828             case "date":
11829                 cv = function(v){
11830                     if(!v){
11831                         return '';
11832                     }
11833                     if(v instanceof Date){
11834                         return v;
11835                     }
11836                     if(dateFormat){
11837                         if(dateFormat == "timestamp"){
11838                             return new Date(v*1000);
11839                         }
11840                         return Date.parseDate(v, dateFormat);
11841                     }
11842                     var parsed = Date.parse(v);
11843                     return parsed ? new Date(parsed) : null;
11844                 };
11845              break;
11846             
11847         }
11848         this.convert = cv;
11849     }
11850 };
11851
11852 Roo.data.Field.prototype = {
11853     dateFormat: null,
11854     defaultValue: "",
11855     mapping: null,
11856     sortType : null,
11857     sortDir : "ASC"
11858 };/*
11859  * Based on:
11860  * Ext JS Library 1.1.1
11861  * Copyright(c) 2006-2007, Ext JS, LLC.
11862  *
11863  * Originally Released Under LGPL - original licence link has changed is not relivant.
11864  *
11865  * Fork - LGPL
11866  * <script type="text/javascript">
11867  */
11868  
11869 // Base class for reading structured data from a data source.  This class is intended to be
11870 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11871
11872 /**
11873  * @class Roo.data.DataReader
11874  * Base class for reading structured data from a data source.  This class is intended to be
11875  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11876  */
11877
11878 Roo.data.DataReader = function(meta, recordType){
11879     
11880     this.meta = meta;
11881     
11882     this.recordType = recordType instanceof Array ? 
11883         Roo.data.Record.create(recordType) : recordType;
11884 };
11885
11886 Roo.data.DataReader.prototype = {
11887      /**
11888      * Create an empty record
11889      * @param {Object} data (optional) - overlay some values
11890      * @return {Roo.data.Record} record created.
11891      */
11892     newRow :  function(d) {
11893         var da =  {};
11894         this.recordType.prototype.fields.each(function(c) {
11895             switch( c.type) {
11896                 case 'int' : da[c.name] = 0; break;
11897                 case 'date' : da[c.name] = new Date(); break;
11898                 case 'float' : da[c.name] = 0.0; break;
11899                 case 'boolean' : da[c.name] = false; break;
11900                 default : da[c.name] = ""; break;
11901             }
11902             
11903         });
11904         return new this.recordType(Roo.apply(da, d));
11905     }
11906     
11907 };/*
11908  * Based on:
11909  * Ext JS Library 1.1.1
11910  * Copyright(c) 2006-2007, Ext JS, LLC.
11911  *
11912  * Originally Released Under LGPL - original licence link has changed is not relivant.
11913  *
11914  * Fork - LGPL
11915  * <script type="text/javascript">
11916  */
11917
11918 /**
11919  * @class Roo.data.DataProxy
11920  * @extends Roo.data.Observable
11921  * This class is an abstract base class for implementations which provide retrieval of
11922  * unformatted data objects.<br>
11923  * <p>
11924  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11925  * (of the appropriate type which knows how to parse the data object) to provide a block of
11926  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11927  * <p>
11928  * Custom implementations must implement the load method as described in
11929  * {@link Roo.data.HttpProxy#load}.
11930  */
11931 Roo.data.DataProxy = function(){
11932     this.addEvents({
11933         /**
11934          * @event beforeload
11935          * Fires before a network request is made to retrieve a data object.
11936          * @param {Object} This DataProxy object.
11937          * @param {Object} params The params parameter to the load function.
11938          */
11939         beforeload : true,
11940         /**
11941          * @event load
11942          * Fires before the load method's callback is called.
11943          * @param {Object} This DataProxy object.
11944          * @param {Object} o The data object.
11945          * @param {Object} arg The callback argument object passed to the load function.
11946          */
11947         load : true,
11948         /**
11949          * @event loadexception
11950          * Fires if an Exception occurs during data retrieval.
11951          * @param {Object} This DataProxy object.
11952          * @param {Object} o The data object.
11953          * @param {Object} arg The callback argument object passed to the load function.
11954          * @param {Object} e The Exception.
11955          */
11956         loadexception : true
11957     });
11958     Roo.data.DataProxy.superclass.constructor.call(this);
11959 };
11960
11961 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11962
11963     /**
11964      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11965      */
11966 /*
11967  * Based on:
11968  * Ext JS Library 1.1.1
11969  * Copyright(c) 2006-2007, Ext JS, LLC.
11970  *
11971  * Originally Released Under LGPL - original licence link has changed is not relivant.
11972  *
11973  * Fork - LGPL
11974  * <script type="text/javascript">
11975  */
11976 /**
11977  * @class Roo.data.MemoryProxy
11978  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11979  * to the Reader when its load method is called.
11980  * @constructor
11981  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11982  */
11983 Roo.data.MemoryProxy = function(data){
11984     if (data.data) {
11985         data = data.data;
11986     }
11987     Roo.data.MemoryProxy.superclass.constructor.call(this);
11988     this.data = data;
11989 };
11990
11991 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11992     
11993     /**
11994      * Load data from the requested source (in this case an in-memory
11995      * data object passed to the constructor), read the data object into
11996      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11997      * process that block using the passed callback.
11998      * @param {Object} params This parameter is not used by the MemoryProxy class.
11999      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12000      * object into a block of Roo.data.Records.
12001      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12002      * The function must be passed <ul>
12003      * <li>The Record block object</li>
12004      * <li>The "arg" argument from the load function</li>
12005      * <li>A boolean success indicator</li>
12006      * </ul>
12007      * @param {Object} scope The scope in which to call the callback
12008      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12009      */
12010     load : function(params, reader, callback, scope, arg){
12011         params = params || {};
12012         var result;
12013         try {
12014             result = reader.readRecords(this.data);
12015         }catch(e){
12016             this.fireEvent("loadexception", this, arg, null, e);
12017             callback.call(scope, null, arg, false);
12018             return;
12019         }
12020         callback.call(scope, result, arg, true);
12021     },
12022     
12023     // private
12024     update : function(params, records){
12025         
12026     }
12027 });/*
12028  * Based on:
12029  * Ext JS Library 1.1.1
12030  * Copyright(c) 2006-2007, Ext JS, LLC.
12031  *
12032  * Originally Released Under LGPL - original licence link has changed is not relivant.
12033  *
12034  * Fork - LGPL
12035  * <script type="text/javascript">
12036  */
12037 /**
12038  * @class Roo.data.HttpProxy
12039  * @extends Roo.data.DataProxy
12040  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12041  * configured to reference a certain URL.<br><br>
12042  * <p>
12043  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12044  * from which the running page was served.<br><br>
12045  * <p>
12046  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12047  * <p>
12048  * Be aware that to enable the browser to parse an XML document, the server must set
12049  * the Content-Type header in the HTTP response to "text/xml".
12050  * @constructor
12051  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12052  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12053  * will be used to make the request.
12054  */
12055 Roo.data.HttpProxy = function(conn){
12056     Roo.data.HttpProxy.superclass.constructor.call(this);
12057     // is conn a conn config or a real conn?
12058     this.conn = conn;
12059     this.useAjax = !conn || !conn.events;
12060   
12061 };
12062
12063 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12064     // thse are take from connection...
12065     
12066     /**
12067      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12068      */
12069     /**
12070      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12071      * extra parameters to each request made by this object. (defaults to undefined)
12072      */
12073     /**
12074      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12075      *  to each request made by this object. (defaults to undefined)
12076      */
12077     /**
12078      * @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)
12079      */
12080     /**
12081      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12082      */
12083      /**
12084      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12085      * @type Boolean
12086      */
12087   
12088
12089     /**
12090      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12091      * @type Boolean
12092      */
12093     /**
12094      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12095      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12096      * a finer-grained basis than the DataProxy events.
12097      */
12098     getConnection : function(){
12099         return this.useAjax ? Roo.Ajax : this.conn;
12100     },
12101
12102     /**
12103      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12104      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12105      * process that block using the passed callback.
12106      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12107      * for the request to the remote server.
12108      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12109      * object into a block of Roo.data.Records.
12110      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12111      * The function must be passed <ul>
12112      * <li>The Record block object</li>
12113      * <li>The "arg" argument from the load function</li>
12114      * <li>A boolean success indicator</li>
12115      * </ul>
12116      * @param {Object} scope The scope in which to call the callback
12117      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12118      */
12119     load : function(params, reader, callback, scope, arg){
12120         if(this.fireEvent("beforeload", this, params) !== false){
12121             var  o = {
12122                 params : params || {},
12123                 request: {
12124                     callback : callback,
12125                     scope : scope,
12126                     arg : arg
12127                 },
12128                 reader: reader,
12129                 callback : this.loadResponse,
12130                 scope: this
12131             };
12132             if(this.useAjax){
12133                 Roo.applyIf(o, this.conn);
12134                 if(this.activeRequest){
12135                     Roo.Ajax.abort(this.activeRequest);
12136                 }
12137                 this.activeRequest = Roo.Ajax.request(o);
12138             }else{
12139                 this.conn.request(o);
12140             }
12141         }else{
12142             callback.call(scope||this, null, arg, false);
12143         }
12144     },
12145
12146     // private
12147     loadResponse : function(o, success, response){
12148         delete this.activeRequest;
12149         if(!success){
12150             this.fireEvent("loadexception", this, o, response);
12151             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12152             return;
12153         }
12154         var result;
12155         try {
12156             result = o.reader.read(response);
12157         }catch(e){
12158             this.fireEvent("loadexception", this, o, response, e);
12159             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12160             return;
12161         }
12162         
12163         this.fireEvent("load", this, o, o.request.arg);
12164         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12165     },
12166
12167     // private
12168     update : function(dataSet){
12169
12170     },
12171
12172     // private
12173     updateResponse : function(dataSet){
12174
12175     }
12176 });/*
12177  * Based on:
12178  * Ext JS Library 1.1.1
12179  * Copyright(c) 2006-2007, Ext JS, LLC.
12180  *
12181  * Originally Released Under LGPL - original licence link has changed is not relivant.
12182  *
12183  * Fork - LGPL
12184  * <script type="text/javascript">
12185  */
12186
12187 /**
12188  * @class Roo.data.ScriptTagProxy
12189  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12190  * other than the originating domain of the running page.<br><br>
12191  * <p>
12192  * <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
12193  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12194  * <p>
12195  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12196  * source code that is used as the source inside a &lt;script> tag.<br><br>
12197  * <p>
12198  * In order for the browser to process the returned data, the server must wrap the data object
12199  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12200  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12201  * depending on whether the callback name was passed:
12202  * <p>
12203  * <pre><code>
12204 boolean scriptTag = false;
12205 String cb = request.getParameter("callback");
12206 if (cb != null) {
12207     scriptTag = true;
12208     response.setContentType("text/javascript");
12209 } else {
12210     response.setContentType("application/x-json");
12211 }
12212 Writer out = response.getWriter();
12213 if (scriptTag) {
12214     out.write(cb + "(");
12215 }
12216 out.print(dataBlock.toJsonString());
12217 if (scriptTag) {
12218     out.write(");");
12219 }
12220 </pre></code>
12221  *
12222  * @constructor
12223  * @param {Object} config A configuration object.
12224  */
12225 Roo.data.ScriptTagProxy = function(config){
12226     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12227     Roo.apply(this, config);
12228     this.head = document.getElementsByTagName("head")[0];
12229 };
12230
12231 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12232
12233 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12234     /**
12235      * @cfg {String} url The URL from which to request the data object.
12236      */
12237     /**
12238      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12239      */
12240     timeout : 30000,
12241     /**
12242      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12243      * the server the name of the callback function set up by the load call to process the returned data object.
12244      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12245      * javascript output which calls this named function passing the data object as its only parameter.
12246      */
12247     callbackParam : "callback",
12248     /**
12249      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12250      * name to the request.
12251      */
12252     nocache : true,
12253
12254     /**
12255      * Load data from the configured URL, read the data object into
12256      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12257      * process that block using the passed callback.
12258      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12259      * for the request to the remote server.
12260      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12261      * object into a block of Roo.data.Records.
12262      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12263      * The function must be passed <ul>
12264      * <li>The Record block object</li>
12265      * <li>The "arg" argument from the load function</li>
12266      * <li>A boolean success indicator</li>
12267      * </ul>
12268      * @param {Object} scope The scope in which to call the callback
12269      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12270      */
12271     load : function(params, reader, callback, scope, arg){
12272         if(this.fireEvent("beforeload", this, params) !== false){
12273
12274             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12275
12276             var url = this.url;
12277             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12278             if(this.nocache){
12279                 url += "&_dc=" + (new Date().getTime());
12280             }
12281             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12282             var trans = {
12283                 id : transId,
12284                 cb : "stcCallback"+transId,
12285                 scriptId : "stcScript"+transId,
12286                 params : params,
12287                 arg : arg,
12288                 url : url,
12289                 callback : callback,
12290                 scope : scope,
12291                 reader : reader
12292             };
12293             var conn = this;
12294
12295             window[trans.cb] = function(o){
12296                 conn.handleResponse(o, trans);
12297             };
12298
12299             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12300
12301             if(this.autoAbort !== false){
12302                 this.abort();
12303             }
12304
12305             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12306
12307             var script = document.createElement("script");
12308             script.setAttribute("src", url);
12309             script.setAttribute("type", "text/javascript");
12310             script.setAttribute("id", trans.scriptId);
12311             this.head.appendChild(script);
12312
12313             this.trans = trans;
12314         }else{
12315             callback.call(scope||this, null, arg, false);
12316         }
12317     },
12318
12319     // private
12320     isLoading : function(){
12321         return this.trans ? true : false;
12322     },
12323
12324     /**
12325      * Abort the current server request.
12326      */
12327     abort : function(){
12328         if(this.isLoading()){
12329             this.destroyTrans(this.trans);
12330         }
12331     },
12332
12333     // private
12334     destroyTrans : function(trans, isLoaded){
12335         this.head.removeChild(document.getElementById(trans.scriptId));
12336         clearTimeout(trans.timeoutId);
12337         if(isLoaded){
12338             window[trans.cb] = undefined;
12339             try{
12340                 delete window[trans.cb];
12341             }catch(e){}
12342         }else{
12343             // if hasn't been loaded, wait for load to remove it to prevent script error
12344             window[trans.cb] = function(){
12345                 window[trans.cb] = undefined;
12346                 try{
12347                     delete window[trans.cb];
12348                 }catch(e){}
12349             };
12350         }
12351     },
12352
12353     // private
12354     handleResponse : function(o, trans){
12355         this.trans = false;
12356         this.destroyTrans(trans, true);
12357         var result;
12358         try {
12359             result = trans.reader.readRecords(o);
12360         }catch(e){
12361             this.fireEvent("loadexception", this, o, trans.arg, e);
12362             trans.callback.call(trans.scope||window, null, trans.arg, false);
12363             return;
12364         }
12365         this.fireEvent("load", this, o, trans.arg);
12366         trans.callback.call(trans.scope||window, result, trans.arg, true);
12367     },
12368
12369     // private
12370     handleFailure : function(trans){
12371         this.trans = false;
12372         this.destroyTrans(trans, false);
12373         this.fireEvent("loadexception", this, null, trans.arg);
12374         trans.callback.call(trans.scope||window, null, trans.arg, false);
12375     }
12376 });/*
12377  * Based on:
12378  * Ext JS Library 1.1.1
12379  * Copyright(c) 2006-2007, Ext JS, LLC.
12380  *
12381  * Originally Released Under LGPL - original licence link has changed is not relivant.
12382  *
12383  * Fork - LGPL
12384  * <script type="text/javascript">
12385  */
12386
12387 /**
12388  * @class Roo.data.JsonReader
12389  * @extends Roo.data.DataReader
12390  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12391  * based on mappings in a provided Roo.data.Record constructor.
12392  * 
12393  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12394  * in the reply previously. 
12395  * 
12396  * <p>
12397  * Example code:
12398  * <pre><code>
12399 var RecordDef = Roo.data.Record.create([
12400     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12401     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12402 ]);
12403 var myReader = new Roo.data.JsonReader({
12404     totalProperty: "results",    // The property which contains the total dataset size (optional)
12405     root: "rows",                // The property which contains an Array of row objects
12406     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12407 }, RecordDef);
12408 </code></pre>
12409  * <p>
12410  * This would consume a JSON file like this:
12411  * <pre><code>
12412 { 'results': 2, 'rows': [
12413     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12414     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12415 }
12416 </code></pre>
12417  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12418  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12419  * paged from the remote server.
12420  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12421  * @cfg {String} root name of the property which contains the Array of row objects.
12422  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12423  * @cfg {Array} fields Array of field definition objects
12424  * @constructor
12425  * Create a new JsonReader
12426  * @param {Object} meta Metadata configuration options
12427  * @param {Object} recordType Either an Array of field definition objects,
12428  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12429  */
12430 Roo.data.JsonReader = function(meta, recordType){
12431     
12432     meta = meta || {};
12433     // set some defaults:
12434     Roo.applyIf(meta, {
12435         totalProperty: 'total',
12436         successProperty : 'success',
12437         root : 'data',
12438         id : 'id'
12439     });
12440     
12441     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12442 };
12443 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12444     
12445     /**
12446      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12447      * Used by Store query builder to append _requestMeta to params.
12448      * 
12449      */
12450     metaFromRemote : false,
12451     /**
12452      * This method is only used by a DataProxy which has retrieved data from a remote server.
12453      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12454      * @return {Object} data A data block which is used by an Roo.data.Store object as
12455      * a cache of Roo.data.Records.
12456      */
12457     read : function(response){
12458         var json = response.responseText;
12459        
12460         var o = /* eval:var:o */ eval("("+json+")");
12461         if(!o) {
12462             throw {message: "JsonReader.read: Json object not found"};
12463         }
12464         
12465         if(o.metaData){
12466             
12467             delete this.ef;
12468             this.metaFromRemote = true;
12469             this.meta = o.metaData;
12470             this.recordType = Roo.data.Record.create(o.metaData.fields);
12471             this.onMetaChange(this.meta, this.recordType, o);
12472         }
12473         return this.readRecords(o);
12474     },
12475
12476     // private function a store will implement
12477     onMetaChange : function(meta, recordType, o){
12478
12479     },
12480
12481     /**
12482          * @ignore
12483          */
12484     simpleAccess: function(obj, subsc) {
12485         return obj[subsc];
12486     },
12487
12488         /**
12489          * @ignore
12490          */
12491     getJsonAccessor: function(){
12492         var re = /[\[\.]/;
12493         return function(expr) {
12494             try {
12495                 return(re.test(expr))
12496                     ? new Function("obj", "return obj." + expr)
12497                     : function(obj){
12498                         return obj[expr];
12499                     };
12500             } catch(e){}
12501             return Roo.emptyFn;
12502         };
12503     }(),
12504
12505     /**
12506      * Create a data block containing Roo.data.Records from an XML document.
12507      * @param {Object} o An object which contains an Array of row objects in the property specified
12508      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12509      * which contains the total size of the dataset.
12510      * @return {Object} data A data block which is used by an Roo.data.Store object as
12511      * a cache of Roo.data.Records.
12512      */
12513     readRecords : function(o){
12514         /**
12515          * After any data loads, the raw JSON data is available for further custom processing.
12516          * @type Object
12517          */
12518         this.o = o;
12519         var s = this.meta, Record = this.recordType,
12520             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12521
12522 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12523         if (!this.ef) {
12524             if(s.totalProperty) {
12525                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12526                 }
12527                 if(s.successProperty) {
12528                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12529                 }
12530                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12531                 if (s.id) {
12532                         var g = this.getJsonAccessor(s.id);
12533                         this.getId = function(rec) {
12534                                 var r = g(rec);  
12535                                 return (r === undefined || r === "") ? null : r;
12536                         };
12537                 } else {
12538                         this.getId = function(){return null;};
12539                 }
12540             this.ef = [];
12541             for(var jj = 0; jj < fl; jj++){
12542                 f = fi[jj];
12543                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12544                 this.ef[jj] = this.getJsonAccessor(map);
12545             }
12546         }
12547
12548         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12549         if(s.totalProperty){
12550             var vt = parseInt(this.getTotal(o), 10);
12551             if(!isNaN(vt)){
12552                 totalRecords = vt;
12553             }
12554         }
12555         if(s.successProperty){
12556             var vs = this.getSuccess(o);
12557             if(vs === false || vs === 'false'){
12558                 success = false;
12559             }
12560         }
12561         var records = [];
12562         for(var i = 0; i < c; i++){
12563                 var n = root[i];
12564             var values = {};
12565             var id = this.getId(n);
12566             for(var j = 0; j < fl; j++){
12567                 f = fi[j];
12568             var v = this.ef[j](n);
12569             if (!f.convert) {
12570                 Roo.log('missing convert for ' + f.name);
12571                 Roo.log(f);
12572                 continue;
12573             }
12574             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12575             }
12576             var record = new Record(values, id);
12577             record.json = n;
12578             records[i] = record;
12579         }
12580         return {
12581             raw : o,
12582             success : success,
12583             records : records,
12584             totalRecords : totalRecords
12585         };
12586     }
12587 });/*
12588  * Based on:
12589  * Ext JS Library 1.1.1
12590  * Copyright(c) 2006-2007, Ext JS, LLC.
12591  *
12592  * Originally Released Under LGPL - original licence link has changed is not relivant.
12593  *
12594  * Fork - LGPL
12595  * <script type="text/javascript">
12596  */
12597
12598 /**
12599  * @class Roo.data.ArrayReader
12600  * @extends Roo.data.DataReader
12601  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12602  * Each element of that Array represents a row of data fields. The
12603  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12604  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12605  * <p>
12606  * Example code:.
12607  * <pre><code>
12608 var RecordDef = Roo.data.Record.create([
12609     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12610     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12611 ]);
12612 var myReader = new Roo.data.ArrayReader({
12613     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12614 }, RecordDef);
12615 </code></pre>
12616  * <p>
12617  * This would consume an Array like this:
12618  * <pre><code>
12619 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12620   </code></pre>
12621  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12622  * @constructor
12623  * Create a new JsonReader
12624  * @param {Object} meta Metadata configuration options.
12625  * @param {Object} recordType Either an Array of field definition objects
12626  * as specified to {@link Roo.data.Record#create},
12627  * or an {@link Roo.data.Record} object
12628  * created using {@link Roo.data.Record#create}.
12629  */
12630 Roo.data.ArrayReader = function(meta, recordType){
12631     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12632 };
12633
12634 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12635     /**
12636      * Create a data block containing Roo.data.Records from an XML document.
12637      * @param {Object} o An Array of row objects which represents the dataset.
12638      * @return {Object} data A data block which is used by an Roo.data.Store object as
12639      * a cache of Roo.data.Records.
12640      */
12641     readRecords : function(o){
12642         var sid = this.meta ? this.meta.id : null;
12643         var recordType = this.recordType, fields = recordType.prototype.fields;
12644         var records = [];
12645         var root = o;
12646             for(var i = 0; i < root.length; i++){
12647                     var n = root[i];
12648                 var values = {};
12649                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12650                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12651                 var f = fields.items[j];
12652                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12653                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12654                 v = f.convert(v);
12655                 values[f.name] = v;
12656             }
12657                 var record = new recordType(values, id);
12658                 record.json = n;
12659                 records[records.length] = record;
12660             }
12661             return {
12662                 records : records,
12663                 totalRecords : records.length
12664             };
12665     }
12666 });/*
12667  * - LGPL
12668  * * 
12669  */
12670
12671 /**
12672  * @class Roo.bootstrap.ComboBox
12673  * @extends Roo.bootstrap.TriggerField
12674  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12675  * @cfg {Boolean} append (true|false) default false
12676  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12677  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12678  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12679  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12680  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12681  * @cfg {Boolean} animate default true
12682  * @cfg {Boolean} emptyResultText only for touch device
12683  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12684  * @cfg {String} emptyTitle default ''
12685  * @constructor
12686  * Create a new ComboBox.
12687  * @param {Object} config Configuration options
12688  */
12689 Roo.bootstrap.ComboBox = function(config){
12690     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12691     this.addEvents({
12692         /**
12693          * @event expand
12694          * Fires when the dropdown list is expanded
12695         * @param {Roo.bootstrap.ComboBox} combo This combo box
12696         */
12697         'expand' : true,
12698         /**
12699          * @event collapse
12700          * Fires when the dropdown list is collapsed
12701         * @param {Roo.bootstrap.ComboBox} combo This combo box
12702         */
12703         'collapse' : true,
12704         /**
12705          * @event beforeselect
12706          * Fires before a list item is selected. Return false to cancel the selection.
12707         * @param {Roo.bootstrap.ComboBox} combo This combo box
12708         * @param {Roo.data.Record} record The data record returned from the underlying store
12709         * @param {Number} index The index of the selected item in the dropdown list
12710         */
12711         'beforeselect' : true,
12712         /**
12713          * @event select
12714          * Fires when a list item is selected
12715         * @param {Roo.bootstrap.ComboBox} combo This combo box
12716         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12717         * @param {Number} index The index of the selected item in the dropdown list
12718         */
12719         'select' : true,
12720         /**
12721          * @event beforequery
12722          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12723          * The event object passed has these properties:
12724         * @param {Roo.bootstrap.ComboBox} combo This combo box
12725         * @param {String} query The query
12726         * @param {Boolean} forceAll true to force "all" query
12727         * @param {Boolean} cancel true to cancel the query
12728         * @param {Object} e The query event object
12729         */
12730         'beforequery': true,
12731          /**
12732          * @event add
12733          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12734         * @param {Roo.bootstrap.ComboBox} combo This combo box
12735         */
12736         'add' : true,
12737         /**
12738          * @event edit
12739          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12740         * @param {Roo.bootstrap.ComboBox} combo This combo box
12741         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12742         */
12743         'edit' : true,
12744         /**
12745          * @event remove
12746          * Fires when the remove value from the combobox array
12747         * @param {Roo.bootstrap.ComboBox} combo This combo box
12748         */
12749         'remove' : true,
12750         /**
12751          * @event afterremove
12752          * Fires when the remove value from the combobox array
12753         * @param {Roo.bootstrap.ComboBox} combo This combo box
12754         */
12755         'afterremove' : true,
12756         /**
12757          * @event specialfilter
12758          * Fires when specialfilter
12759             * @param {Roo.bootstrap.ComboBox} combo This combo box
12760             */
12761         'specialfilter' : true,
12762         /**
12763          * @event tick
12764          * Fires when tick the element
12765             * @param {Roo.bootstrap.ComboBox} combo This combo box
12766             */
12767         'tick' : true,
12768         /**
12769          * @event touchviewdisplay
12770          * Fires when touch view require special display (default is using displayField)
12771             * @param {Roo.bootstrap.ComboBox} combo This combo box
12772             * @param {Object} cfg set html .
12773             */
12774         'touchviewdisplay' : true
12775         
12776     });
12777     
12778     this.item = [];
12779     this.tickItems = [];
12780     
12781     this.selectedIndex = -1;
12782     if(this.mode == 'local'){
12783         if(config.queryDelay === undefined){
12784             this.queryDelay = 10;
12785         }
12786         if(config.minChars === undefined){
12787             this.minChars = 0;
12788         }
12789     }
12790 };
12791
12792 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12793      
12794     /**
12795      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12796      * rendering into an Roo.Editor, defaults to false)
12797      */
12798     /**
12799      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12800      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12801      */
12802     /**
12803      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12804      */
12805     /**
12806      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12807      * the dropdown list (defaults to undefined, with no header element)
12808      */
12809
12810      /**
12811      * @cfg {String/Roo.Template} tpl The template to use to render the output
12812      */
12813      
12814      /**
12815      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12816      */
12817     listWidth: undefined,
12818     /**
12819      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12820      * mode = 'remote' or 'text' if mode = 'local')
12821      */
12822     displayField: undefined,
12823     
12824     /**
12825      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12826      * mode = 'remote' or 'value' if mode = 'local'). 
12827      * Note: use of a valueField requires the user make a selection
12828      * in order for a value to be mapped.
12829      */
12830     valueField: undefined,
12831     /**
12832      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12833      */
12834     modalTitle : '',
12835     
12836     /**
12837      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12838      * field's data value (defaults to the underlying DOM element's name)
12839      */
12840     hiddenName: undefined,
12841     /**
12842      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12843      */
12844     listClass: '',
12845     /**
12846      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12847      */
12848     selectedClass: 'active',
12849     
12850     /**
12851      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12852      */
12853     shadow:'sides',
12854     /**
12855      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12856      * anchor positions (defaults to 'tl-bl')
12857      */
12858     listAlign: 'tl-bl?',
12859     /**
12860      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12861      */
12862     maxHeight: 300,
12863     /**
12864      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12865      * query specified by the allQuery config option (defaults to 'query')
12866      */
12867     triggerAction: 'query',
12868     /**
12869      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12870      * (defaults to 4, does not apply if editable = false)
12871      */
12872     minChars : 4,
12873     /**
12874      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12875      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12876      */
12877     typeAhead: false,
12878     /**
12879      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12880      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12881      */
12882     queryDelay: 500,
12883     /**
12884      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12885      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12886      */
12887     pageSize: 0,
12888     /**
12889      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12890      * when editable = true (defaults to false)
12891      */
12892     selectOnFocus:false,
12893     /**
12894      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12895      */
12896     queryParam: 'query',
12897     /**
12898      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12899      * when mode = 'remote' (defaults to 'Loading...')
12900      */
12901     loadingText: 'Loading...',
12902     /**
12903      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12904      */
12905     resizable: false,
12906     /**
12907      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12908      */
12909     handleHeight : 8,
12910     /**
12911      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12912      * traditional select (defaults to true)
12913      */
12914     editable: true,
12915     /**
12916      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12917      */
12918     allQuery: '',
12919     /**
12920      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12921      */
12922     mode: 'remote',
12923     /**
12924      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12925      * listWidth has a higher value)
12926      */
12927     minListWidth : 70,
12928     /**
12929      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12930      * allow the user to set arbitrary text into the field (defaults to false)
12931      */
12932     forceSelection:false,
12933     /**
12934      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12935      * if typeAhead = true (defaults to 250)
12936      */
12937     typeAheadDelay : 250,
12938     /**
12939      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12940      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12941      */
12942     valueNotFoundText : undefined,
12943     /**
12944      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12945      */
12946     blockFocus : false,
12947     
12948     /**
12949      * @cfg {Boolean} disableClear Disable showing of clear button.
12950      */
12951     disableClear : false,
12952     /**
12953      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12954      */
12955     alwaysQuery : false,
12956     
12957     /**
12958      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12959      */
12960     multiple : false,
12961     
12962     /**
12963      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12964      */
12965     invalidClass : "has-warning",
12966     
12967     /**
12968      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12969      */
12970     validClass : "has-success",
12971     
12972     /**
12973      * @cfg {Boolean} specialFilter (true|false) special filter default false
12974      */
12975     specialFilter : false,
12976     
12977     /**
12978      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12979      */
12980     mobileTouchView : true,
12981     
12982     /**
12983      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12984      */
12985     useNativeIOS : false,
12986     
12987     ios_options : false,
12988     
12989     //private
12990     addicon : false,
12991     editicon: false,
12992     
12993     page: 0,
12994     hasQuery: false,
12995     append: false,
12996     loadNext: false,
12997     autoFocus : true,
12998     tickable : false,
12999     btnPosition : 'right',
13000     triggerList : true,
13001     showToggleBtn : true,
13002     animate : true,
13003     emptyResultText: 'Empty',
13004     triggerText : 'Select',
13005     emptyTitle : '',
13006     
13007     // element that contains real text value.. (when hidden is used..)
13008     
13009     getAutoCreate : function()
13010     {   
13011         var cfg = false;
13012         //render
13013         /*
13014          * Render classic select for iso
13015          */
13016         
13017         if(Roo.isIOS && this.useNativeIOS){
13018             cfg = this.getAutoCreateNativeIOS();
13019             return cfg;
13020         }
13021         
13022         /*
13023          * Touch Devices
13024          */
13025         
13026         if(Roo.isTouch && this.mobileTouchView){
13027             cfg = this.getAutoCreateTouchView();
13028             return cfg;;
13029         }
13030         
13031         /*
13032          *  Normal ComboBox
13033          */
13034         if(!this.tickable){
13035             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13036             return cfg;
13037         }
13038         
13039         /*
13040          *  ComboBox with tickable selections
13041          */
13042              
13043         var align = this.labelAlign || this.parentLabelAlign();
13044         
13045         cfg = {
13046             cls : 'form-group roo-combobox-tickable' //input-group
13047         };
13048         
13049         var btn_text_select = '';
13050         var btn_text_done = '';
13051         var btn_text_cancel = '';
13052         
13053         if (this.btn_text_show) {
13054             btn_text_select = 'Select';
13055             btn_text_done = 'Done';
13056             btn_text_cancel = 'Cancel'; 
13057         }
13058         
13059         var buttons = {
13060             tag : 'div',
13061             cls : 'tickable-buttons',
13062             cn : [
13063                 {
13064                     tag : 'button',
13065                     type : 'button',
13066                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13067                     //html : this.triggerText
13068                     html: btn_text_select
13069                 },
13070                 {
13071                     tag : 'button',
13072                     type : 'button',
13073                     name : 'ok',
13074                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13075                     //html : 'Done'
13076                     html: btn_text_done
13077                 },
13078                 {
13079                     tag : 'button',
13080                     type : 'button',
13081                     name : 'cancel',
13082                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13083                     //html : 'Cancel'
13084                     html: btn_text_cancel
13085                 }
13086             ]
13087         };
13088         
13089         if(this.editable){
13090             buttons.cn.unshift({
13091                 tag: 'input',
13092                 cls: 'roo-select2-search-field-input'
13093             });
13094         }
13095         
13096         var _this = this;
13097         
13098         Roo.each(buttons.cn, function(c){
13099             if (_this.size) {
13100                 c.cls += ' btn-' + _this.size;
13101             }
13102
13103             if (_this.disabled) {
13104                 c.disabled = true;
13105             }
13106         });
13107         
13108         var box = {
13109             tag: 'div',
13110             cn: [
13111                 {
13112                     tag: 'input',
13113                     type : 'hidden',
13114                     cls: 'form-hidden-field'
13115                 },
13116                 {
13117                     tag: 'ul',
13118                     cls: 'roo-select2-choices',
13119                     cn:[
13120                         {
13121                             tag: 'li',
13122                             cls: 'roo-select2-search-field',
13123                             cn: [
13124                                 buttons
13125                             ]
13126                         }
13127                     ]
13128                 }
13129             ]
13130         };
13131         
13132         var combobox = {
13133             cls: 'roo-select2-container input-group roo-select2-container-multi',
13134             cn: [
13135                 box
13136 //                {
13137 //                    tag: 'ul',
13138 //                    cls: 'typeahead typeahead-long dropdown-menu',
13139 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13140 //                }
13141             ]
13142         };
13143         
13144         if(this.hasFeedback && !this.allowBlank){
13145             
13146             var feedback = {
13147                 tag: 'span',
13148                 cls: 'glyphicon form-control-feedback'
13149             };
13150
13151             combobox.cn.push(feedback);
13152         }
13153         
13154         
13155         if (align ==='left' && this.fieldLabel.length) {
13156             
13157             cfg.cls += ' roo-form-group-label-left';
13158             
13159             cfg.cn = [
13160                 {
13161                     tag : 'i',
13162                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13163                     tooltip : 'This field is required'
13164                 },
13165                 {
13166                     tag: 'label',
13167                     'for' :  id,
13168                     cls : 'control-label',
13169                     html : this.fieldLabel
13170
13171                 },
13172                 {
13173                     cls : "", 
13174                     cn: [
13175                         combobox
13176                     ]
13177                 }
13178
13179             ];
13180             
13181             var labelCfg = cfg.cn[1];
13182             var contentCfg = cfg.cn[2];
13183             
13184
13185             if(this.indicatorpos == 'right'){
13186                 
13187                 cfg.cn = [
13188                     {
13189                         tag: 'label',
13190                         'for' :  id,
13191                         cls : 'control-label',
13192                         cn : [
13193                             {
13194                                 tag : 'span',
13195                                 html : this.fieldLabel
13196                             },
13197                             {
13198                                 tag : 'i',
13199                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13200                                 tooltip : 'This field is required'
13201                             }
13202                         ]
13203                     },
13204                     {
13205                         cls : "",
13206                         cn: [
13207                             combobox
13208                         ]
13209                     }
13210
13211                 ];
13212                 
13213                 
13214                 
13215                 labelCfg = cfg.cn[0];
13216                 contentCfg = cfg.cn[1];
13217             
13218             }
13219             
13220             if(this.labelWidth > 12){
13221                 labelCfg.style = "width: " + this.labelWidth + 'px';
13222             }
13223             
13224             if(this.labelWidth < 13 && this.labelmd == 0){
13225                 this.labelmd = this.labelWidth;
13226             }
13227             
13228             if(this.labellg > 0){
13229                 labelCfg.cls += ' col-lg-' + this.labellg;
13230                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13231             }
13232             
13233             if(this.labelmd > 0){
13234                 labelCfg.cls += ' col-md-' + this.labelmd;
13235                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13236             }
13237             
13238             if(this.labelsm > 0){
13239                 labelCfg.cls += ' col-sm-' + this.labelsm;
13240                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13241             }
13242             
13243             if(this.labelxs > 0){
13244                 labelCfg.cls += ' col-xs-' + this.labelxs;
13245                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13246             }
13247                 
13248                 
13249         } else if ( this.fieldLabel.length) {
13250 //                Roo.log(" label");
13251                  cfg.cn = [
13252                     {
13253                         tag : 'i',
13254                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13255                         tooltip : 'This field is required'
13256                     },
13257                     {
13258                         tag: 'label',
13259                         //cls : 'input-group-addon',
13260                         html : this.fieldLabel
13261                     },
13262                     combobox
13263                 ];
13264                 
13265                 if(this.indicatorpos == 'right'){
13266                     cfg.cn = [
13267                         {
13268                             tag: 'label',
13269                             //cls : 'input-group-addon',
13270                             html : this.fieldLabel
13271                         },
13272                         {
13273                             tag : 'i',
13274                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13275                             tooltip : 'This field is required'
13276                         },
13277                         combobox
13278                     ];
13279                     
13280                 }
13281
13282         } else {
13283             
13284 //                Roo.log(" no label && no align");
13285                 cfg = combobox
13286                      
13287                 
13288         }
13289          
13290         var settings=this;
13291         ['xs','sm','md','lg'].map(function(size){
13292             if (settings[size]) {
13293                 cfg.cls += ' col-' + size + '-' + settings[size];
13294             }
13295         });
13296         
13297         return cfg;
13298         
13299     },
13300     
13301     _initEventsCalled : false,
13302     
13303     // private
13304     initEvents: function()
13305     {   
13306         if (this._initEventsCalled) { // as we call render... prevent looping...
13307             return;
13308         }
13309         this._initEventsCalled = true;
13310         
13311         if (!this.store) {
13312             throw "can not find store for combo";
13313         }
13314         
13315         this.indicator = this.indicatorEl();
13316         
13317         this.store = Roo.factory(this.store, Roo.data);
13318         this.store.parent = this;
13319         
13320         // if we are building from html. then this element is so complex, that we can not really
13321         // use the rendered HTML.
13322         // so we have to trash and replace the previous code.
13323         if (Roo.XComponent.build_from_html) {
13324             // remove this element....
13325             var e = this.el.dom, k=0;
13326             while (e ) { e = e.previousSibling;  ++k;}
13327
13328             this.el.remove();
13329             
13330             this.el=false;
13331             this.rendered = false;
13332             
13333             this.render(this.parent().getChildContainer(true), k);
13334         }
13335         
13336         if(Roo.isIOS && this.useNativeIOS){
13337             this.initIOSView();
13338             return;
13339         }
13340         
13341         /*
13342          * Touch Devices
13343          */
13344         
13345         if(Roo.isTouch && this.mobileTouchView){
13346             this.initTouchView();
13347             return;
13348         }
13349         
13350         if(this.tickable){
13351             this.initTickableEvents();
13352             return;
13353         }
13354         
13355         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13356         
13357         if(this.hiddenName){
13358             
13359             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13360             
13361             this.hiddenField.dom.value =
13362                 this.hiddenValue !== undefined ? this.hiddenValue :
13363                 this.value !== undefined ? this.value : '';
13364
13365             // prevent input submission
13366             this.el.dom.removeAttribute('name');
13367             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13368              
13369              
13370         }
13371         //if(Roo.isGecko){
13372         //    this.el.dom.setAttribute('autocomplete', 'off');
13373         //}
13374         
13375         var cls = 'x-combo-list';
13376         
13377         //this.list = new Roo.Layer({
13378         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13379         //});
13380         
13381         var _this = this;
13382         
13383         (function(){
13384             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13385             _this.list.setWidth(lw);
13386         }).defer(100);
13387         
13388         this.list.on('mouseover', this.onViewOver, this);
13389         this.list.on('mousemove', this.onViewMove, this);
13390         this.list.on('scroll', this.onViewScroll, this);
13391         
13392         /*
13393         this.list.swallowEvent('mousewheel');
13394         this.assetHeight = 0;
13395
13396         if(this.title){
13397             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13398             this.assetHeight += this.header.getHeight();
13399         }
13400
13401         this.innerList = this.list.createChild({cls:cls+'-inner'});
13402         this.innerList.on('mouseover', this.onViewOver, this);
13403         this.innerList.on('mousemove', this.onViewMove, this);
13404         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13405         
13406         if(this.allowBlank && !this.pageSize && !this.disableClear){
13407             this.footer = this.list.createChild({cls:cls+'-ft'});
13408             this.pageTb = new Roo.Toolbar(this.footer);
13409            
13410         }
13411         if(this.pageSize){
13412             this.footer = this.list.createChild({cls:cls+'-ft'});
13413             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13414                     {pageSize: this.pageSize});
13415             
13416         }
13417         
13418         if (this.pageTb && this.allowBlank && !this.disableClear) {
13419             var _this = this;
13420             this.pageTb.add(new Roo.Toolbar.Fill(), {
13421                 cls: 'x-btn-icon x-btn-clear',
13422                 text: '&#160;',
13423                 handler: function()
13424                 {
13425                     _this.collapse();
13426                     _this.clearValue();
13427                     _this.onSelect(false, -1);
13428                 }
13429             });
13430         }
13431         if (this.footer) {
13432             this.assetHeight += this.footer.getHeight();
13433         }
13434         */
13435             
13436         if(!this.tpl){
13437             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13438         }
13439
13440         this.view = new Roo.View(this.list, this.tpl, {
13441             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13442         });
13443         //this.view.wrapEl.setDisplayed(false);
13444         this.view.on('click', this.onViewClick, this);
13445         
13446         
13447         this.store.on('beforeload', this.onBeforeLoad, this);
13448         this.store.on('load', this.onLoad, this);
13449         this.store.on('loadexception', this.onLoadException, this);
13450         /*
13451         if(this.resizable){
13452             this.resizer = new Roo.Resizable(this.list,  {
13453                pinned:true, handles:'se'
13454             });
13455             this.resizer.on('resize', function(r, w, h){
13456                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13457                 this.listWidth = w;
13458                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13459                 this.restrictHeight();
13460             }, this);
13461             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13462         }
13463         */
13464         if(!this.editable){
13465             this.editable = true;
13466             this.setEditable(false);
13467         }
13468         
13469         /*
13470         
13471         if (typeof(this.events.add.listeners) != 'undefined') {
13472             
13473             this.addicon = this.wrap.createChild(
13474                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13475        
13476             this.addicon.on('click', function(e) {
13477                 this.fireEvent('add', this);
13478             }, this);
13479         }
13480         if (typeof(this.events.edit.listeners) != 'undefined') {
13481             
13482             this.editicon = this.wrap.createChild(
13483                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13484             if (this.addicon) {
13485                 this.editicon.setStyle('margin-left', '40px');
13486             }
13487             this.editicon.on('click', function(e) {
13488                 
13489                 // we fire even  if inothing is selected..
13490                 this.fireEvent('edit', this, this.lastData );
13491                 
13492             }, this);
13493         }
13494         */
13495         
13496         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13497             "up" : function(e){
13498                 this.inKeyMode = true;
13499                 this.selectPrev();
13500             },
13501
13502             "down" : function(e){
13503                 if(!this.isExpanded()){
13504                     this.onTriggerClick();
13505                 }else{
13506                     this.inKeyMode = true;
13507                     this.selectNext();
13508                 }
13509             },
13510
13511             "enter" : function(e){
13512 //                this.onViewClick();
13513                 //return true;
13514                 this.collapse();
13515                 
13516                 if(this.fireEvent("specialkey", this, e)){
13517                     this.onViewClick(false);
13518                 }
13519                 
13520                 return true;
13521             },
13522
13523             "esc" : function(e){
13524                 this.collapse();
13525             },
13526
13527             "tab" : function(e){
13528                 this.collapse();
13529                 
13530                 if(this.fireEvent("specialkey", this, e)){
13531                     this.onViewClick(false);
13532                 }
13533                 
13534                 return true;
13535             },
13536
13537             scope : this,
13538
13539             doRelay : function(foo, bar, hname){
13540                 if(hname == 'down' || this.scope.isExpanded()){
13541                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13542                 }
13543                 return true;
13544             },
13545
13546             forceKeyDown: true
13547         });
13548         
13549         
13550         this.queryDelay = Math.max(this.queryDelay || 10,
13551                 this.mode == 'local' ? 10 : 250);
13552         
13553         
13554         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13555         
13556         if(this.typeAhead){
13557             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13558         }
13559         if(this.editable !== false){
13560             this.inputEl().on("keyup", this.onKeyUp, this);
13561         }
13562         if(this.forceSelection){
13563             this.inputEl().on('blur', this.doForce, this);
13564         }
13565         
13566         if(this.multiple){
13567             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13568             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13569         }
13570     },
13571     
13572     initTickableEvents: function()
13573     {   
13574         this.createList();
13575         
13576         if(this.hiddenName){
13577             
13578             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13579             
13580             this.hiddenField.dom.value =
13581                 this.hiddenValue !== undefined ? this.hiddenValue :
13582                 this.value !== undefined ? this.value : '';
13583
13584             // prevent input submission
13585             this.el.dom.removeAttribute('name');
13586             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13587              
13588              
13589         }
13590         
13591 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13592         
13593         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13594         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13595         if(this.triggerList){
13596             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13597         }
13598          
13599         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13600         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13601         
13602         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13603         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13604         
13605         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13606         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13607         
13608         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13609         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13610         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13611         
13612         this.okBtn.hide();
13613         this.cancelBtn.hide();
13614         
13615         var _this = this;
13616         
13617         (function(){
13618             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13619             _this.list.setWidth(lw);
13620         }).defer(100);
13621         
13622         this.list.on('mouseover', this.onViewOver, this);
13623         this.list.on('mousemove', this.onViewMove, this);
13624         
13625         this.list.on('scroll', this.onViewScroll, this);
13626         
13627         if(!this.tpl){
13628             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13629                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13630         }
13631
13632         this.view = new Roo.View(this.list, this.tpl, {
13633             singleSelect:true,
13634             tickable:true,
13635             parent:this,
13636             store: this.store,
13637             selectedClass: this.selectedClass
13638         });
13639         
13640         //this.view.wrapEl.setDisplayed(false);
13641         this.view.on('click', this.onViewClick, this);
13642         
13643         
13644         
13645         this.store.on('beforeload', this.onBeforeLoad, this);
13646         this.store.on('load', this.onLoad, this);
13647         this.store.on('loadexception', this.onLoadException, this);
13648         
13649         if(this.editable){
13650             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13651                 "up" : function(e){
13652                     this.inKeyMode = true;
13653                     this.selectPrev();
13654                 },
13655
13656                 "down" : function(e){
13657                     this.inKeyMode = true;
13658                     this.selectNext();
13659                 },
13660
13661                 "enter" : function(e){
13662                     if(this.fireEvent("specialkey", this, e)){
13663                         this.onViewClick(false);
13664                     }
13665                     
13666                     return true;
13667                 },
13668
13669                 "esc" : function(e){
13670                     this.onTickableFooterButtonClick(e, false, false);
13671                 },
13672
13673                 "tab" : function(e){
13674                     this.fireEvent("specialkey", this, e);
13675                     
13676                     this.onTickableFooterButtonClick(e, false, false);
13677                     
13678                     return true;
13679                 },
13680
13681                 scope : this,
13682
13683                 doRelay : function(e, fn, key){
13684                     if(this.scope.isExpanded()){
13685                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13686                     }
13687                     return true;
13688                 },
13689
13690                 forceKeyDown: true
13691             });
13692         }
13693         
13694         this.queryDelay = Math.max(this.queryDelay || 10,
13695                 this.mode == 'local' ? 10 : 250);
13696         
13697         
13698         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13699         
13700         if(this.typeAhead){
13701             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13702         }
13703         
13704         if(this.editable !== false){
13705             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13706         }
13707         
13708         this.indicator = this.indicatorEl();
13709         
13710         if(this.indicator){
13711             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13712             this.indicator.hide();
13713         }
13714         
13715     },
13716
13717     onDestroy : function(){
13718         if(this.view){
13719             this.view.setStore(null);
13720             this.view.el.removeAllListeners();
13721             this.view.el.remove();
13722             this.view.purgeListeners();
13723         }
13724         if(this.list){
13725             this.list.dom.innerHTML  = '';
13726         }
13727         
13728         if(this.store){
13729             this.store.un('beforeload', this.onBeforeLoad, this);
13730             this.store.un('load', this.onLoad, this);
13731             this.store.un('loadexception', this.onLoadException, this);
13732         }
13733         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13734     },
13735
13736     // private
13737     fireKey : function(e){
13738         if(e.isNavKeyPress() && !this.list.isVisible()){
13739             this.fireEvent("specialkey", this, e);
13740         }
13741     },
13742
13743     // private
13744     onResize: function(w, h){
13745 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13746 //        
13747 //        if(typeof w != 'number'){
13748 //            // we do not handle it!?!?
13749 //            return;
13750 //        }
13751 //        var tw = this.trigger.getWidth();
13752 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13753 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13754 //        var x = w - tw;
13755 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13756 //            
13757 //        //this.trigger.setStyle('left', x+'px');
13758 //        
13759 //        if(this.list && this.listWidth === undefined){
13760 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13761 //            this.list.setWidth(lw);
13762 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13763 //        }
13764         
13765     
13766         
13767     },
13768
13769     /**
13770      * Allow or prevent the user from directly editing the field text.  If false is passed,
13771      * the user will only be able to select from the items defined in the dropdown list.  This method
13772      * is the runtime equivalent of setting the 'editable' config option at config time.
13773      * @param {Boolean} value True to allow the user to directly edit the field text
13774      */
13775     setEditable : function(value){
13776         if(value == this.editable){
13777             return;
13778         }
13779         this.editable = value;
13780         if(!value){
13781             this.inputEl().dom.setAttribute('readOnly', true);
13782             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13783             this.inputEl().addClass('x-combo-noedit');
13784         }else{
13785             this.inputEl().dom.setAttribute('readOnly', false);
13786             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13787             this.inputEl().removeClass('x-combo-noedit');
13788         }
13789     },
13790
13791     // private
13792     
13793     onBeforeLoad : function(combo,opts){
13794         if(!this.hasFocus){
13795             return;
13796         }
13797          if (!opts.add) {
13798             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13799          }
13800         this.restrictHeight();
13801         this.selectedIndex = -1;
13802     },
13803
13804     // private
13805     onLoad : function(){
13806         
13807         this.hasQuery = false;
13808         
13809         if(!this.hasFocus){
13810             return;
13811         }
13812         
13813         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13814             this.loading.hide();
13815         }
13816         
13817         if(this.store.getCount() > 0){
13818             
13819             this.expand();
13820             this.restrictHeight();
13821             if(this.lastQuery == this.allQuery){
13822                 if(this.editable && !this.tickable){
13823                     this.inputEl().dom.select();
13824                 }
13825                 
13826                 if(
13827                     !this.selectByValue(this.value, true) &&
13828                     this.autoFocus && 
13829                     (
13830                         !this.store.lastOptions ||
13831                         typeof(this.store.lastOptions.add) == 'undefined' || 
13832                         this.store.lastOptions.add != true
13833                     )
13834                 ){
13835                     this.select(0, true);
13836                 }
13837             }else{
13838                 if(this.autoFocus){
13839                     this.selectNext();
13840                 }
13841                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13842                     this.taTask.delay(this.typeAheadDelay);
13843                 }
13844             }
13845         }else{
13846             this.onEmptyResults();
13847         }
13848         
13849         //this.el.focus();
13850     },
13851     // private
13852     onLoadException : function()
13853     {
13854         this.hasQuery = false;
13855         
13856         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13857             this.loading.hide();
13858         }
13859         
13860         if(this.tickable && this.editable){
13861             return;
13862         }
13863         
13864         this.collapse();
13865         // only causes errors at present
13866         //Roo.log(this.store.reader.jsonData);
13867         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13868             // fixme
13869             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13870         //}
13871         
13872         
13873     },
13874     // private
13875     onTypeAhead : function(){
13876         if(this.store.getCount() > 0){
13877             var r = this.store.getAt(0);
13878             var newValue = r.data[this.displayField];
13879             var len = newValue.length;
13880             var selStart = this.getRawValue().length;
13881             
13882             if(selStart != len){
13883                 this.setRawValue(newValue);
13884                 this.selectText(selStart, newValue.length);
13885             }
13886         }
13887     },
13888
13889     // private
13890     onSelect : function(record, index){
13891         
13892         if(this.fireEvent('beforeselect', this, record, index) !== false){
13893         
13894             this.setFromData(index > -1 ? record.data : false);
13895             
13896             this.collapse();
13897             this.fireEvent('select', this, record, index);
13898         }
13899     },
13900
13901     /**
13902      * Returns the currently selected field value or empty string if no value is set.
13903      * @return {String} value The selected value
13904      */
13905     getValue : function()
13906     {
13907         if(Roo.isIOS && this.useNativeIOS){
13908             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13909         }
13910         
13911         if(this.multiple){
13912             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13913         }
13914         
13915         if(this.valueField){
13916             return typeof this.value != 'undefined' ? this.value : '';
13917         }else{
13918             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13919         }
13920     },
13921     
13922     getRawValue : function()
13923     {
13924         if(Roo.isIOS && this.useNativeIOS){
13925             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13926         }
13927         
13928         var v = this.inputEl().getValue();
13929         
13930         return v;
13931     },
13932
13933     /**
13934      * Clears any text/value currently set in the field
13935      */
13936     clearValue : function(){
13937         
13938         if(this.hiddenField){
13939             this.hiddenField.dom.value = '';
13940         }
13941         this.value = '';
13942         this.setRawValue('');
13943         this.lastSelectionText = '';
13944         this.lastData = false;
13945         
13946         var close = this.closeTriggerEl();
13947         
13948         if(close){
13949             close.hide();
13950         }
13951         
13952         this.validate();
13953         
13954     },
13955
13956     /**
13957      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13958      * will be displayed in the field.  If the value does not match the data value of an existing item,
13959      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13960      * Otherwise the field will be blank (although the value will still be set).
13961      * @param {String} value The value to match
13962      */
13963     setValue : function(v)
13964     {
13965         if(Roo.isIOS && this.useNativeIOS){
13966             this.setIOSValue(v);
13967             return;
13968         }
13969         
13970         if(this.multiple){
13971             this.syncValue();
13972             return;
13973         }
13974         
13975         var text = v;
13976         if(this.valueField){
13977             var r = this.findRecord(this.valueField, v);
13978             if(r){
13979                 text = r.data[this.displayField];
13980             }else if(this.valueNotFoundText !== undefined){
13981                 text = this.valueNotFoundText;
13982             }
13983         }
13984         this.lastSelectionText = text;
13985         if(this.hiddenField){
13986             this.hiddenField.dom.value = v;
13987         }
13988         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13989         this.value = v;
13990         
13991         var close = this.closeTriggerEl();
13992         
13993         if(close){
13994             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13995         }
13996         
13997         this.validate();
13998     },
13999     /**
14000      * @property {Object} the last set data for the element
14001      */
14002     
14003     lastData : false,
14004     /**
14005      * Sets the value of the field based on a object which is related to the record format for the store.
14006      * @param {Object} value the value to set as. or false on reset?
14007      */
14008     setFromData : function(o){
14009         
14010         if(this.multiple){
14011             this.addItem(o);
14012             return;
14013         }
14014             
14015         var dv = ''; // display value
14016         var vv = ''; // value value..
14017         this.lastData = o;
14018         if (this.displayField) {
14019             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14020         } else {
14021             // this is an error condition!!!
14022             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14023         }
14024         
14025         if(this.valueField){
14026             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14027         }
14028         
14029         var close = this.closeTriggerEl();
14030         
14031         if(close){
14032             if(dv.length || vv * 1 > 0){
14033                 close.show() ;
14034                 this.blockFocus=true;
14035             } else {
14036                 close.hide();
14037             }             
14038         }
14039         
14040         if(this.hiddenField){
14041             this.hiddenField.dom.value = vv;
14042             
14043             this.lastSelectionText = dv;
14044             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14045             this.value = vv;
14046             return;
14047         }
14048         // no hidden field.. - we store the value in 'value', but still display
14049         // display field!!!!
14050         this.lastSelectionText = dv;
14051         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14052         this.value = vv;
14053         
14054         
14055         
14056     },
14057     // private
14058     reset : function(){
14059         // overridden so that last data is reset..
14060         
14061         if(this.multiple){
14062             this.clearItem();
14063             return;
14064         }
14065         
14066         this.setValue(this.originalValue);
14067         //this.clearInvalid();
14068         this.lastData = false;
14069         if (this.view) {
14070             this.view.clearSelections();
14071         }
14072         
14073         this.validate();
14074     },
14075     // private
14076     findRecord : function(prop, value){
14077         var record;
14078         if(this.store.getCount() > 0){
14079             this.store.each(function(r){
14080                 if(r.data[prop] == value){
14081                     record = r;
14082                     return false;
14083                 }
14084                 return true;
14085             });
14086         }
14087         return record;
14088     },
14089     
14090     getName: function()
14091     {
14092         // returns hidden if it's set..
14093         if (!this.rendered) {return ''};
14094         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14095         
14096     },
14097     // private
14098     onViewMove : function(e, t){
14099         this.inKeyMode = false;
14100     },
14101
14102     // private
14103     onViewOver : function(e, t){
14104         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14105             return;
14106         }
14107         var item = this.view.findItemFromChild(t);
14108         
14109         if(item){
14110             var index = this.view.indexOf(item);
14111             this.select(index, false);
14112         }
14113     },
14114
14115     // private
14116     onViewClick : function(view, doFocus, el, e)
14117     {
14118         var index = this.view.getSelectedIndexes()[0];
14119         
14120         var r = this.store.getAt(index);
14121         
14122         if(this.tickable){
14123             
14124             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14125                 return;
14126             }
14127             
14128             var rm = false;
14129             var _this = this;
14130             
14131             Roo.each(this.tickItems, function(v,k){
14132                 
14133                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14134                     Roo.log(v);
14135                     _this.tickItems.splice(k, 1);
14136                     
14137                     if(typeof(e) == 'undefined' && view == false){
14138                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14139                     }
14140                     
14141                     rm = true;
14142                     return;
14143                 }
14144             });
14145             
14146             if(rm){
14147                 return;
14148             }
14149             
14150             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14151                 this.tickItems.push(r.data);
14152             }
14153             
14154             if(typeof(e) == 'undefined' && view == false){
14155                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14156             }
14157                     
14158             return;
14159         }
14160         
14161         if(r){
14162             this.onSelect(r, index);
14163         }
14164         if(doFocus !== false && !this.blockFocus){
14165             this.inputEl().focus();
14166         }
14167     },
14168
14169     // private
14170     restrictHeight : function(){
14171         //this.innerList.dom.style.height = '';
14172         //var inner = this.innerList.dom;
14173         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14174         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14175         //this.list.beginUpdate();
14176         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14177         this.list.alignTo(this.inputEl(), this.listAlign);
14178         this.list.alignTo(this.inputEl(), this.listAlign);
14179         //this.list.endUpdate();
14180     },
14181
14182     // private
14183     onEmptyResults : function(){
14184         
14185         if(this.tickable && this.editable){
14186             this.hasFocus = false;
14187             this.restrictHeight();
14188             return;
14189         }
14190         
14191         this.collapse();
14192     },
14193
14194     /**
14195      * Returns true if the dropdown list is expanded, else false.
14196      */
14197     isExpanded : function(){
14198         return this.list.isVisible();
14199     },
14200
14201     /**
14202      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14203      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14204      * @param {String} value The data value of the item to select
14205      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14206      * selected item if it is not currently in view (defaults to true)
14207      * @return {Boolean} True if the value matched an item in the list, else false
14208      */
14209     selectByValue : function(v, scrollIntoView){
14210         if(v !== undefined && v !== null){
14211             var r = this.findRecord(this.valueField || this.displayField, v);
14212             if(r){
14213                 this.select(this.store.indexOf(r), scrollIntoView);
14214                 return true;
14215             }
14216         }
14217         return false;
14218     },
14219
14220     /**
14221      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14222      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14223      * @param {Number} index The zero-based index of the list item to select
14224      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14225      * selected item if it is not currently in view (defaults to true)
14226      */
14227     select : function(index, scrollIntoView){
14228         this.selectedIndex = index;
14229         this.view.select(index);
14230         if(scrollIntoView !== false){
14231             var el = this.view.getNode(index);
14232             /*
14233              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14234              */
14235             if(el){
14236                 this.list.scrollChildIntoView(el, false);
14237             }
14238         }
14239     },
14240
14241     // private
14242     selectNext : function(){
14243         var ct = this.store.getCount();
14244         if(ct > 0){
14245             if(this.selectedIndex == -1){
14246                 this.select(0);
14247             }else if(this.selectedIndex < ct-1){
14248                 this.select(this.selectedIndex+1);
14249             }
14250         }
14251     },
14252
14253     // private
14254     selectPrev : function(){
14255         var ct = this.store.getCount();
14256         if(ct > 0){
14257             if(this.selectedIndex == -1){
14258                 this.select(0);
14259             }else if(this.selectedIndex != 0){
14260                 this.select(this.selectedIndex-1);
14261             }
14262         }
14263     },
14264
14265     // private
14266     onKeyUp : function(e){
14267         if(this.editable !== false && !e.isSpecialKey()){
14268             this.lastKey = e.getKey();
14269             this.dqTask.delay(this.queryDelay);
14270         }
14271     },
14272
14273     // private
14274     validateBlur : function(){
14275         return !this.list || !this.list.isVisible();   
14276     },
14277
14278     // private
14279     initQuery : function(){
14280         
14281         var v = this.getRawValue();
14282         
14283         if(this.tickable && this.editable){
14284             v = this.tickableInputEl().getValue();
14285         }
14286         
14287         this.doQuery(v);
14288     },
14289
14290     // private
14291     doForce : function(){
14292         if(this.inputEl().dom.value.length > 0){
14293             this.inputEl().dom.value =
14294                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14295              
14296         }
14297     },
14298
14299     /**
14300      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14301      * query allowing the query action to be canceled if needed.
14302      * @param {String} query The SQL query to execute
14303      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14304      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14305      * saved in the current store (defaults to false)
14306      */
14307     doQuery : function(q, forceAll){
14308         
14309         if(q === undefined || q === null){
14310             q = '';
14311         }
14312         var qe = {
14313             query: q,
14314             forceAll: forceAll,
14315             combo: this,
14316             cancel:false
14317         };
14318         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14319             return false;
14320         }
14321         q = qe.query;
14322         
14323         forceAll = qe.forceAll;
14324         if(forceAll === true || (q.length >= this.minChars)){
14325             
14326             this.hasQuery = true;
14327             
14328             if(this.lastQuery != q || this.alwaysQuery){
14329                 this.lastQuery = q;
14330                 if(this.mode == 'local'){
14331                     this.selectedIndex = -1;
14332                     if(forceAll){
14333                         this.store.clearFilter();
14334                     }else{
14335                         
14336                         if(this.specialFilter){
14337                             this.fireEvent('specialfilter', this);
14338                             this.onLoad();
14339                             return;
14340                         }
14341                         
14342                         this.store.filter(this.displayField, q);
14343                     }
14344                     
14345                     this.store.fireEvent("datachanged", this.store);
14346                     
14347                     this.onLoad();
14348                     
14349                     
14350                 }else{
14351                     
14352                     this.store.baseParams[this.queryParam] = q;
14353                     
14354                     var options = {params : this.getParams(q)};
14355                     
14356                     if(this.loadNext){
14357                         options.add = true;
14358                         options.params.start = this.page * this.pageSize;
14359                     }
14360                     
14361                     this.store.load(options);
14362                     
14363                     /*
14364                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14365                      *  we should expand the list on onLoad
14366                      *  so command out it
14367                      */
14368 //                    this.expand();
14369                 }
14370             }else{
14371                 this.selectedIndex = -1;
14372                 this.onLoad();   
14373             }
14374         }
14375         
14376         this.loadNext = false;
14377     },
14378     
14379     // private
14380     getParams : function(q){
14381         var p = {};
14382         //p[this.queryParam] = q;
14383         
14384         if(this.pageSize){
14385             p.start = 0;
14386             p.limit = this.pageSize;
14387         }
14388         return p;
14389     },
14390
14391     /**
14392      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14393      */
14394     collapse : function(){
14395         if(!this.isExpanded()){
14396             return;
14397         }
14398         
14399         this.list.hide();
14400         
14401         this.hasFocus = false;
14402         
14403         if(this.tickable){
14404             this.okBtn.hide();
14405             this.cancelBtn.hide();
14406             this.trigger.show();
14407             
14408             if(this.editable){
14409                 this.tickableInputEl().dom.value = '';
14410                 this.tickableInputEl().blur();
14411             }
14412             
14413         }
14414         
14415         Roo.get(document).un('mousedown', this.collapseIf, this);
14416         Roo.get(document).un('mousewheel', this.collapseIf, this);
14417         if (!this.editable) {
14418             Roo.get(document).un('keydown', this.listKeyPress, this);
14419         }
14420         this.fireEvent('collapse', this);
14421         
14422         this.validate();
14423     },
14424
14425     // private
14426     collapseIf : function(e){
14427         var in_combo  = e.within(this.el);
14428         var in_list =  e.within(this.list);
14429         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14430         
14431         if (in_combo || in_list || is_list) {
14432             //e.stopPropagation();
14433             return;
14434         }
14435         
14436         if(this.tickable){
14437             this.onTickableFooterButtonClick(e, false, false);
14438         }
14439
14440         this.collapse();
14441         
14442     },
14443
14444     /**
14445      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14446      */
14447     expand : function(){
14448        
14449         if(this.isExpanded() || !this.hasFocus){
14450             return;
14451         }
14452         
14453         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14454         this.list.setWidth(lw);
14455         
14456         Roo.log('expand');
14457         
14458         this.list.show();
14459         
14460         this.restrictHeight();
14461         
14462         if(this.tickable){
14463             
14464             this.tickItems = Roo.apply([], this.item);
14465             
14466             this.okBtn.show();
14467             this.cancelBtn.show();
14468             this.trigger.hide();
14469             
14470             if(this.editable){
14471                 this.tickableInputEl().focus();
14472             }
14473             
14474         }
14475         
14476         Roo.get(document).on('mousedown', this.collapseIf, this);
14477         Roo.get(document).on('mousewheel', this.collapseIf, this);
14478         if (!this.editable) {
14479             Roo.get(document).on('keydown', this.listKeyPress, this);
14480         }
14481         
14482         this.fireEvent('expand', this);
14483     },
14484
14485     // private
14486     // Implements the default empty TriggerField.onTriggerClick function
14487     onTriggerClick : function(e)
14488     {
14489         Roo.log('trigger click');
14490         
14491         if(this.disabled || !this.triggerList){
14492             return;
14493         }
14494         
14495         this.page = 0;
14496         this.loadNext = false;
14497         
14498         if(this.isExpanded()){
14499             this.collapse();
14500             if (!this.blockFocus) {
14501                 this.inputEl().focus();
14502             }
14503             
14504         }else {
14505             this.hasFocus = true;
14506             if(this.triggerAction == 'all') {
14507                 this.doQuery(this.allQuery, true);
14508             } else {
14509                 this.doQuery(this.getRawValue());
14510             }
14511             if (!this.blockFocus) {
14512                 this.inputEl().focus();
14513             }
14514         }
14515     },
14516     
14517     onTickableTriggerClick : function(e)
14518     {
14519         if(this.disabled){
14520             return;
14521         }
14522         
14523         this.page = 0;
14524         this.loadNext = false;
14525         this.hasFocus = true;
14526         
14527         if(this.triggerAction == 'all') {
14528             this.doQuery(this.allQuery, true);
14529         } else {
14530             this.doQuery(this.getRawValue());
14531         }
14532     },
14533     
14534     onSearchFieldClick : function(e)
14535     {
14536         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14537             this.onTickableFooterButtonClick(e, false, false);
14538             return;
14539         }
14540         
14541         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14542             return;
14543         }
14544         
14545         this.page = 0;
14546         this.loadNext = false;
14547         this.hasFocus = true;
14548         
14549         if(this.triggerAction == 'all') {
14550             this.doQuery(this.allQuery, true);
14551         } else {
14552             this.doQuery(this.getRawValue());
14553         }
14554     },
14555     
14556     listKeyPress : function(e)
14557     {
14558         //Roo.log('listkeypress');
14559         // scroll to first matching element based on key pres..
14560         if (e.isSpecialKey()) {
14561             return false;
14562         }
14563         var k = String.fromCharCode(e.getKey()).toUpperCase();
14564         //Roo.log(k);
14565         var match  = false;
14566         var csel = this.view.getSelectedNodes();
14567         var cselitem = false;
14568         if (csel.length) {
14569             var ix = this.view.indexOf(csel[0]);
14570             cselitem  = this.store.getAt(ix);
14571             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14572                 cselitem = false;
14573             }
14574             
14575         }
14576         
14577         this.store.each(function(v) { 
14578             if (cselitem) {
14579                 // start at existing selection.
14580                 if (cselitem.id == v.id) {
14581                     cselitem = false;
14582                 }
14583                 return true;
14584             }
14585                 
14586             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14587                 match = this.store.indexOf(v);
14588                 return false;
14589             }
14590             return true;
14591         }, this);
14592         
14593         if (match === false) {
14594             return true; // no more action?
14595         }
14596         // scroll to?
14597         this.view.select(match);
14598         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14599         sn.scrollIntoView(sn.dom.parentNode, false);
14600     },
14601     
14602     onViewScroll : function(e, t){
14603         
14604         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){
14605             return;
14606         }
14607         
14608         this.hasQuery = true;
14609         
14610         this.loading = this.list.select('.loading', true).first();
14611         
14612         if(this.loading === null){
14613             this.list.createChild({
14614                 tag: 'div',
14615                 cls: 'loading roo-select2-more-results roo-select2-active',
14616                 html: 'Loading more results...'
14617             });
14618             
14619             this.loading = this.list.select('.loading', true).first();
14620             
14621             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14622             
14623             this.loading.hide();
14624         }
14625         
14626         this.loading.show();
14627         
14628         var _combo = this;
14629         
14630         this.page++;
14631         this.loadNext = true;
14632         
14633         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14634         
14635         return;
14636     },
14637     
14638     addItem : function(o)
14639     {   
14640         var dv = ''; // display value
14641         
14642         if (this.displayField) {
14643             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14644         } else {
14645             // this is an error condition!!!
14646             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14647         }
14648         
14649         if(!dv.length){
14650             return;
14651         }
14652         
14653         var choice = this.choices.createChild({
14654             tag: 'li',
14655             cls: 'roo-select2-search-choice',
14656             cn: [
14657                 {
14658                     tag: 'div',
14659                     html: dv
14660                 },
14661                 {
14662                     tag: 'a',
14663                     href: '#',
14664                     cls: 'roo-select2-search-choice-close fa fa-times',
14665                     tabindex: '-1'
14666                 }
14667             ]
14668             
14669         }, this.searchField);
14670         
14671         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14672         
14673         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14674         
14675         this.item.push(o);
14676         
14677         this.lastData = o;
14678         
14679         this.syncValue();
14680         
14681         this.inputEl().dom.value = '';
14682         
14683         this.validate();
14684     },
14685     
14686     onRemoveItem : function(e, _self, o)
14687     {
14688         e.preventDefault();
14689         
14690         this.lastItem = Roo.apply([], this.item);
14691         
14692         var index = this.item.indexOf(o.data) * 1;
14693         
14694         if( index < 0){
14695             Roo.log('not this item?!');
14696             return;
14697         }
14698         
14699         this.item.splice(index, 1);
14700         o.item.remove();
14701         
14702         this.syncValue();
14703         
14704         this.fireEvent('remove', this, e);
14705         
14706         this.validate();
14707         
14708     },
14709     
14710     syncValue : function()
14711     {
14712         if(!this.item.length){
14713             this.clearValue();
14714             return;
14715         }
14716             
14717         var value = [];
14718         var _this = this;
14719         Roo.each(this.item, function(i){
14720             if(_this.valueField){
14721                 value.push(i[_this.valueField]);
14722                 return;
14723             }
14724
14725             value.push(i);
14726         });
14727
14728         this.value = value.join(',');
14729
14730         if(this.hiddenField){
14731             this.hiddenField.dom.value = this.value;
14732         }
14733         
14734         this.store.fireEvent("datachanged", this.store);
14735         
14736         this.validate();
14737     },
14738     
14739     clearItem : function()
14740     {
14741         if(!this.multiple){
14742             return;
14743         }
14744         
14745         this.item = [];
14746         
14747         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14748            c.remove();
14749         });
14750         
14751         this.syncValue();
14752         
14753         this.validate();
14754         
14755         if(this.tickable && !Roo.isTouch){
14756             this.view.refresh();
14757         }
14758     },
14759     
14760     inputEl: function ()
14761     {
14762         if(Roo.isIOS && this.useNativeIOS){
14763             return this.el.select('select.roo-ios-select', true).first();
14764         }
14765         
14766         if(Roo.isTouch && this.mobileTouchView){
14767             return this.el.select('input.form-control',true).first();
14768         }
14769         
14770         if(this.tickable){
14771             return this.searchField;
14772         }
14773         
14774         return this.el.select('input.form-control',true).first();
14775     },
14776     
14777     onTickableFooterButtonClick : function(e, btn, el)
14778     {
14779         e.preventDefault();
14780         
14781         this.lastItem = Roo.apply([], this.item);
14782         
14783         if(btn && btn.name == 'cancel'){
14784             this.tickItems = Roo.apply([], this.item);
14785             this.collapse();
14786             return;
14787         }
14788         
14789         this.clearItem();
14790         
14791         var _this = this;
14792         
14793         Roo.each(this.tickItems, function(o){
14794             _this.addItem(o);
14795         });
14796         
14797         this.collapse();
14798         
14799     },
14800     
14801     validate : function()
14802     {
14803         if(this.getVisibilityEl().hasClass('hidden')){
14804             return true;
14805         }
14806         
14807         var v = this.getRawValue();
14808         
14809         if(this.multiple){
14810             v = this.getValue();
14811         }
14812         
14813         if(this.disabled || this.allowBlank || v.length){
14814             this.markValid();
14815             return true;
14816         }
14817         
14818         this.markInvalid();
14819         return false;
14820     },
14821     
14822     tickableInputEl : function()
14823     {
14824         if(!this.tickable || !this.editable){
14825             return this.inputEl();
14826         }
14827         
14828         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14829     },
14830     
14831     
14832     getAutoCreateTouchView : function()
14833     {
14834         var id = Roo.id();
14835         
14836         var cfg = {
14837             cls: 'form-group' //input-group
14838         };
14839         
14840         var input =  {
14841             tag: 'input',
14842             id : id,
14843             type : this.inputType,
14844             cls : 'form-control x-combo-noedit',
14845             autocomplete: 'new-password',
14846             placeholder : this.placeholder || '',
14847             readonly : true
14848         };
14849         
14850         if (this.name) {
14851             input.name = this.name;
14852         }
14853         
14854         if (this.size) {
14855             input.cls += ' input-' + this.size;
14856         }
14857         
14858         if (this.disabled) {
14859             input.disabled = true;
14860         }
14861         
14862         var inputblock = {
14863             cls : '',
14864             cn : [
14865                 input
14866             ]
14867         };
14868         
14869         if(this.before){
14870             inputblock.cls += ' input-group';
14871             
14872             inputblock.cn.unshift({
14873                 tag :'span',
14874                 cls : 'input-group-addon',
14875                 html : this.before
14876             });
14877         }
14878         
14879         if(this.removable && !this.multiple){
14880             inputblock.cls += ' roo-removable';
14881             
14882             inputblock.cn.push({
14883                 tag: 'button',
14884                 html : 'x',
14885                 cls : 'roo-combo-removable-btn close'
14886             });
14887         }
14888
14889         if(this.hasFeedback && !this.allowBlank){
14890             
14891             inputblock.cls += ' has-feedback';
14892             
14893             inputblock.cn.push({
14894                 tag: 'span',
14895                 cls: 'glyphicon form-control-feedback'
14896             });
14897             
14898         }
14899         
14900         if (this.after) {
14901             
14902             inputblock.cls += (this.before) ? '' : ' input-group';
14903             
14904             inputblock.cn.push({
14905                 tag :'span',
14906                 cls : 'input-group-addon',
14907                 html : this.after
14908             });
14909         }
14910
14911         var box = {
14912             tag: 'div',
14913             cn: [
14914                 {
14915                     tag: 'input',
14916                     type : 'hidden',
14917                     cls: 'form-hidden-field'
14918                 },
14919                 inputblock
14920             ]
14921             
14922         };
14923         
14924         if(this.multiple){
14925             box = {
14926                 tag: 'div',
14927                 cn: [
14928                     {
14929                         tag: 'input',
14930                         type : 'hidden',
14931                         cls: 'form-hidden-field'
14932                     },
14933                     {
14934                         tag: 'ul',
14935                         cls: 'roo-select2-choices',
14936                         cn:[
14937                             {
14938                                 tag: 'li',
14939                                 cls: 'roo-select2-search-field',
14940                                 cn: [
14941
14942                                     inputblock
14943                                 ]
14944                             }
14945                         ]
14946                     }
14947                 ]
14948             }
14949         };
14950         
14951         var combobox = {
14952             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14953             cn: [
14954                 box
14955             ]
14956         };
14957         
14958         if(!this.multiple && this.showToggleBtn){
14959             
14960             var caret = {
14961                         tag: 'span',
14962                         cls: 'caret'
14963             };
14964             
14965             if (this.caret != false) {
14966                 caret = {
14967                      tag: 'i',
14968                      cls: 'fa fa-' + this.caret
14969                 };
14970                 
14971             }
14972             
14973             combobox.cn.push({
14974                 tag :'span',
14975                 cls : 'input-group-addon btn dropdown-toggle',
14976                 cn : [
14977                     caret,
14978                     {
14979                         tag: 'span',
14980                         cls: 'combobox-clear',
14981                         cn  : [
14982                             {
14983                                 tag : 'i',
14984                                 cls: 'icon-remove'
14985                             }
14986                         ]
14987                     }
14988                 ]
14989
14990             })
14991         }
14992         
14993         if(this.multiple){
14994             combobox.cls += ' roo-select2-container-multi';
14995         }
14996         
14997         var align = this.labelAlign || this.parentLabelAlign();
14998         
14999         if (align ==='left' && this.fieldLabel.length) {
15000
15001             cfg.cn = [
15002                 {
15003                    tag : 'i',
15004                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15005                    tooltip : 'This field is required'
15006                 },
15007                 {
15008                     tag: 'label',
15009                     cls : 'control-label',
15010                     html : this.fieldLabel
15011
15012                 },
15013                 {
15014                     cls : '', 
15015                     cn: [
15016                         combobox
15017                     ]
15018                 }
15019             ];
15020             
15021             var labelCfg = cfg.cn[1];
15022             var contentCfg = cfg.cn[2];
15023             
15024
15025             if(this.indicatorpos == 'right'){
15026                 cfg.cn = [
15027                     {
15028                         tag: 'label',
15029                         'for' :  id,
15030                         cls : 'control-label',
15031                         cn : [
15032                             {
15033                                 tag : 'span',
15034                                 html : this.fieldLabel
15035                             },
15036                             {
15037                                 tag : 'i',
15038                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15039                                 tooltip : 'This field is required'
15040                             }
15041                         ]
15042                     },
15043                     {
15044                         cls : "",
15045                         cn: [
15046                             combobox
15047                         ]
15048                     }
15049
15050                 ];
15051                 
15052                 labelCfg = cfg.cn[0];
15053                 contentCfg = cfg.cn[1];
15054             }
15055             
15056            
15057             
15058             if(this.labelWidth > 12){
15059                 labelCfg.style = "width: " + this.labelWidth + 'px';
15060             }
15061             
15062             if(this.labelWidth < 13 && this.labelmd == 0){
15063                 this.labelmd = this.labelWidth;
15064             }
15065             
15066             if(this.labellg > 0){
15067                 labelCfg.cls += ' col-lg-' + this.labellg;
15068                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15069             }
15070             
15071             if(this.labelmd > 0){
15072                 labelCfg.cls += ' col-md-' + this.labelmd;
15073                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15074             }
15075             
15076             if(this.labelsm > 0){
15077                 labelCfg.cls += ' col-sm-' + this.labelsm;
15078                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15079             }
15080             
15081             if(this.labelxs > 0){
15082                 labelCfg.cls += ' col-xs-' + this.labelxs;
15083                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15084             }
15085                 
15086                 
15087         } else if ( this.fieldLabel.length) {
15088             cfg.cn = [
15089                 {
15090                    tag : 'i',
15091                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15092                    tooltip : 'This field is required'
15093                 },
15094                 {
15095                     tag: 'label',
15096                     cls : 'control-label',
15097                     html : this.fieldLabel
15098
15099                 },
15100                 {
15101                     cls : '', 
15102                     cn: [
15103                         combobox
15104                     ]
15105                 }
15106             ];
15107             
15108             if(this.indicatorpos == 'right'){
15109                 cfg.cn = [
15110                     {
15111                         tag: 'label',
15112                         cls : 'control-label',
15113                         html : this.fieldLabel,
15114                         cn : [
15115                             {
15116                                tag : 'i',
15117                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15118                                tooltip : 'This field is required'
15119                             }
15120                         ]
15121                     },
15122                     {
15123                         cls : '', 
15124                         cn: [
15125                             combobox
15126                         ]
15127                     }
15128                 ];
15129             }
15130         } else {
15131             cfg.cn = combobox;    
15132         }
15133         
15134         
15135         var settings = this;
15136         
15137         ['xs','sm','md','lg'].map(function(size){
15138             if (settings[size]) {
15139                 cfg.cls += ' col-' + size + '-' + settings[size];
15140             }
15141         });
15142         
15143         return cfg;
15144     },
15145     
15146     initTouchView : function()
15147     {
15148         this.renderTouchView();
15149         
15150         this.touchViewEl.on('scroll', function(){
15151             this.el.dom.scrollTop = 0;
15152         }, this);
15153         
15154         this.originalValue = this.getValue();
15155         
15156         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15157         
15158         this.inputEl().on("click", this.showTouchView, this);
15159         if (this.triggerEl) {
15160             this.triggerEl.on("click", this.showTouchView, this);
15161         }
15162         
15163         
15164         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15165         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15166         
15167         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15168         
15169         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15170         this.store.on('load', this.onTouchViewLoad, this);
15171         this.store.on('loadexception', this.onTouchViewLoadException, this);
15172         
15173         if(this.hiddenName){
15174             
15175             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15176             
15177             this.hiddenField.dom.value =
15178                 this.hiddenValue !== undefined ? this.hiddenValue :
15179                 this.value !== undefined ? this.value : '';
15180         
15181             this.el.dom.removeAttribute('name');
15182             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15183         }
15184         
15185         if(this.multiple){
15186             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15187             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15188         }
15189         
15190         if(this.removable && !this.multiple){
15191             var close = this.closeTriggerEl();
15192             if(close){
15193                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15194                 close.on('click', this.removeBtnClick, this, close);
15195             }
15196         }
15197         /*
15198          * fix the bug in Safari iOS8
15199          */
15200         this.inputEl().on("focus", function(e){
15201             document.activeElement.blur();
15202         }, this);
15203         
15204         return;
15205         
15206         
15207     },
15208     
15209     renderTouchView : function()
15210     {
15211         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15212         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15213         
15214         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15215         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15216         
15217         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15218         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15219         this.touchViewBodyEl.setStyle('overflow', 'auto');
15220         
15221         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15222         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15223         
15224         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15225         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15226         
15227     },
15228     
15229     showTouchView : function()
15230     {
15231         if(this.disabled){
15232             return;
15233         }
15234         
15235         this.touchViewHeaderEl.hide();
15236
15237         if(this.modalTitle.length){
15238             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15239             this.touchViewHeaderEl.show();
15240         }
15241
15242         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15243         this.touchViewEl.show();
15244
15245         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15246         
15247         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15248         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15249
15250         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15251
15252         if(this.modalTitle.length){
15253             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15254         }
15255         
15256         this.touchViewBodyEl.setHeight(bodyHeight);
15257
15258         if(this.animate){
15259             var _this = this;
15260             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15261         }else{
15262             this.touchViewEl.addClass('in');
15263         }
15264
15265         this.doTouchViewQuery();
15266         
15267     },
15268     
15269     hideTouchView : function()
15270     {
15271         this.touchViewEl.removeClass('in');
15272
15273         if(this.animate){
15274             var _this = this;
15275             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15276         }else{
15277             this.touchViewEl.setStyle('display', 'none');
15278         }
15279         
15280     },
15281     
15282     setTouchViewValue : function()
15283     {
15284         if(this.multiple){
15285             this.clearItem();
15286         
15287             var _this = this;
15288
15289             Roo.each(this.tickItems, function(o){
15290                 this.addItem(o);
15291             }, this);
15292         }
15293         
15294         this.hideTouchView();
15295     },
15296     
15297     doTouchViewQuery : function()
15298     {
15299         var qe = {
15300             query: '',
15301             forceAll: true,
15302             combo: this,
15303             cancel:false
15304         };
15305         
15306         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15307             return false;
15308         }
15309         
15310         if(!this.alwaysQuery || this.mode == 'local'){
15311             this.onTouchViewLoad();
15312             return;
15313         }
15314         
15315         this.store.load();
15316     },
15317     
15318     onTouchViewBeforeLoad : function(combo,opts)
15319     {
15320         return;
15321     },
15322
15323     // private
15324     onTouchViewLoad : function()
15325     {
15326         if(this.store.getCount() < 1){
15327             this.onTouchViewEmptyResults();
15328             return;
15329         }
15330         
15331         this.clearTouchView();
15332         
15333         var rawValue = this.getRawValue();
15334         
15335         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15336         
15337         this.tickItems = [];
15338         
15339         this.store.data.each(function(d, rowIndex){
15340             var row = this.touchViewListGroup.createChild(template);
15341             
15342             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15343                 row.addClass(d.data.cls);
15344             }
15345             
15346             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15347                 var cfg = {
15348                     data : d.data,
15349                     html : d.data[this.displayField]
15350                 };
15351                 
15352                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15353                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15354                 }
15355             }
15356             row.removeClass('selected');
15357             if(!this.multiple && this.valueField &&
15358                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15359             {
15360                 // radio buttons..
15361                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15362                 row.addClass('selected');
15363             }
15364             
15365             if(this.multiple && this.valueField &&
15366                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15367             {
15368                 
15369                 // checkboxes...
15370                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15371                 this.tickItems.push(d.data);
15372             }
15373             
15374             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15375             
15376         }, this);
15377         
15378         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15379         
15380         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15381
15382         if(this.modalTitle.length){
15383             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15384         }
15385
15386         var listHeight = this.touchViewListGroup.getHeight();
15387         
15388         var _this = this;
15389         
15390         if(firstChecked && listHeight > bodyHeight){
15391             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15392         }
15393         
15394     },
15395     
15396     onTouchViewLoadException : function()
15397     {
15398         this.hideTouchView();
15399     },
15400     
15401     onTouchViewEmptyResults : function()
15402     {
15403         this.clearTouchView();
15404         
15405         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15406         
15407         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15408         
15409     },
15410     
15411     clearTouchView : function()
15412     {
15413         this.touchViewListGroup.dom.innerHTML = '';
15414     },
15415     
15416     onTouchViewClick : function(e, el, o)
15417     {
15418         e.preventDefault();
15419         
15420         var row = o.row;
15421         var rowIndex = o.rowIndex;
15422         
15423         var r = this.store.getAt(rowIndex);
15424         
15425         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15426             
15427             if(!this.multiple){
15428                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15429                     c.dom.removeAttribute('checked');
15430                 }, this);
15431
15432                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15433
15434                 this.setFromData(r.data);
15435
15436                 var close = this.closeTriggerEl();
15437
15438                 if(close){
15439                     close.show();
15440                 }
15441
15442                 this.hideTouchView();
15443
15444                 this.fireEvent('select', this, r, rowIndex);
15445
15446                 return;
15447             }
15448
15449             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15450                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15451                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15452                 return;
15453             }
15454
15455             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15456             this.addItem(r.data);
15457             this.tickItems.push(r.data);
15458         }
15459     },
15460     
15461     getAutoCreateNativeIOS : function()
15462     {
15463         var cfg = {
15464             cls: 'form-group' //input-group,
15465         };
15466         
15467         var combobox =  {
15468             tag: 'select',
15469             cls : 'roo-ios-select'
15470         };
15471         
15472         if (this.name) {
15473             combobox.name = this.name;
15474         }
15475         
15476         if (this.disabled) {
15477             combobox.disabled = true;
15478         }
15479         
15480         var settings = this;
15481         
15482         ['xs','sm','md','lg'].map(function(size){
15483             if (settings[size]) {
15484                 cfg.cls += ' col-' + size + '-' + settings[size];
15485             }
15486         });
15487         
15488         cfg.cn = combobox;
15489         
15490         return cfg;
15491         
15492     },
15493     
15494     initIOSView : function()
15495     {
15496         this.store.on('load', this.onIOSViewLoad, this);
15497         
15498         return;
15499     },
15500     
15501     onIOSViewLoad : function()
15502     {
15503         if(this.store.getCount() < 1){
15504             return;
15505         }
15506         
15507         this.clearIOSView();
15508         
15509         if(this.allowBlank) {
15510             
15511             var default_text = '-- SELECT --';
15512             
15513             if(this.placeholder.length){
15514                 default_text = this.placeholder;
15515             }
15516             
15517             if(this.emptyTitle.length){
15518                 default_text += ' - ' + this.emptyTitle + ' -';
15519             }
15520             
15521             var opt = this.inputEl().createChild({
15522                 tag: 'option',
15523                 value : 0,
15524                 html : default_text
15525             });
15526             
15527             var o = {};
15528             o[this.valueField] = 0;
15529             o[this.displayField] = default_text;
15530             
15531             this.ios_options.push({
15532                 data : o,
15533                 el : opt
15534             });
15535             
15536         }
15537         
15538         this.store.data.each(function(d, rowIndex){
15539             
15540             var html = '';
15541             
15542             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15543                 html = d.data[this.displayField];
15544             }
15545             
15546             var value = '';
15547             
15548             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15549                 value = d.data[this.valueField];
15550             }
15551             
15552             var option = {
15553                 tag: 'option',
15554                 value : value,
15555                 html : html
15556             };
15557             
15558             if(this.value == d.data[this.valueField]){
15559                 option['selected'] = true;
15560             }
15561             
15562             var opt = this.inputEl().createChild(option);
15563             
15564             this.ios_options.push({
15565                 data : d.data,
15566                 el : opt
15567             });
15568             
15569         }, this);
15570         
15571         this.inputEl().on('change', function(){
15572            this.fireEvent('select', this);
15573         }, this);
15574         
15575     },
15576     
15577     clearIOSView: function()
15578     {
15579         this.inputEl().dom.innerHTML = '';
15580         
15581         this.ios_options = [];
15582     },
15583     
15584     setIOSValue: function(v)
15585     {
15586         this.value = v;
15587         
15588         if(!this.ios_options){
15589             return;
15590         }
15591         
15592         Roo.each(this.ios_options, function(opts){
15593            
15594            opts.el.dom.removeAttribute('selected');
15595            
15596            if(opts.data[this.valueField] != v){
15597                return;
15598            }
15599            
15600            opts.el.dom.setAttribute('selected', true);
15601            
15602         }, this);
15603     }
15604
15605     /** 
15606     * @cfg {Boolean} grow 
15607     * @hide 
15608     */
15609     /** 
15610     * @cfg {Number} growMin 
15611     * @hide 
15612     */
15613     /** 
15614     * @cfg {Number} growMax 
15615     * @hide 
15616     */
15617     /**
15618      * @hide
15619      * @method autoSize
15620      */
15621 });
15622
15623 Roo.apply(Roo.bootstrap.ComboBox,  {
15624     
15625     header : {
15626         tag: 'div',
15627         cls: 'modal-header',
15628         cn: [
15629             {
15630                 tag: 'h4',
15631                 cls: 'modal-title'
15632             }
15633         ]
15634     },
15635     
15636     body : {
15637         tag: 'div',
15638         cls: 'modal-body',
15639         cn: [
15640             {
15641                 tag: 'ul',
15642                 cls: 'list-group'
15643             }
15644         ]
15645     },
15646     
15647     listItemRadio : {
15648         tag: 'li',
15649         cls: 'list-group-item',
15650         cn: [
15651             {
15652                 tag: 'span',
15653                 cls: 'roo-combobox-list-group-item-value'
15654             },
15655             {
15656                 tag: 'div',
15657                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15658                 cn: [
15659                     {
15660                         tag: 'input',
15661                         type: 'radio'
15662                     },
15663                     {
15664                         tag: 'label'
15665                     }
15666                 ]
15667             }
15668         ]
15669     },
15670     
15671     listItemCheckbox : {
15672         tag: 'li',
15673         cls: 'list-group-item',
15674         cn: [
15675             {
15676                 tag: 'span',
15677                 cls: 'roo-combobox-list-group-item-value'
15678             },
15679             {
15680                 tag: 'div',
15681                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15682                 cn: [
15683                     {
15684                         tag: 'input',
15685                         type: 'checkbox'
15686                     },
15687                     {
15688                         tag: 'label'
15689                     }
15690                 ]
15691             }
15692         ]
15693     },
15694     
15695     emptyResult : {
15696         tag: 'div',
15697         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15698     },
15699     
15700     footer : {
15701         tag: 'div',
15702         cls: 'modal-footer',
15703         cn: [
15704             {
15705                 tag: 'div',
15706                 cls: 'row',
15707                 cn: [
15708                     {
15709                         tag: 'div',
15710                         cls: 'col-xs-6 text-left',
15711                         cn: {
15712                             tag: 'button',
15713                             cls: 'btn btn-danger roo-touch-view-cancel',
15714                             html: 'Cancel'
15715                         }
15716                     },
15717                     {
15718                         tag: 'div',
15719                         cls: 'col-xs-6 text-right',
15720                         cn: {
15721                             tag: 'button',
15722                             cls: 'btn btn-success roo-touch-view-ok',
15723                             html: 'OK'
15724                         }
15725                     }
15726                 ]
15727             }
15728         ]
15729         
15730     }
15731 });
15732
15733 Roo.apply(Roo.bootstrap.ComboBox,  {
15734     
15735     touchViewTemplate : {
15736         tag: 'div',
15737         cls: 'modal fade roo-combobox-touch-view',
15738         cn: [
15739             {
15740                 tag: 'div',
15741                 cls: 'modal-dialog',
15742                 style : 'position:fixed', // we have to fix position....
15743                 cn: [
15744                     {
15745                         tag: 'div',
15746                         cls: 'modal-content',
15747                         cn: [
15748                             Roo.bootstrap.ComboBox.header,
15749                             Roo.bootstrap.ComboBox.body,
15750                             Roo.bootstrap.ComboBox.footer
15751                         ]
15752                     }
15753                 ]
15754             }
15755         ]
15756     }
15757 });/*
15758  * Based on:
15759  * Ext JS Library 1.1.1
15760  * Copyright(c) 2006-2007, Ext JS, LLC.
15761  *
15762  * Originally Released Under LGPL - original licence link has changed is not relivant.
15763  *
15764  * Fork - LGPL
15765  * <script type="text/javascript">
15766  */
15767
15768 /**
15769  * @class Roo.View
15770  * @extends Roo.util.Observable
15771  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15772  * This class also supports single and multi selection modes. <br>
15773  * Create a data model bound view:
15774  <pre><code>
15775  var store = new Roo.data.Store(...);
15776
15777  var view = new Roo.View({
15778     el : "my-element",
15779     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15780  
15781     singleSelect: true,
15782     selectedClass: "ydataview-selected",
15783     store: store
15784  });
15785
15786  // listen for node click?
15787  view.on("click", function(vw, index, node, e){
15788  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15789  });
15790
15791  // load XML data
15792  dataModel.load("foobar.xml");
15793  </code></pre>
15794  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15795  * <br><br>
15796  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15797  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15798  * 
15799  * Note: old style constructor is still suported (container, template, config)
15800  * 
15801  * @constructor
15802  * Create a new View
15803  * @param {Object} config The config object
15804  * 
15805  */
15806 Roo.View = function(config, depreciated_tpl, depreciated_config){
15807     
15808     this.parent = false;
15809     
15810     if (typeof(depreciated_tpl) == 'undefined') {
15811         // new way.. - universal constructor.
15812         Roo.apply(this, config);
15813         this.el  = Roo.get(this.el);
15814     } else {
15815         // old format..
15816         this.el  = Roo.get(config);
15817         this.tpl = depreciated_tpl;
15818         Roo.apply(this, depreciated_config);
15819     }
15820     this.wrapEl  = this.el.wrap().wrap();
15821     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15822     
15823     
15824     if(typeof(this.tpl) == "string"){
15825         this.tpl = new Roo.Template(this.tpl);
15826     } else {
15827         // support xtype ctors..
15828         this.tpl = new Roo.factory(this.tpl, Roo);
15829     }
15830     
15831     
15832     this.tpl.compile();
15833     
15834     /** @private */
15835     this.addEvents({
15836         /**
15837          * @event beforeclick
15838          * Fires before a click is processed. Returns false to cancel the default action.
15839          * @param {Roo.View} this
15840          * @param {Number} index The index of the target node
15841          * @param {HTMLElement} node The target node
15842          * @param {Roo.EventObject} e The raw event object
15843          */
15844             "beforeclick" : true,
15845         /**
15846          * @event click
15847          * Fires when a template node is clicked.
15848          * @param {Roo.View} this
15849          * @param {Number} index The index of the target node
15850          * @param {HTMLElement} node The target node
15851          * @param {Roo.EventObject} e The raw event object
15852          */
15853             "click" : true,
15854         /**
15855          * @event dblclick
15856          * Fires when a template node is double clicked.
15857          * @param {Roo.View} this
15858          * @param {Number} index The index of the target node
15859          * @param {HTMLElement} node The target node
15860          * @param {Roo.EventObject} e The raw event object
15861          */
15862             "dblclick" : true,
15863         /**
15864          * @event contextmenu
15865          * Fires when a template node is right clicked.
15866          * @param {Roo.View} this
15867          * @param {Number} index The index of the target node
15868          * @param {HTMLElement} node The target node
15869          * @param {Roo.EventObject} e The raw event object
15870          */
15871             "contextmenu" : true,
15872         /**
15873          * @event selectionchange
15874          * Fires when the selected nodes change.
15875          * @param {Roo.View} this
15876          * @param {Array} selections Array of the selected nodes
15877          */
15878             "selectionchange" : true,
15879     
15880         /**
15881          * @event beforeselect
15882          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15883          * @param {Roo.View} this
15884          * @param {HTMLElement} node The node to be selected
15885          * @param {Array} selections Array of currently selected nodes
15886          */
15887             "beforeselect" : true,
15888         /**
15889          * @event preparedata
15890          * Fires on every row to render, to allow you to change the data.
15891          * @param {Roo.View} this
15892          * @param {Object} data to be rendered (change this)
15893          */
15894           "preparedata" : true
15895           
15896           
15897         });
15898
15899
15900
15901     this.el.on({
15902         "click": this.onClick,
15903         "dblclick": this.onDblClick,
15904         "contextmenu": this.onContextMenu,
15905         scope:this
15906     });
15907
15908     this.selections = [];
15909     this.nodes = [];
15910     this.cmp = new Roo.CompositeElementLite([]);
15911     if(this.store){
15912         this.store = Roo.factory(this.store, Roo.data);
15913         this.setStore(this.store, true);
15914     }
15915     
15916     if ( this.footer && this.footer.xtype) {
15917            
15918          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15919         
15920         this.footer.dataSource = this.store;
15921         this.footer.container = fctr;
15922         this.footer = Roo.factory(this.footer, Roo);
15923         fctr.insertFirst(this.el);
15924         
15925         // this is a bit insane - as the paging toolbar seems to detach the el..
15926 //        dom.parentNode.parentNode.parentNode
15927          // they get detached?
15928     }
15929     
15930     
15931     Roo.View.superclass.constructor.call(this);
15932     
15933     
15934 };
15935
15936 Roo.extend(Roo.View, Roo.util.Observable, {
15937     
15938      /**
15939      * @cfg {Roo.data.Store} store Data store to load data from.
15940      */
15941     store : false,
15942     
15943     /**
15944      * @cfg {String|Roo.Element} el The container element.
15945      */
15946     el : '',
15947     
15948     /**
15949      * @cfg {String|Roo.Template} tpl The template used by this View 
15950      */
15951     tpl : false,
15952     /**
15953      * @cfg {String} dataName the named area of the template to use as the data area
15954      *                          Works with domtemplates roo-name="name"
15955      */
15956     dataName: false,
15957     /**
15958      * @cfg {String} selectedClass The css class to add to selected nodes
15959      */
15960     selectedClass : "x-view-selected",
15961      /**
15962      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15963      */
15964     emptyText : "",
15965     
15966     /**
15967      * @cfg {String} text to display on mask (default Loading)
15968      */
15969     mask : false,
15970     /**
15971      * @cfg {Boolean} multiSelect Allow multiple selection
15972      */
15973     multiSelect : false,
15974     /**
15975      * @cfg {Boolean} singleSelect Allow single selection
15976      */
15977     singleSelect:  false,
15978     
15979     /**
15980      * @cfg {Boolean} toggleSelect - selecting 
15981      */
15982     toggleSelect : false,
15983     
15984     /**
15985      * @cfg {Boolean} tickable - selecting 
15986      */
15987     tickable : false,
15988     
15989     /**
15990      * Returns the element this view is bound to.
15991      * @return {Roo.Element}
15992      */
15993     getEl : function(){
15994         return this.wrapEl;
15995     },
15996     
15997     
15998
15999     /**
16000      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16001      */
16002     refresh : function(){
16003         //Roo.log('refresh');
16004         var t = this.tpl;
16005         
16006         // if we are using something like 'domtemplate', then
16007         // the what gets used is:
16008         // t.applySubtemplate(NAME, data, wrapping data..)
16009         // the outer template then get' applied with
16010         //     the store 'extra data'
16011         // and the body get's added to the
16012         //      roo-name="data" node?
16013         //      <span class='roo-tpl-{name}'></span> ?????
16014         
16015         
16016         
16017         this.clearSelections();
16018         this.el.update("");
16019         var html = [];
16020         var records = this.store.getRange();
16021         if(records.length < 1) {
16022             
16023             // is this valid??  = should it render a template??
16024             
16025             this.el.update(this.emptyText);
16026             return;
16027         }
16028         var el = this.el;
16029         if (this.dataName) {
16030             this.el.update(t.apply(this.store.meta)); //????
16031             el = this.el.child('.roo-tpl-' + this.dataName);
16032         }
16033         
16034         for(var i = 0, len = records.length; i < len; i++){
16035             var data = this.prepareData(records[i].data, i, records[i]);
16036             this.fireEvent("preparedata", this, data, i, records[i]);
16037             
16038             var d = Roo.apply({}, data);
16039             
16040             if(this.tickable){
16041                 Roo.apply(d, {'roo-id' : Roo.id()});
16042                 
16043                 var _this = this;
16044             
16045                 Roo.each(this.parent.item, function(item){
16046                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16047                         return;
16048                     }
16049                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16050                 });
16051             }
16052             
16053             html[html.length] = Roo.util.Format.trim(
16054                 this.dataName ?
16055                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16056                     t.apply(d)
16057             );
16058         }
16059         
16060         
16061         
16062         el.update(html.join(""));
16063         this.nodes = el.dom.childNodes;
16064         this.updateIndexes(0);
16065     },
16066     
16067
16068     /**
16069      * Function to override to reformat the data that is sent to
16070      * the template for each node.
16071      * DEPRICATED - use the preparedata event handler.
16072      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16073      * a JSON object for an UpdateManager bound view).
16074      */
16075     prepareData : function(data, index, record)
16076     {
16077         this.fireEvent("preparedata", this, data, index, record);
16078         return data;
16079     },
16080
16081     onUpdate : function(ds, record){
16082         // Roo.log('on update');   
16083         this.clearSelections();
16084         var index = this.store.indexOf(record);
16085         var n = this.nodes[index];
16086         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16087         n.parentNode.removeChild(n);
16088         this.updateIndexes(index, index);
16089     },
16090
16091     
16092     
16093 // --------- FIXME     
16094     onAdd : function(ds, records, index)
16095     {
16096         //Roo.log(['on Add', ds, records, index] );        
16097         this.clearSelections();
16098         if(this.nodes.length == 0){
16099             this.refresh();
16100             return;
16101         }
16102         var n = this.nodes[index];
16103         for(var i = 0, len = records.length; i < len; i++){
16104             var d = this.prepareData(records[i].data, i, records[i]);
16105             if(n){
16106                 this.tpl.insertBefore(n, d);
16107             }else{
16108                 
16109                 this.tpl.append(this.el, d);
16110             }
16111         }
16112         this.updateIndexes(index);
16113     },
16114
16115     onRemove : function(ds, record, index){
16116        // Roo.log('onRemove');
16117         this.clearSelections();
16118         var el = this.dataName  ?
16119             this.el.child('.roo-tpl-' + this.dataName) :
16120             this.el; 
16121         
16122         el.dom.removeChild(this.nodes[index]);
16123         this.updateIndexes(index);
16124     },
16125
16126     /**
16127      * Refresh an individual node.
16128      * @param {Number} index
16129      */
16130     refreshNode : function(index){
16131         this.onUpdate(this.store, this.store.getAt(index));
16132     },
16133
16134     updateIndexes : function(startIndex, endIndex){
16135         var ns = this.nodes;
16136         startIndex = startIndex || 0;
16137         endIndex = endIndex || ns.length - 1;
16138         for(var i = startIndex; i <= endIndex; i++){
16139             ns[i].nodeIndex = i;
16140         }
16141     },
16142
16143     /**
16144      * Changes the data store this view uses and refresh the view.
16145      * @param {Store} store
16146      */
16147     setStore : function(store, initial){
16148         if(!initial && this.store){
16149             this.store.un("datachanged", this.refresh);
16150             this.store.un("add", this.onAdd);
16151             this.store.un("remove", this.onRemove);
16152             this.store.un("update", this.onUpdate);
16153             this.store.un("clear", this.refresh);
16154             this.store.un("beforeload", this.onBeforeLoad);
16155             this.store.un("load", this.onLoad);
16156             this.store.un("loadexception", this.onLoad);
16157         }
16158         if(store){
16159           
16160             store.on("datachanged", this.refresh, this);
16161             store.on("add", this.onAdd, this);
16162             store.on("remove", this.onRemove, this);
16163             store.on("update", this.onUpdate, this);
16164             store.on("clear", this.refresh, this);
16165             store.on("beforeload", this.onBeforeLoad, this);
16166             store.on("load", this.onLoad, this);
16167             store.on("loadexception", this.onLoad, this);
16168         }
16169         
16170         if(store){
16171             this.refresh();
16172         }
16173     },
16174     /**
16175      * onbeforeLoad - masks the loading area.
16176      *
16177      */
16178     onBeforeLoad : function(store,opts)
16179     {
16180          //Roo.log('onBeforeLoad');   
16181         if (!opts.add) {
16182             this.el.update("");
16183         }
16184         this.el.mask(this.mask ? this.mask : "Loading" ); 
16185     },
16186     onLoad : function ()
16187     {
16188         this.el.unmask();
16189     },
16190     
16191
16192     /**
16193      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16194      * @param {HTMLElement} node
16195      * @return {HTMLElement} The template node
16196      */
16197     findItemFromChild : function(node){
16198         var el = this.dataName  ?
16199             this.el.child('.roo-tpl-' + this.dataName,true) :
16200             this.el.dom; 
16201         
16202         if(!node || node.parentNode == el){
16203                     return node;
16204             }
16205             var p = node.parentNode;
16206             while(p && p != el){
16207             if(p.parentNode == el){
16208                 return p;
16209             }
16210             p = p.parentNode;
16211         }
16212             return null;
16213     },
16214
16215     /** @ignore */
16216     onClick : function(e){
16217         var item = this.findItemFromChild(e.getTarget());
16218         if(item){
16219             var index = this.indexOf(item);
16220             if(this.onItemClick(item, index, e) !== false){
16221                 this.fireEvent("click", this, index, item, e);
16222             }
16223         }else{
16224             this.clearSelections();
16225         }
16226     },
16227
16228     /** @ignore */
16229     onContextMenu : function(e){
16230         var item = this.findItemFromChild(e.getTarget());
16231         if(item){
16232             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16233         }
16234     },
16235
16236     /** @ignore */
16237     onDblClick : function(e){
16238         var item = this.findItemFromChild(e.getTarget());
16239         if(item){
16240             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16241         }
16242     },
16243
16244     onItemClick : function(item, index, e)
16245     {
16246         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16247             return false;
16248         }
16249         if (this.toggleSelect) {
16250             var m = this.isSelected(item) ? 'unselect' : 'select';
16251             //Roo.log(m);
16252             var _t = this;
16253             _t[m](item, true, false);
16254             return true;
16255         }
16256         if(this.multiSelect || this.singleSelect){
16257             if(this.multiSelect && e.shiftKey && this.lastSelection){
16258                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16259             }else{
16260                 this.select(item, this.multiSelect && e.ctrlKey);
16261                 this.lastSelection = item;
16262             }
16263             
16264             if(!this.tickable){
16265                 e.preventDefault();
16266             }
16267             
16268         }
16269         return true;
16270     },
16271
16272     /**
16273      * Get the number of selected nodes.
16274      * @return {Number}
16275      */
16276     getSelectionCount : function(){
16277         return this.selections.length;
16278     },
16279
16280     /**
16281      * Get the currently selected nodes.
16282      * @return {Array} An array of HTMLElements
16283      */
16284     getSelectedNodes : function(){
16285         return this.selections;
16286     },
16287
16288     /**
16289      * Get the indexes of the selected nodes.
16290      * @return {Array}
16291      */
16292     getSelectedIndexes : function(){
16293         var indexes = [], s = this.selections;
16294         for(var i = 0, len = s.length; i < len; i++){
16295             indexes.push(s[i].nodeIndex);
16296         }
16297         return indexes;
16298     },
16299
16300     /**
16301      * Clear all selections
16302      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16303      */
16304     clearSelections : function(suppressEvent){
16305         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16306             this.cmp.elements = this.selections;
16307             this.cmp.removeClass(this.selectedClass);
16308             this.selections = [];
16309             if(!suppressEvent){
16310                 this.fireEvent("selectionchange", this, this.selections);
16311             }
16312         }
16313     },
16314
16315     /**
16316      * Returns true if the passed node is selected
16317      * @param {HTMLElement/Number} node The node or node index
16318      * @return {Boolean}
16319      */
16320     isSelected : function(node){
16321         var s = this.selections;
16322         if(s.length < 1){
16323             return false;
16324         }
16325         node = this.getNode(node);
16326         return s.indexOf(node) !== -1;
16327     },
16328
16329     /**
16330      * Selects nodes.
16331      * @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
16332      * @param {Boolean} keepExisting (optional) true to keep existing selections
16333      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16334      */
16335     select : function(nodeInfo, keepExisting, suppressEvent){
16336         if(nodeInfo instanceof Array){
16337             if(!keepExisting){
16338                 this.clearSelections(true);
16339             }
16340             for(var i = 0, len = nodeInfo.length; i < len; i++){
16341                 this.select(nodeInfo[i], true, true);
16342             }
16343             return;
16344         } 
16345         var node = this.getNode(nodeInfo);
16346         if(!node || this.isSelected(node)){
16347             return; // already selected.
16348         }
16349         if(!keepExisting){
16350             this.clearSelections(true);
16351         }
16352         
16353         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16354             Roo.fly(node).addClass(this.selectedClass);
16355             this.selections.push(node);
16356             if(!suppressEvent){
16357                 this.fireEvent("selectionchange", this, this.selections);
16358             }
16359         }
16360         
16361         
16362     },
16363       /**
16364      * Unselects nodes.
16365      * @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
16366      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16367      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16368      */
16369     unselect : function(nodeInfo, keepExisting, suppressEvent)
16370     {
16371         if(nodeInfo instanceof Array){
16372             Roo.each(this.selections, function(s) {
16373                 this.unselect(s, nodeInfo);
16374             }, this);
16375             return;
16376         }
16377         var node = this.getNode(nodeInfo);
16378         if(!node || !this.isSelected(node)){
16379             //Roo.log("not selected");
16380             return; // not selected.
16381         }
16382         // fireevent???
16383         var ns = [];
16384         Roo.each(this.selections, function(s) {
16385             if (s == node ) {
16386                 Roo.fly(node).removeClass(this.selectedClass);
16387
16388                 return;
16389             }
16390             ns.push(s);
16391         },this);
16392         
16393         this.selections= ns;
16394         this.fireEvent("selectionchange", this, this.selections);
16395     },
16396
16397     /**
16398      * Gets a template node.
16399      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16400      * @return {HTMLElement} The node or null if it wasn't found
16401      */
16402     getNode : function(nodeInfo){
16403         if(typeof nodeInfo == "string"){
16404             return document.getElementById(nodeInfo);
16405         }else if(typeof nodeInfo == "number"){
16406             return this.nodes[nodeInfo];
16407         }
16408         return nodeInfo;
16409     },
16410
16411     /**
16412      * Gets a range template nodes.
16413      * @param {Number} startIndex
16414      * @param {Number} endIndex
16415      * @return {Array} An array of nodes
16416      */
16417     getNodes : function(start, end){
16418         var ns = this.nodes;
16419         start = start || 0;
16420         end = typeof end == "undefined" ? ns.length - 1 : end;
16421         var nodes = [];
16422         if(start <= end){
16423             for(var i = start; i <= end; i++){
16424                 nodes.push(ns[i]);
16425             }
16426         } else{
16427             for(var i = start; i >= end; i--){
16428                 nodes.push(ns[i]);
16429             }
16430         }
16431         return nodes;
16432     },
16433
16434     /**
16435      * Finds the index of the passed node
16436      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16437      * @return {Number} The index of the node or -1
16438      */
16439     indexOf : function(node){
16440         node = this.getNode(node);
16441         if(typeof node.nodeIndex == "number"){
16442             return node.nodeIndex;
16443         }
16444         var ns = this.nodes;
16445         for(var i = 0, len = ns.length; i < len; i++){
16446             if(ns[i] == node){
16447                 return i;
16448             }
16449         }
16450         return -1;
16451     }
16452 });
16453 /*
16454  * - LGPL
16455  *
16456  * based on jquery fullcalendar
16457  * 
16458  */
16459
16460 Roo.bootstrap = Roo.bootstrap || {};
16461 /**
16462  * @class Roo.bootstrap.Calendar
16463  * @extends Roo.bootstrap.Component
16464  * Bootstrap Calendar class
16465  * @cfg {Boolean} loadMask (true|false) default false
16466  * @cfg {Object} header generate the user specific header of the calendar, default false
16467
16468  * @constructor
16469  * Create a new Container
16470  * @param {Object} config The config object
16471  */
16472
16473
16474
16475 Roo.bootstrap.Calendar = function(config){
16476     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16477      this.addEvents({
16478         /**
16479              * @event select
16480              * Fires when a date is selected
16481              * @param {DatePicker} this
16482              * @param {Date} date The selected date
16483              */
16484         'select': true,
16485         /**
16486              * @event monthchange
16487              * Fires when the displayed month changes 
16488              * @param {DatePicker} this
16489              * @param {Date} date The selected month
16490              */
16491         'monthchange': true,
16492         /**
16493              * @event evententer
16494              * Fires when mouse over an event
16495              * @param {Calendar} this
16496              * @param {event} Event
16497              */
16498         'evententer': true,
16499         /**
16500              * @event eventleave
16501              * Fires when the mouse leaves an
16502              * @param {Calendar} this
16503              * @param {event}
16504              */
16505         'eventleave': true,
16506         /**
16507              * @event eventclick
16508              * Fires when the mouse click an
16509              * @param {Calendar} this
16510              * @param {event}
16511              */
16512         'eventclick': true
16513         
16514     });
16515
16516 };
16517
16518 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16519     
16520      /**
16521      * @cfg {Number} startDay
16522      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16523      */
16524     startDay : 0,
16525     
16526     loadMask : false,
16527     
16528     header : false,
16529       
16530     getAutoCreate : function(){
16531         
16532         
16533         var fc_button = function(name, corner, style, content ) {
16534             return Roo.apply({},{
16535                 tag : 'span',
16536                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16537                          (corner.length ?
16538                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16539                             ''
16540                         ),
16541                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16542                 unselectable: 'on'
16543             });
16544         };
16545         
16546         var header = {};
16547         
16548         if(!this.header){
16549             header = {
16550                 tag : 'table',
16551                 cls : 'fc-header',
16552                 style : 'width:100%',
16553                 cn : [
16554                     {
16555                         tag: 'tr',
16556                         cn : [
16557                             {
16558                                 tag : 'td',
16559                                 cls : 'fc-header-left',
16560                                 cn : [
16561                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16562                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16563                                     { tag: 'span', cls: 'fc-header-space' },
16564                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16565
16566
16567                                 ]
16568                             },
16569
16570                             {
16571                                 tag : 'td',
16572                                 cls : 'fc-header-center',
16573                                 cn : [
16574                                     {
16575                                         tag: 'span',
16576                                         cls: 'fc-header-title',
16577                                         cn : {
16578                                             tag: 'H2',
16579                                             html : 'month / year'
16580                                         }
16581                                     }
16582
16583                                 ]
16584                             },
16585                             {
16586                                 tag : 'td',
16587                                 cls : 'fc-header-right',
16588                                 cn : [
16589                               /*      fc_button('month', 'left', '', 'month' ),
16590                                     fc_button('week', '', '', 'week' ),
16591                                     fc_button('day', 'right', '', 'day' )
16592                                 */    
16593
16594                                 ]
16595                             }
16596
16597                         ]
16598                     }
16599                 ]
16600             };
16601         }
16602         
16603         header = this.header;
16604         
16605        
16606         var cal_heads = function() {
16607             var ret = [];
16608             // fixme - handle this.
16609             
16610             for (var i =0; i < Date.dayNames.length; i++) {
16611                 var d = Date.dayNames[i];
16612                 ret.push({
16613                     tag: 'th',
16614                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16615                     html : d.substring(0,3)
16616                 });
16617                 
16618             }
16619             ret[0].cls += ' fc-first';
16620             ret[6].cls += ' fc-last';
16621             return ret;
16622         };
16623         var cal_cell = function(n) {
16624             return  {
16625                 tag: 'td',
16626                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16627                 cn : [
16628                     {
16629                         cn : [
16630                             {
16631                                 cls: 'fc-day-number',
16632                                 html: 'D'
16633                             },
16634                             {
16635                                 cls: 'fc-day-content',
16636                              
16637                                 cn : [
16638                                      {
16639                                         style: 'position: relative;' // height: 17px;
16640                                     }
16641                                 ]
16642                             }
16643                             
16644                             
16645                         ]
16646                     }
16647                 ]
16648                 
16649             }
16650         };
16651         var cal_rows = function() {
16652             
16653             var ret = [];
16654             for (var r = 0; r < 6; r++) {
16655                 var row= {
16656                     tag : 'tr',
16657                     cls : 'fc-week',
16658                     cn : []
16659                 };
16660                 
16661                 for (var i =0; i < Date.dayNames.length; i++) {
16662                     var d = Date.dayNames[i];
16663                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16664
16665                 }
16666                 row.cn[0].cls+=' fc-first';
16667                 row.cn[0].cn[0].style = 'min-height:90px';
16668                 row.cn[6].cls+=' fc-last';
16669                 ret.push(row);
16670                 
16671             }
16672             ret[0].cls += ' fc-first';
16673             ret[4].cls += ' fc-prev-last';
16674             ret[5].cls += ' fc-last';
16675             return ret;
16676             
16677         };
16678         
16679         var cal_table = {
16680             tag: 'table',
16681             cls: 'fc-border-separate',
16682             style : 'width:100%',
16683             cellspacing  : 0,
16684             cn : [
16685                 { 
16686                     tag: 'thead',
16687                     cn : [
16688                         { 
16689                             tag: 'tr',
16690                             cls : 'fc-first fc-last',
16691                             cn : cal_heads()
16692                         }
16693                     ]
16694                 },
16695                 { 
16696                     tag: 'tbody',
16697                     cn : cal_rows()
16698                 }
16699                   
16700             ]
16701         };
16702          
16703          var cfg = {
16704             cls : 'fc fc-ltr',
16705             cn : [
16706                 header,
16707                 {
16708                     cls : 'fc-content',
16709                     style : "position: relative;",
16710                     cn : [
16711                         {
16712                             cls : 'fc-view fc-view-month fc-grid',
16713                             style : 'position: relative',
16714                             unselectable : 'on',
16715                             cn : [
16716                                 {
16717                                     cls : 'fc-event-container',
16718                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16719                                 },
16720                                 cal_table
16721                             ]
16722                         }
16723                     ]
16724     
16725                 }
16726            ] 
16727             
16728         };
16729         
16730          
16731         
16732         return cfg;
16733     },
16734     
16735     
16736     initEvents : function()
16737     {
16738         if(!this.store){
16739             throw "can not find store for calendar";
16740         }
16741         
16742         var mark = {
16743             tag: "div",
16744             cls:"x-dlg-mask",
16745             style: "text-align:center",
16746             cn: [
16747                 {
16748                     tag: "div",
16749                     style: "background-color:white;width:50%;margin:250 auto",
16750                     cn: [
16751                         {
16752                             tag: "img",
16753                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16754                         },
16755                         {
16756                             tag: "span",
16757                             html: "Loading"
16758                         }
16759                         
16760                     ]
16761                 }
16762             ]
16763         };
16764         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16765         
16766         var size = this.el.select('.fc-content', true).first().getSize();
16767         this.maskEl.setSize(size.width, size.height);
16768         this.maskEl.enableDisplayMode("block");
16769         if(!this.loadMask){
16770             this.maskEl.hide();
16771         }
16772         
16773         this.store = Roo.factory(this.store, Roo.data);
16774         this.store.on('load', this.onLoad, this);
16775         this.store.on('beforeload', this.onBeforeLoad, this);
16776         
16777         this.resize();
16778         
16779         this.cells = this.el.select('.fc-day',true);
16780         //Roo.log(this.cells);
16781         this.textNodes = this.el.query('.fc-day-number');
16782         this.cells.addClassOnOver('fc-state-hover');
16783         
16784         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16785         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16786         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16787         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16788         
16789         this.on('monthchange', this.onMonthChange, this);
16790         
16791         this.update(new Date().clearTime());
16792     },
16793     
16794     resize : function() {
16795         var sz  = this.el.getSize();
16796         
16797         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16798         this.el.select('.fc-day-content div',true).setHeight(34);
16799     },
16800     
16801     
16802     // private
16803     showPrevMonth : function(e){
16804         this.update(this.activeDate.add("mo", -1));
16805     },
16806     showToday : function(e){
16807         this.update(new Date().clearTime());
16808     },
16809     // private
16810     showNextMonth : function(e){
16811         this.update(this.activeDate.add("mo", 1));
16812     },
16813
16814     // private
16815     showPrevYear : function(){
16816         this.update(this.activeDate.add("y", -1));
16817     },
16818
16819     // private
16820     showNextYear : function(){
16821         this.update(this.activeDate.add("y", 1));
16822     },
16823
16824     
16825    // private
16826     update : function(date)
16827     {
16828         var vd = this.activeDate;
16829         this.activeDate = date;
16830 //        if(vd && this.el){
16831 //            var t = date.getTime();
16832 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16833 //                Roo.log('using add remove');
16834 //                
16835 //                this.fireEvent('monthchange', this, date);
16836 //                
16837 //                this.cells.removeClass("fc-state-highlight");
16838 //                this.cells.each(function(c){
16839 //                   if(c.dateValue == t){
16840 //                       c.addClass("fc-state-highlight");
16841 //                       setTimeout(function(){
16842 //                            try{c.dom.firstChild.focus();}catch(e){}
16843 //                       }, 50);
16844 //                       return false;
16845 //                   }
16846 //                   return true;
16847 //                });
16848 //                return;
16849 //            }
16850 //        }
16851         
16852         var days = date.getDaysInMonth();
16853         
16854         var firstOfMonth = date.getFirstDateOfMonth();
16855         var startingPos = firstOfMonth.getDay()-this.startDay;
16856         
16857         if(startingPos < this.startDay){
16858             startingPos += 7;
16859         }
16860         
16861         var pm = date.add(Date.MONTH, -1);
16862         var prevStart = pm.getDaysInMonth()-startingPos;
16863 //        
16864         this.cells = this.el.select('.fc-day',true);
16865         this.textNodes = this.el.query('.fc-day-number');
16866         this.cells.addClassOnOver('fc-state-hover');
16867         
16868         var cells = this.cells.elements;
16869         var textEls = this.textNodes;
16870         
16871         Roo.each(cells, function(cell){
16872             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16873         });
16874         
16875         days += startingPos;
16876
16877         // convert everything to numbers so it's fast
16878         var day = 86400000;
16879         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16880         //Roo.log(d);
16881         //Roo.log(pm);
16882         //Roo.log(prevStart);
16883         
16884         var today = new Date().clearTime().getTime();
16885         var sel = date.clearTime().getTime();
16886         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16887         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16888         var ddMatch = this.disabledDatesRE;
16889         var ddText = this.disabledDatesText;
16890         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16891         var ddaysText = this.disabledDaysText;
16892         var format = this.format;
16893         
16894         var setCellClass = function(cal, cell){
16895             cell.row = 0;
16896             cell.events = [];
16897             cell.more = [];
16898             //Roo.log('set Cell Class');
16899             cell.title = "";
16900             var t = d.getTime();
16901             
16902             //Roo.log(d);
16903             
16904             cell.dateValue = t;
16905             if(t == today){
16906                 cell.className += " fc-today";
16907                 cell.className += " fc-state-highlight";
16908                 cell.title = cal.todayText;
16909             }
16910             if(t == sel){
16911                 // disable highlight in other month..
16912                 //cell.className += " fc-state-highlight";
16913                 
16914             }
16915             // disabling
16916             if(t < min) {
16917                 cell.className = " fc-state-disabled";
16918                 cell.title = cal.minText;
16919                 return;
16920             }
16921             if(t > max) {
16922                 cell.className = " fc-state-disabled";
16923                 cell.title = cal.maxText;
16924                 return;
16925             }
16926             if(ddays){
16927                 if(ddays.indexOf(d.getDay()) != -1){
16928                     cell.title = ddaysText;
16929                     cell.className = " fc-state-disabled";
16930                 }
16931             }
16932             if(ddMatch && format){
16933                 var fvalue = d.dateFormat(format);
16934                 if(ddMatch.test(fvalue)){
16935                     cell.title = ddText.replace("%0", fvalue);
16936                     cell.className = " fc-state-disabled";
16937                 }
16938             }
16939             
16940             if (!cell.initialClassName) {
16941                 cell.initialClassName = cell.dom.className;
16942             }
16943             
16944             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16945         };
16946
16947         var i = 0;
16948         
16949         for(; i < startingPos; i++) {
16950             textEls[i].innerHTML = (++prevStart);
16951             d.setDate(d.getDate()+1);
16952             
16953             cells[i].className = "fc-past fc-other-month";
16954             setCellClass(this, cells[i]);
16955         }
16956         
16957         var intDay = 0;
16958         
16959         for(; i < days; i++){
16960             intDay = i - startingPos + 1;
16961             textEls[i].innerHTML = (intDay);
16962             d.setDate(d.getDate()+1);
16963             
16964             cells[i].className = ''; // "x-date-active";
16965             setCellClass(this, cells[i]);
16966         }
16967         var extraDays = 0;
16968         
16969         for(; i < 42; i++) {
16970             textEls[i].innerHTML = (++extraDays);
16971             d.setDate(d.getDate()+1);
16972             
16973             cells[i].className = "fc-future fc-other-month";
16974             setCellClass(this, cells[i]);
16975         }
16976         
16977         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16978         
16979         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16980         
16981         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16982         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16983         
16984         if(totalRows != 6){
16985             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16986             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16987         }
16988         
16989         this.fireEvent('monthchange', this, date);
16990         
16991         
16992         /*
16993         if(!this.internalRender){
16994             var main = this.el.dom.firstChild;
16995             var w = main.offsetWidth;
16996             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16997             Roo.fly(main).setWidth(w);
16998             this.internalRender = true;
16999             // opera does not respect the auto grow header center column
17000             // then, after it gets a width opera refuses to recalculate
17001             // without a second pass
17002             if(Roo.isOpera && !this.secondPass){
17003                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17004                 this.secondPass = true;
17005                 this.update.defer(10, this, [date]);
17006             }
17007         }
17008         */
17009         
17010     },
17011     
17012     findCell : function(dt) {
17013         dt = dt.clearTime().getTime();
17014         var ret = false;
17015         this.cells.each(function(c){
17016             //Roo.log("check " +c.dateValue + '?=' + dt);
17017             if(c.dateValue == dt){
17018                 ret = c;
17019                 return false;
17020             }
17021             return true;
17022         });
17023         
17024         return ret;
17025     },
17026     
17027     findCells : function(ev) {
17028         var s = ev.start.clone().clearTime().getTime();
17029        // Roo.log(s);
17030         var e= ev.end.clone().clearTime().getTime();
17031        // Roo.log(e);
17032         var ret = [];
17033         this.cells.each(function(c){
17034              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17035             
17036             if(c.dateValue > e){
17037                 return ;
17038             }
17039             if(c.dateValue < s){
17040                 return ;
17041             }
17042             ret.push(c);
17043         });
17044         
17045         return ret;    
17046     },
17047     
17048 //    findBestRow: function(cells)
17049 //    {
17050 //        var ret = 0;
17051 //        
17052 //        for (var i =0 ; i < cells.length;i++) {
17053 //            ret  = Math.max(cells[i].rows || 0,ret);
17054 //        }
17055 //        return ret;
17056 //        
17057 //    },
17058     
17059     
17060     addItem : function(ev)
17061     {
17062         // look for vertical location slot in
17063         var cells = this.findCells(ev);
17064         
17065 //        ev.row = this.findBestRow(cells);
17066         
17067         // work out the location.
17068         
17069         var crow = false;
17070         var rows = [];
17071         for(var i =0; i < cells.length; i++) {
17072             
17073             cells[i].row = cells[0].row;
17074             
17075             if(i == 0){
17076                 cells[i].row = cells[i].row + 1;
17077             }
17078             
17079             if (!crow) {
17080                 crow = {
17081                     start : cells[i],
17082                     end :  cells[i]
17083                 };
17084                 continue;
17085             }
17086             if (crow.start.getY() == cells[i].getY()) {
17087                 // on same row.
17088                 crow.end = cells[i];
17089                 continue;
17090             }
17091             // different row.
17092             rows.push(crow);
17093             crow = {
17094                 start: cells[i],
17095                 end : cells[i]
17096             };
17097             
17098         }
17099         
17100         rows.push(crow);
17101         ev.els = [];
17102         ev.rows = rows;
17103         ev.cells = cells;
17104         
17105         cells[0].events.push(ev);
17106         
17107         this.calevents.push(ev);
17108     },
17109     
17110     clearEvents: function() {
17111         
17112         if(!this.calevents){
17113             return;
17114         }
17115         
17116         Roo.each(this.cells.elements, function(c){
17117             c.row = 0;
17118             c.events = [];
17119             c.more = [];
17120         });
17121         
17122         Roo.each(this.calevents, function(e) {
17123             Roo.each(e.els, function(el) {
17124                 el.un('mouseenter' ,this.onEventEnter, this);
17125                 el.un('mouseleave' ,this.onEventLeave, this);
17126                 el.remove();
17127             },this);
17128         },this);
17129         
17130         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17131             e.remove();
17132         });
17133         
17134     },
17135     
17136     renderEvents: function()
17137     {   
17138         var _this = this;
17139         
17140         this.cells.each(function(c) {
17141             
17142             if(c.row < 5){
17143                 return;
17144             }
17145             
17146             var ev = c.events;
17147             
17148             var r = 4;
17149             if(c.row != c.events.length){
17150                 r = 4 - (4 - (c.row - c.events.length));
17151             }
17152             
17153             c.events = ev.slice(0, r);
17154             c.more = ev.slice(r);
17155             
17156             if(c.more.length && c.more.length == 1){
17157                 c.events.push(c.more.pop());
17158             }
17159             
17160             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17161             
17162         });
17163             
17164         this.cells.each(function(c) {
17165             
17166             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17167             
17168             
17169             for (var e = 0; e < c.events.length; e++){
17170                 var ev = c.events[e];
17171                 var rows = ev.rows;
17172                 
17173                 for(var i = 0; i < rows.length; i++) {
17174                 
17175                     // how many rows should it span..
17176
17177                     var  cfg = {
17178                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17179                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17180
17181                         unselectable : "on",
17182                         cn : [
17183                             {
17184                                 cls: 'fc-event-inner',
17185                                 cn : [
17186     //                                {
17187     //                                  tag:'span',
17188     //                                  cls: 'fc-event-time',
17189     //                                  html : cells.length > 1 ? '' : ev.time
17190     //                                },
17191                                     {
17192                                       tag:'span',
17193                                       cls: 'fc-event-title',
17194                                       html : String.format('{0}', ev.title)
17195                                     }
17196
17197
17198                                 ]
17199                             },
17200                             {
17201                                 cls: 'ui-resizable-handle ui-resizable-e',
17202                                 html : '&nbsp;&nbsp;&nbsp'
17203                             }
17204
17205                         ]
17206                     };
17207
17208                     if (i == 0) {
17209                         cfg.cls += ' fc-event-start';
17210                     }
17211                     if ((i+1) == rows.length) {
17212                         cfg.cls += ' fc-event-end';
17213                     }
17214
17215                     var ctr = _this.el.select('.fc-event-container',true).first();
17216                     var cg = ctr.createChild(cfg);
17217
17218                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17219                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17220
17221                     var r = (c.more.length) ? 1 : 0;
17222                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17223                     cg.setWidth(ebox.right - sbox.x -2);
17224
17225                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17226                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17227                     cg.on('click', _this.onEventClick, _this, ev);
17228
17229                     ev.els.push(cg);
17230                     
17231                 }
17232                 
17233             }
17234             
17235             
17236             if(c.more.length){
17237                 var  cfg = {
17238                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17239                     style : 'position: absolute',
17240                     unselectable : "on",
17241                     cn : [
17242                         {
17243                             cls: 'fc-event-inner',
17244                             cn : [
17245                                 {
17246                                   tag:'span',
17247                                   cls: 'fc-event-title',
17248                                   html : 'More'
17249                                 }
17250
17251
17252                             ]
17253                         },
17254                         {
17255                             cls: 'ui-resizable-handle ui-resizable-e',
17256                             html : '&nbsp;&nbsp;&nbsp'
17257                         }
17258
17259                     ]
17260                 };
17261
17262                 var ctr = _this.el.select('.fc-event-container',true).first();
17263                 var cg = ctr.createChild(cfg);
17264
17265                 var sbox = c.select('.fc-day-content',true).first().getBox();
17266                 var ebox = c.select('.fc-day-content',true).first().getBox();
17267                 //Roo.log(cg);
17268                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17269                 cg.setWidth(ebox.right - sbox.x -2);
17270
17271                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17272                 
17273             }
17274             
17275         });
17276         
17277         
17278         
17279     },
17280     
17281     onEventEnter: function (e, el,event,d) {
17282         this.fireEvent('evententer', this, el, event);
17283     },
17284     
17285     onEventLeave: function (e, el,event,d) {
17286         this.fireEvent('eventleave', this, el, event);
17287     },
17288     
17289     onEventClick: function (e, el,event,d) {
17290         this.fireEvent('eventclick', this, el, event);
17291     },
17292     
17293     onMonthChange: function () {
17294         this.store.load();
17295     },
17296     
17297     onMoreEventClick: function(e, el, more)
17298     {
17299         var _this = this;
17300         
17301         this.calpopover.placement = 'right';
17302         this.calpopover.setTitle('More');
17303         
17304         this.calpopover.setContent('');
17305         
17306         var ctr = this.calpopover.el.select('.popover-content', true).first();
17307         
17308         Roo.each(more, function(m){
17309             var cfg = {
17310                 cls : 'fc-event-hori fc-event-draggable',
17311                 html : m.title
17312             };
17313             var cg = ctr.createChild(cfg);
17314             
17315             cg.on('click', _this.onEventClick, _this, m);
17316         });
17317         
17318         this.calpopover.show(el);
17319         
17320         
17321     },
17322     
17323     onLoad: function () 
17324     {   
17325         this.calevents = [];
17326         var cal = this;
17327         
17328         if(this.store.getCount() > 0){
17329             this.store.data.each(function(d){
17330                cal.addItem({
17331                     id : d.data.id,
17332                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17333                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17334                     time : d.data.start_time,
17335                     title : d.data.title,
17336                     description : d.data.description,
17337                     venue : d.data.venue
17338                 });
17339             });
17340         }
17341         
17342         this.renderEvents();
17343         
17344         if(this.calevents.length && this.loadMask){
17345             this.maskEl.hide();
17346         }
17347     },
17348     
17349     onBeforeLoad: function()
17350     {
17351         this.clearEvents();
17352         if(this.loadMask){
17353             this.maskEl.show();
17354         }
17355     }
17356 });
17357
17358  
17359  /*
17360  * - LGPL
17361  *
17362  * element
17363  * 
17364  */
17365
17366 /**
17367  * @class Roo.bootstrap.Popover
17368  * @extends Roo.bootstrap.Component
17369  * Bootstrap Popover class
17370  * @cfg {String} html contents of the popover   (or false to use children..)
17371  * @cfg {String} title of popover (or false to hide)
17372  * @cfg {String} placement how it is placed
17373  * @cfg {String} trigger click || hover (or false to trigger manually)
17374  * @cfg {String} over what (parent or false to trigger manually.)
17375  * @cfg {Number} delay - delay before showing
17376  
17377  * @constructor
17378  * Create a new Popover
17379  * @param {Object} config The config object
17380  */
17381
17382 Roo.bootstrap.Popover = function(config){
17383     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17384     
17385     this.addEvents({
17386         // raw events
17387          /**
17388          * @event show
17389          * After the popover show
17390          * 
17391          * @param {Roo.bootstrap.Popover} this
17392          */
17393         "show" : true,
17394         /**
17395          * @event hide
17396          * After the popover hide
17397          * 
17398          * @param {Roo.bootstrap.Popover} this
17399          */
17400         "hide" : true
17401     });
17402 };
17403
17404 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17405     
17406     title: 'Fill in a title',
17407     html: false,
17408     
17409     placement : 'right',
17410     trigger : 'hover', // hover
17411     
17412     delay : 0,
17413     
17414     over: 'parent',
17415     
17416     can_build_overlaid : false,
17417     
17418     getChildContainer : function()
17419     {
17420         return this.el.select('.popover-content',true).first();
17421     },
17422     
17423     getAutoCreate : function(){
17424          
17425         var cfg = {
17426            cls : 'popover roo-dynamic',
17427            style: 'display:block',
17428            cn : [
17429                 {
17430                     cls : 'arrow'
17431                 },
17432                 {
17433                     cls : 'popover-inner',
17434                     cn : [
17435                         {
17436                             tag: 'h3',
17437                             cls: 'popover-title',
17438                             html : this.title
17439                         },
17440                         {
17441                             cls : 'popover-content',
17442                             html : this.html
17443                         }
17444                     ]
17445                     
17446                 }
17447            ]
17448         };
17449         
17450         return cfg;
17451     },
17452     setTitle: function(str)
17453     {
17454         this.title = str;
17455         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17456     },
17457     setContent: function(str)
17458     {
17459         this.html = str;
17460         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17461     },
17462     // as it get's added to the bottom of the page.
17463     onRender : function(ct, position)
17464     {
17465         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17466         if(!this.el){
17467             var cfg = Roo.apply({},  this.getAutoCreate());
17468             cfg.id = Roo.id();
17469             
17470             if (this.cls) {
17471                 cfg.cls += ' ' + this.cls;
17472             }
17473             if (this.style) {
17474                 cfg.style = this.style;
17475             }
17476             //Roo.log("adding to ");
17477             this.el = Roo.get(document.body).createChild(cfg, position);
17478 //            Roo.log(this.el);
17479         }
17480         this.initEvents();
17481     },
17482     
17483     initEvents : function()
17484     {
17485         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17486         this.el.enableDisplayMode('block');
17487         this.el.hide();
17488         if (this.over === false) {
17489             return; 
17490         }
17491         if (this.triggers === false) {
17492             return;
17493         }
17494         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17495         var triggers = this.trigger ? this.trigger.split(' ') : [];
17496         Roo.each(triggers, function(trigger) {
17497         
17498             if (trigger == 'click') {
17499                 on_el.on('click', this.toggle, this);
17500             } else if (trigger != 'manual') {
17501                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17502                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17503       
17504                 on_el.on(eventIn  ,this.enter, this);
17505                 on_el.on(eventOut, this.leave, this);
17506             }
17507         }, this);
17508         
17509     },
17510     
17511     
17512     // private
17513     timeout : null,
17514     hoverState : null,
17515     
17516     toggle : function () {
17517         this.hoverState == 'in' ? this.leave() : this.enter();
17518     },
17519     
17520     enter : function () {
17521         
17522         clearTimeout(this.timeout);
17523     
17524         this.hoverState = 'in';
17525     
17526         if (!this.delay || !this.delay.show) {
17527             this.show();
17528             return;
17529         }
17530         var _t = this;
17531         this.timeout = setTimeout(function () {
17532             if (_t.hoverState == 'in') {
17533                 _t.show();
17534             }
17535         }, this.delay.show)
17536     },
17537     
17538     leave : function() {
17539         clearTimeout(this.timeout);
17540     
17541         this.hoverState = 'out';
17542     
17543         if (!this.delay || !this.delay.hide) {
17544             this.hide();
17545             return;
17546         }
17547         var _t = this;
17548         this.timeout = setTimeout(function () {
17549             if (_t.hoverState == 'out') {
17550                 _t.hide();
17551             }
17552         }, this.delay.hide)
17553     },
17554     
17555     show : function (on_el)
17556     {
17557         if (!on_el) {
17558             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17559         }
17560         
17561         // set content.
17562         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17563         if (this.html !== false) {
17564             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17565         }
17566         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17567         if (!this.title.length) {
17568             this.el.select('.popover-title',true).hide();
17569         }
17570         
17571         var placement = typeof this.placement == 'function' ?
17572             this.placement.call(this, this.el, on_el) :
17573             this.placement;
17574             
17575         var autoToken = /\s?auto?\s?/i;
17576         var autoPlace = autoToken.test(placement);
17577         if (autoPlace) {
17578             placement = placement.replace(autoToken, '') || 'top';
17579         }
17580         
17581         //this.el.detach()
17582         //this.el.setXY([0,0]);
17583         this.el.show();
17584         this.el.dom.style.display='block';
17585         this.el.addClass(placement);
17586         
17587         //this.el.appendTo(on_el);
17588         
17589         var p = this.getPosition();
17590         var box = this.el.getBox();
17591         
17592         if (autoPlace) {
17593             // fixme..
17594         }
17595         var align = Roo.bootstrap.Popover.alignment[placement];
17596         
17597 //        Roo.log(align);
17598         this.el.alignTo(on_el, align[0],align[1]);
17599         //var arrow = this.el.select('.arrow',true).first();
17600         //arrow.set(align[2], 
17601         
17602         this.el.addClass('in');
17603         
17604         
17605         if (this.el.hasClass('fade')) {
17606             // fade it?
17607         }
17608         
17609         this.hoverState = 'in';
17610         
17611         this.fireEvent('show', this);
17612         
17613     },
17614     hide : function()
17615     {
17616         this.el.setXY([0,0]);
17617         this.el.removeClass('in');
17618         this.el.hide();
17619         this.hoverState = null;
17620         
17621         this.fireEvent('hide', this);
17622     }
17623     
17624 });
17625
17626 Roo.bootstrap.Popover.alignment = {
17627     'left' : ['r-l', [-10,0], 'right'],
17628     'right' : ['l-r', [10,0], 'left'],
17629     'bottom' : ['t-b', [0,10], 'top'],
17630     'top' : [ 'b-t', [0,-10], 'bottom']
17631 };
17632
17633  /*
17634  * - LGPL
17635  *
17636  * Progress
17637  * 
17638  */
17639
17640 /**
17641  * @class Roo.bootstrap.Progress
17642  * @extends Roo.bootstrap.Component
17643  * Bootstrap Progress class
17644  * @cfg {Boolean} striped striped of the progress bar
17645  * @cfg {Boolean} active animated of the progress bar
17646  * 
17647  * 
17648  * @constructor
17649  * Create a new Progress
17650  * @param {Object} config The config object
17651  */
17652
17653 Roo.bootstrap.Progress = function(config){
17654     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17655 };
17656
17657 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17658     
17659     striped : false,
17660     active: false,
17661     
17662     getAutoCreate : function(){
17663         var cfg = {
17664             tag: 'div',
17665             cls: 'progress'
17666         };
17667         
17668         
17669         if(this.striped){
17670             cfg.cls += ' progress-striped';
17671         }
17672       
17673         if(this.active){
17674             cfg.cls += ' active';
17675         }
17676         
17677         
17678         return cfg;
17679     }
17680    
17681 });
17682
17683  
17684
17685  /*
17686  * - LGPL
17687  *
17688  * ProgressBar
17689  * 
17690  */
17691
17692 /**
17693  * @class Roo.bootstrap.ProgressBar
17694  * @extends Roo.bootstrap.Component
17695  * Bootstrap ProgressBar class
17696  * @cfg {Number} aria_valuenow aria-value now
17697  * @cfg {Number} aria_valuemin aria-value min
17698  * @cfg {Number} aria_valuemax aria-value max
17699  * @cfg {String} label label for the progress bar
17700  * @cfg {String} panel (success | info | warning | danger )
17701  * @cfg {String} role role of the progress bar
17702  * @cfg {String} sr_only text
17703  * 
17704  * 
17705  * @constructor
17706  * Create a new ProgressBar
17707  * @param {Object} config The config object
17708  */
17709
17710 Roo.bootstrap.ProgressBar = function(config){
17711     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17712 };
17713
17714 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17715     
17716     aria_valuenow : 0,
17717     aria_valuemin : 0,
17718     aria_valuemax : 100,
17719     label : false,
17720     panel : false,
17721     role : false,
17722     sr_only: false,
17723     
17724     getAutoCreate : function()
17725     {
17726         
17727         var cfg = {
17728             tag: 'div',
17729             cls: 'progress-bar',
17730             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17731         };
17732         
17733         if(this.sr_only){
17734             cfg.cn = {
17735                 tag: 'span',
17736                 cls: 'sr-only',
17737                 html: this.sr_only
17738             }
17739         }
17740         
17741         if(this.role){
17742             cfg.role = this.role;
17743         }
17744         
17745         if(this.aria_valuenow){
17746             cfg['aria-valuenow'] = this.aria_valuenow;
17747         }
17748         
17749         if(this.aria_valuemin){
17750             cfg['aria-valuemin'] = this.aria_valuemin;
17751         }
17752         
17753         if(this.aria_valuemax){
17754             cfg['aria-valuemax'] = this.aria_valuemax;
17755         }
17756         
17757         if(this.label && !this.sr_only){
17758             cfg.html = this.label;
17759         }
17760         
17761         if(this.panel){
17762             cfg.cls += ' progress-bar-' + this.panel;
17763         }
17764         
17765         return cfg;
17766     },
17767     
17768     update : function(aria_valuenow)
17769     {
17770         this.aria_valuenow = aria_valuenow;
17771         
17772         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17773     }
17774    
17775 });
17776
17777  
17778
17779  /*
17780  * - LGPL
17781  *
17782  * column
17783  * 
17784  */
17785
17786 /**
17787  * @class Roo.bootstrap.TabGroup
17788  * @extends Roo.bootstrap.Column
17789  * Bootstrap Column class
17790  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17791  * @cfg {Boolean} carousel true to make the group behave like a carousel
17792  * @cfg {Boolean} bullets show bullets for the panels
17793  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17794  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17795  * @cfg {Boolean} showarrow (true|false) show arrow default true
17796  * 
17797  * @constructor
17798  * Create a new TabGroup
17799  * @param {Object} config The config object
17800  */
17801
17802 Roo.bootstrap.TabGroup = function(config){
17803     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17804     if (!this.navId) {
17805         this.navId = Roo.id();
17806     }
17807     this.tabs = [];
17808     Roo.bootstrap.TabGroup.register(this);
17809     
17810 };
17811
17812 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17813     
17814     carousel : false,
17815     transition : false,
17816     bullets : 0,
17817     timer : 0,
17818     autoslide : false,
17819     slideFn : false,
17820     slideOnTouch : false,
17821     showarrow : true,
17822     
17823     getAutoCreate : function()
17824     {
17825         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17826         
17827         cfg.cls += ' tab-content';
17828         
17829         if (this.carousel) {
17830             cfg.cls += ' carousel slide';
17831             
17832             cfg.cn = [{
17833                cls : 'carousel-inner',
17834                cn : []
17835             }];
17836         
17837             if(this.bullets  && !Roo.isTouch){
17838                 
17839                 var bullets = {
17840                     cls : 'carousel-bullets',
17841                     cn : []
17842                 };
17843                
17844                 if(this.bullets_cls){
17845                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17846                 }
17847                 
17848                 bullets.cn.push({
17849                     cls : 'clear'
17850                 });
17851                 
17852                 cfg.cn[0].cn.push(bullets);
17853             }
17854             
17855             if(this.showarrow){
17856                 cfg.cn[0].cn.push({
17857                     tag : 'div',
17858                     class : 'carousel-arrow',
17859                     cn : [
17860                         {
17861                             tag : 'div',
17862                             class : 'carousel-prev',
17863                             cn : [
17864                                 {
17865                                     tag : 'i',
17866                                     class : 'fa fa-chevron-left'
17867                                 }
17868                             ]
17869                         },
17870                         {
17871                             tag : 'div',
17872                             class : 'carousel-next',
17873                             cn : [
17874                                 {
17875                                     tag : 'i',
17876                                     class : 'fa fa-chevron-right'
17877                                 }
17878                             ]
17879                         }
17880                     ]
17881                 });
17882             }
17883             
17884         }
17885         
17886         return cfg;
17887     },
17888     
17889     initEvents:  function()
17890     {
17891 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17892 //            this.el.on("touchstart", this.onTouchStart, this);
17893 //        }
17894         
17895         if(this.autoslide){
17896             var _this = this;
17897             
17898             this.slideFn = window.setInterval(function() {
17899                 _this.showPanelNext();
17900             }, this.timer);
17901         }
17902         
17903         if(this.showarrow){
17904             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17905             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17906         }
17907         
17908         
17909     },
17910     
17911 //    onTouchStart : function(e, el, o)
17912 //    {
17913 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17914 //            return;
17915 //        }
17916 //        
17917 //        this.showPanelNext();
17918 //    },
17919     
17920     
17921     getChildContainer : function()
17922     {
17923         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17924     },
17925     
17926     /**
17927     * register a Navigation item
17928     * @param {Roo.bootstrap.NavItem} the navitem to add
17929     */
17930     register : function(item)
17931     {
17932         this.tabs.push( item);
17933         item.navId = this.navId; // not really needed..
17934         this.addBullet();
17935     
17936     },
17937     
17938     getActivePanel : function()
17939     {
17940         var r = false;
17941         Roo.each(this.tabs, function(t) {
17942             if (t.active) {
17943                 r = t;
17944                 return false;
17945             }
17946             return null;
17947         });
17948         return r;
17949         
17950     },
17951     getPanelByName : function(n)
17952     {
17953         var r = false;
17954         Roo.each(this.tabs, function(t) {
17955             if (t.tabId == n) {
17956                 r = t;
17957                 return false;
17958             }
17959             return null;
17960         });
17961         return r;
17962     },
17963     indexOfPanel : function(p)
17964     {
17965         var r = false;
17966         Roo.each(this.tabs, function(t,i) {
17967             if (t.tabId == p.tabId) {
17968                 r = i;
17969                 return false;
17970             }
17971             return null;
17972         });
17973         return r;
17974     },
17975     /**
17976      * show a specific panel
17977      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17978      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17979      */
17980     showPanel : function (pan)
17981     {
17982         if(this.transition || typeof(pan) == 'undefined'){
17983             Roo.log("waiting for the transitionend");
17984             return;
17985         }
17986         
17987         if (typeof(pan) == 'number') {
17988             pan = this.tabs[pan];
17989         }
17990         
17991         if (typeof(pan) == 'string') {
17992             pan = this.getPanelByName(pan);
17993         }
17994         
17995         var cur = this.getActivePanel();
17996         
17997         if(!pan || !cur){
17998             Roo.log('pan or acitve pan is undefined');
17999             return false;
18000         }
18001         
18002         if (pan.tabId == this.getActivePanel().tabId) {
18003             return true;
18004         }
18005         
18006         if (false === cur.fireEvent('beforedeactivate')) {
18007             return false;
18008         }
18009         
18010         if(this.bullets > 0 && !Roo.isTouch){
18011             this.setActiveBullet(this.indexOfPanel(pan));
18012         }
18013         
18014         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18015             
18016             this.transition = true;
18017             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18018             var lr = dir == 'next' ? 'left' : 'right';
18019             pan.el.addClass(dir); // or prev
18020             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18021             cur.el.addClass(lr); // or right
18022             pan.el.addClass(lr);
18023             
18024             var _this = this;
18025             cur.el.on('transitionend', function() {
18026                 Roo.log("trans end?");
18027                 
18028                 pan.el.removeClass([lr,dir]);
18029                 pan.setActive(true);
18030                 
18031                 cur.el.removeClass([lr]);
18032                 cur.setActive(false);
18033                 
18034                 _this.transition = false;
18035                 
18036             }, this, { single:  true } );
18037             
18038             return true;
18039         }
18040         
18041         cur.setActive(false);
18042         pan.setActive(true);
18043         
18044         return true;
18045         
18046     },
18047     showPanelNext : function()
18048     {
18049         var i = this.indexOfPanel(this.getActivePanel());
18050         
18051         if (i >= this.tabs.length - 1 && !this.autoslide) {
18052             return;
18053         }
18054         
18055         if (i >= this.tabs.length - 1 && this.autoslide) {
18056             i = -1;
18057         }
18058         
18059         this.showPanel(this.tabs[i+1]);
18060     },
18061     
18062     showPanelPrev : function()
18063     {
18064         var i = this.indexOfPanel(this.getActivePanel());
18065         
18066         if (i  < 1 && !this.autoslide) {
18067             return;
18068         }
18069         
18070         if (i < 1 && this.autoslide) {
18071             i = this.tabs.length;
18072         }
18073         
18074         this.showPanel(this.tabs[i-1]);
18075     },
18076     
18077     
18078     addBullet: function()
18079     {
18080         if(!this.bullets || Roo.isTouch){
18081             return;
18082         }
18083         var ctr = this.el.select('.carousel-bullets',true).first();
18084         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18085         var bullet = ctr.createChild({
18086             cls : 'bullet bullet-' + i
18087         },ctr.dom.lastChild);
18088         
18089         
18090         var _this = this;
18091         
18092         bullet.on('click', (function(e, el, o, ii, t){
18093
18094             e.preventDefault();
18095
18096             this.showPanel(ii);
18097
18098             if(this.autoslide && this.slideFn){
18099                 clearInterval(this.slideFn);
18100                 this.slideFn = window.setInterval(function() {
18101                     _this.showPanelNext();
18102                 }, this.timer);
18103             }
18104
18105         }).createDelegate(this, [i, bullet], true));
18106                 
18107         
18108     },
18109      
18110     setActiveBullet : function(i)
18111     {
18112         if(Roo.isTouch){
18113             return;
18114         }
18115         
18116         Roo.each(this.el.select('.bullet', true).elements, function(el){
18117             el.removeClass('selected');
18118         });
18119
18120         var bullet = this.el.select('.bullet-' + i, true).first();
18121         
18122         if(!bullet){
18123             return;
18124         }
18125         
18126         bullet.addClass('selected');
18127     }
18128     
18129     
18130   
18131 });
18132
18133  
18134
18135  
18136  
18137 Roo.apply(Roo.bootstrap.TabGroup, {
18138     
18139     groups: {},
18140      /**
18141     * register a Navigation Group
18142     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18143     */
18144     register : function(navgrp)
18145     {
18146         this.groups[navgrp.navId] = navgrp;
18147         
18148     },
18149     /**
18150     * fetch a Navigation Group based on the navigation ID
18151     * if one does not exist , it will get created.
18152     * @param {string} the navgroup to add
18153     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18154     */
18155     get: function(navId) {
18156         if (typeof(this.groups[navId]) == 'undefined') {
18157             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18158         }
18159         return this.groups[navId] ;
18160     }
18161     
18162     
18163     
18164 });
18165
18166  /*
18167  * - LGPL
18168  *
18169  * TabPanel
18170  * 
18171  */
18172
18173 /**
18174  * @class Roo.bootstrap.TabPanel
18175  * @extends Roo.bootstrap.Component
18176  * Bootstrap TabPanel class
18177  * @cfg {Boolean} active panel active
18178  * @cfg {String} html panel content
18179  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18180  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18181  * @cfg {String} href click to link..
18182  * 
18183  * 
18184  * @constructor
18185  * Create a new TabPanel
18186  * @param {Object} config The config object
18187  */
18188
18189 Roo.bootstrap.TabPanel = function(config){
18190     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18191     this.addEvents({
18192         /**
18193              * @event changed
18194              * Fires when the active status changes
18195              * @param {Roo.bootstrap.TabPanel} this
18196              * @param {Boolean} state the new state
18197             
18198          */
18199         'changed': true,
18200         /**
18201              * @event beforedeactivate
18202              * Fires before a tab is de-activated - can be used to do validation on a form.
18203              * @param {Roo.bootstrap.TabPanel} this
18204              * @return {Boolean} false if there is an error
18205             
18206          */
18207         'beforedeactivate': true
18208      });
18209     
18210     this.tabId = this.tabId || Roo.id();
18211   
18212 };
18213
18214 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18215     
18216     active: false,
18217     html: false,
18218     tabId: false,
18219     navId : false,
18220     href : '',
18221     
18222     getAutoCreate : function(){
18223         var cfg = {
18224             tag: 'div',
18225             // item is needed for carousel - not sure if it has any effect otherwise
18226             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18227             html: this.html || ''
18228         };
18229         
18230         if(this.active){
18231             cfg.cls += ' active';
18232         }
18233         
18234         if(this.tabId){
18235             cfg.tabId = this.tabId;
18236         }
18237         
18238         
18239         return cfg;
18240     },
18241     
18242     initEvents:  function()
18243     {
18244         var p = this.parent();
18245         
18246         this.navId = this.navId || p.navId;
18247         
18248         if (typeof(this.navId) != 'undefined') {
18249             // not really needed.. but just in case.. parent should be a NavGroup.
18250             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18251             
18252             tg.register(this);
18253             
18254             var i = tg.tabs.length - 1;
18255             
18256             if(this.active && tg.bullets > 0 && i < tg.bullets){
18257                 tg.setActiveBullet(i);
18258             }
18259         }
18260         
18261         this.el.on('click', this.onClick, this);
18262         
18263         if(Roo.isTouch){
18264             this.el.on("touchstart", this.onTouchStart, this);
18265             this.el.on("touchmove", this.onTouchMove, this);
18266             this.el.on("touchend", this.onTouchEnd, this);
18267         }
18268         
18269     },
18270     
18271     onRender : function(ct, position)
18272     {
18273         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18274     },
18275     
18276     setActive : function(state)
18277     {
18278         Roo.log("panel - set active " + this.tabId + "=" + state);
18279         
18280         this.active = state;
18281         if (!state) {
18282             this.el.removeClass('active');
18283             
18284         } else  if (!this.el.hasClass('active')) {
18285             this.el.addClass('active');
18286         }
18287         
18288         this.fireEvent('changed', this, state);
18289     },
18290     
18291     onClick : function(e)
18292     {
18293         e.preventDefault();
18294         
18295         if(!this.href.length){
18296             return;
18297         }
18298         
18299         window.location.href = this.href;
18300     },
18301     
18302     startX : 0,
18303     startY : 0,
18304     endX : 0,
18305     endY : 0,
18306     swiping : false,
18307     
18308     onTouchStart : function(e)
18309     {
18310         this.swiping = false;
18311         
18312         this.startX = e.browserEvent.touches[0].clientX;
18313         this.startY = e.browserEvent.touches[0].clientY;
18314     },
18315     
18316     onTouchMove : function(e)
18317     {
18318         this.swiping = true;
18319         
18320         this.endX = e.browserEvent.touches[0].clientX;
18321         this.endY = e.browserEvent.touches[0].clientY;
18322     },
18323     
18324     onTouchEnd : function(e)
18325     {
18326         if(!this.swiping){
18327             this.onClick(e);
18328             return;
18329         }
18330         
18331         var tabGroup = this.parent();
18332         
18333         if(this.endX > this.startX){ // swiping right
18334             tabGroup.showPanelPrev();
18335             return;
18336         }
18337         
18338         if(this.startX > this.endX){ // swiping left
18339             tabGroup.showPanelNext();
18340             return;
18341         }
18342     }
18343     
18344     
18345 });
18346  
18347
18348  
18349
18350  /*
18351  * - LGPL
18352  *
18353  * DateField
18354  * 
18355  */
18356
18357 /**
18358  * @class Roo.bootstrap.DateField
18359  * @extends Roo.bootstrap.Input
18360  * Bootstrap DateField class
18361  * @cfg {Number} weekStart default 0
18362  * @cfg {String} viewMode default empty, (months|years)
18363  * @cfg {String} minViewMode default empty, (months|years)
18364  * @cfg {Number} startDate default -Infinity
18365  * @cfg {Number} endDate default Infinity
18366  * @cfg {Boolean} todayHighlight default false
18367  * @cfg {Boolean} todayBtn default false
18368  * @cfg {Boolean} calendarWeeks default false
18369  * @cfg {Object} daysOfWeekDisabled default empty
18370  * @cfg {Boolean} singleMode default false (true | false)
18371  * 
18372  * @cfg {Boolean} keyboardNavigation default true
18373  * @cfg {String} language default en
18374  * 
18375  * @constructor
18376  * Create a new DateField
18377  * @param {Object} config The config object
18378  */
18379
18380 Roo.bootstrap.DateField = function(config){
18381     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18382      this.addEvents({
18383             /**
18384              * @event show
18385              * Fires when this field show.
18386              * @param {Roo.bootstrap.DateField} this
18387              * @param {Mixed} date The date value
18388              */
18389             show : true,
18390             /**
18391              * @event show
18392              * Fires when this field hide.
18393              * @param {Roo.bootstrap.DateField} this
18394              * @param {Mixed} date The date value
18395              */
18396             hide : true,
18397             /**
18398              * @event select
18399              * Fires when select a date.
18400              * @param {Roo.bootstrap.DateField} this
18401              * @param {Mixed} date The date value
18402              */
18403             select : true,
18404             /**
18405              * @event beforeselect
18406              * Fires when before select a date.
18407              * @param {Roo.bootstrap.DateField} this
18408              * @param {Mixed} date The date value
18409              */
18410             beforeselect : true
18411         });
18412 };
18413
18414 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18415     
18416     /**
18417      * @cfg {String} format
18418      * The default date format string which can be overriden for localization support.  The format must be
18419      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18420      */
18421     format : "m/d/y",
18422     /**
18423      * @cfg {String} altFormats
18424      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18425      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18426      */
18427     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18428     
18429     weekStart : 0,
18430     
18431     viewMode : '',
18432     
18433     minViewMode : '',
18434     
18435     todayHighlight : false,
18436     
18437     todayBtn: false,
18438     
18439     language: 'en',
18440     
18441     keyboardNavigation: true,
18442     
18443     calendarWeeks: false,
18444     
18445     startDate: -Infinity,
18446     
18447     endDate: Infinity,
18448     
18449     daysOfWeekDisabled: [],
18450     
18451     _events: [],
18452     
18453     singleMode : false,
18454     
18455     UTCDate: function()
18456     {
18457         return new Date(Date.UTC.apply(Date, arguments));
18458     },
18459     
18460     UTCToday: function()
18461     {
18462         var today = new Date();
18463         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18464     },
18465     
18466     getDate: function() {
18467             var d = this.getUTCDate();
18468             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18469     },
18470     
18471     getUTCDate: function() {
18472             return this.date;
18473     },
18474     
18475     setDate: function(d) {
18476             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18477     },
18478     
18479     setUTCDate: function(d) {
18480             this.date = d;
18481             this.setValue(this.formatDate(this.date));
18482     },
18483         
18484     onRender: function(ct, position)
18485     {
18486         
18487         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18488         
18489         this.language = this.language || 'en';
18490         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18491         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18492         
18493         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18494         this.format = this.format || 'm/d/y';
18495         this.isInline = false;
18496         this.isInput = true;
18497         this.component = this.el.select('.add-on', true).first() || false;
18498         this.component = (this.component && this.component.length === 0) ? false : this.component;
18499         this.hasInput = this.component && this.inputEl().length;
18500         
18501         if (typeof(this.minViewMode === 'string')) {
18502             switch (this.minViewMode) {
18503                 case 'months':
18504                     this.minViewMode = 1;
18505                     break;
18506                 case 'years':
18507                     this.minViewMode = 2;
18508                     break;
18509                 default:
18510                     this.minViewMode = 0;
18511                     break;
18512             }
18513         }
18514         
18515         if (typeof(this.viewMode === 'string')) {
18516             switch (this.viewMode) {
18517                 case 'months':
18518                     this.viewMode = 1;
18519                     break;
18520                 case 'years':
18521                     this.viewMode = 2;
18522                     break;
18523                 default:
18524                     this.viewMode = 0;
18525                     break;
18526             }
18527         }
18528                 
18529         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18530         
18531 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18532         
18533         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18534         
18535         this.picker().on('mousedown', this.onMousedown, this);
18536         this.picker().on('click', this.onClick, this);
18537         
18538         this.picker().addClass('datepicker-dropdown');
18539         
18540         this.startViewMode = this.viewMode;
18541         
18542         if(this.singleMode){
18543             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18544                 v.setVisibilityMode(Roo.Element.DISPLAY);
18545                 v.hide();
18546             });
18547             
18548             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18549                 v.setStyle('width', '189px');
18550             });
18551         }
18552         
18553         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18554             if(!this.calendarWeeks){
18555                 v.remove();
18556                 return;
18557             }
18558             
18559             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18560             v.attr('colspan', function(i, val){
18561                 return parseInt(val) + 1;
18562             });
18563         });
18564                         
18565         
18566         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18567         
18568         this.setStartDate(this.startDate);
18569         this.setEndDate(this.endDate);
18570         
18571         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18572         
18573         this.fillDow();
18574         this.fillMonths();
18575         this.update();
18576         this.showMode();
18577         
18578         if(this.isInline) {
18579             this.showPopup();
18580         }
18581     },
18582     
18583     picker : function()
18584     {
18585         return this.pickerEl;
18586 //        return this.el.select('.datepicker', true).first();
18587     },
18588     
18589     fillDow: function()
18590     {
18591         var dowCnt = this.weekStart;
18592         
18593         var dow = {
18594             tag: 'tr',
18595             cn: [
18596                 
18597             ]
18598         };
18599         
18600         if(this.calendarWeeks){
18601             dow.cn.push({
18602                 tag: 'th',
18603                 cls: 'cw',
18604                 html: '&nbsp;'
18605             })
18606         }
18607         
18608         while (dowCnt < this.weekStart + 7) {
18609             dow.cn.push({
18610                 tag: 'th',
18611                 cls: 'dow',
18612                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18613             });
18614         }
18615         
18616         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18617     },
18618     
18619     fillMonths: function()
18620     {    
18621         var i = 0;
18622         var months = this.picker().select('>.datepicker-months td', true).first();
18623         
18624         months.dom.innerHTML = '';
18625         
18626         while (i < 12) {
18627             var month = {
18628                 tag: 'span',
18629                 cls: 'month',
18630                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18631             };
18632             
18633             months.createChild(month);
18634         }
18635         
18636     },
18637     
18638     update: function()
18639     {
18640         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;
18641         
18642         if (this.date < this.startDate) {
18643             this.viewDate = new Date(this.startDate);
18644         } else if (this.date > this.endDate) {
18645             this.viewDate = new Date(this.endDate);
18646         } else {
18647             this.viewDate = new Date(this.date);
18648         }
18649         
18650         this.fill();
18651     },
18652     
18653     fill: function() 
18654     {
18655         var d = new Date(this.viewDate),
18656                 year = d.getUTCFullYear(),
18657                 month = d.getUTCMonth(),
18658                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18659                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18660                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18661                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18662                 currentDate = this.date && this.date.valueOf(),
18663                 today = this.UTCToday();
18664         
18665         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18666         
18667 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18668         
18669 //        this.picker.select('>tfoot th.today').
18670 //                                              .text(dates[this.language].today)
18671 //                                              .toggle(this.todayBtn !== false);
18672     
18673         this.updateNavArrows();
18674         this.fillMonths();
18675                                                 
18676         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18677         
18678         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18679          
18680         prevMonth.setUTCDate(day);
18681         
18682         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18683         
18684         var nextMonth = new Date(prevMonth);
18685         
18686         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18687         
18688         nextMonth = nextMonth.valueOf();
18689         
18690         var fillMonths = false;
18691         
18692         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18693         
18694         while(prevMonth.valueOf() <= nextMonth) {
18695             var clsName = '';
18696             
18697             if (prevMonth.getUTCDay() === this.weekStart) {
18698                 if(fillMonths){
18699                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18700                 }
18701                     
18702                 fillMonths = {
18703                     tag: 'tr',
18704                     cn: []
18705                 };
18706                 
18707                 if(this.calendarWeeks){
18708                     // ISO 8601: First week contains first thursday.
18709                     // ISO also states week starts on Monday, but we can be more abstract here.
18710                     var
18711                     // Start of current week: based on weekstart/current date
18712                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18713                     // Thursday of this week
18714                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18715                     // First Thursday of year, year from thursday
18716                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18717                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18718                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18719                     
18720                     fillMonths.cn.push({
18721                         tag: 'td',
18722                         cls: 'cw',
18723                         html: calWeek
18724                     });
18725                 }
18726             }
18727             
18728             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18729                 clsName += ' old';
18730             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18731                 clsName += ' new';
18732             }
18733             if (this.todayHighlight &&
18734                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18735                 prevMonth.getUTCMonth() == today.getMonth() &&
18736                 prevMonth.getUTCDate() == today.getDate()) {
18737                 clsName += ' today';
18738             }
18739             
18740             if (currentDate && prevMonth.valueOf() === currentDate) {
18741                 clsName += ' active';
18742             }
18743             
18744             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18745                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18746                     clsName += ' disabled';
18747             }
18748             
18749             fillMonths.cn.push({
18750                 tag: 'td',
18751                 cls: 'day ' + clsName,
18752                 html: prevMonth.getDate()
18753             });
18754             
18755             prevMonth.setDate(prevMonth.getDate()+1);
18756         }
18757           
18758         var currentYear = this.date && this.date.getUTCFullYear();
18759         var currentMonth = this.date && this.date.getUTCMonth();
18760         
18761         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18762         
18763         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18764             v.removeClass('active');
18765             
18766             if(currentYear === year && k === currentMonth){
18767                 v.addClass('active');
18768             }
18769             
18770             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18771                 v.addClass('disabled');
18772             }
18773             
18774         });
18775         
18776         
18777         year = parseInt(year/10, 10) * 10;
18778         
18779         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18780         
18781         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18782         
18783         year -= 1;
18784         for (var i = -1; i < 11; i++) {
18785             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18786                 tag: 'span',
18787                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18788                 html: year
18789             });
18790             
18791             year += 1;
18792         }
18793     },
18794     
18795     showMode: function(dir) 
18796     {
18797         if (dir) {
18798             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18799         }
18800         
18801         Roo.each(this.picker().select('>div',true).elements, function(v){
18802             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18803             v.hide();
18804         });
18805         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18806     },
18807     
18808     place: function()
18809     {
18810         if(this.isInline) {
18811             return;
18812         }
18813         
18814         this.picker().removeClass(['bottom', 'top']);
18815         
18816         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18817             /*
18818              * place to the top of element!
18819              *
18820              */
18821             
18822             this.picker().addClass('top');
18823             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18824             
18825             return;
18826         }
18827         
18828         this.picker().addClass('bottom');
18829         
18830         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18831     },
18832     
18833     parseDate : function(value)
18834     {
18835         if(!value || value instanceof Date){
18836             return value;
18837         }
18838         var v = Date.parseDate(value, this.format);
18839         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18840             v = Date.parseDate(value, 'Y-m-d');
18841         }
18842         if(!v && this.altFormats){
18843             if(!this.altFormatsArray){
18844                 this.altFormatsArray = this.altFormats.split("|");
18845             }
18846             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18847                 v = Date.parseDate(value, this.altFormatsArray[i]);
18848             }
18849         }
18850         return v;
18851     },
18852     
18853     formatDate : function(date, fmt)
18854     {   
18855         return (!date || !(date instanceof Date)) ?
18856         date : date.dateFormat(fmt || this.format);
18857     },
18858     
18859     onFocus : function()
18860     {
18861         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18862         this.showPopup();
18863     },
18864     
18865     onBlur : function()
18866     {
18867         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18868         
18869         var d = this.inputEl().getValue();
18870         
18871         this.setValue(d);
18872                 
18873         this.hidePopup();
18874     },
18875     
18876     showPopup : function()
18877     {
18878         this.picker().show();
18879         this.update();
18880         this.place();
18881         
18882         this.fireEvent('showpopup', this, this.date);
18883     },
18884     
18885     hidePopup : function()
18886     {
18887         if(this.isInline) {
18888             return;
18889         }
18890         this.picker().hide();
18891         this.viewMode = this.startViewMode;
18892         this.showMode();
18893         
18894         this.fireEvent('hidepopup', this, this.date);
18895         
18896     },
18897     
18898     onMousedown: function(e)
18899     {
18900         e.stopPropagation();
18901         e.preventDefault();
18902     },
18903     
18904     keyup: function(e)
18905     {
18906         Roo.bootstrap.DateField.superclass.keyup.call(this);
18907         this.update();
18908     },
18909
18910     setValue: function(v)
18911     {
18912         if(this.fireEvent('beforeselect', this, v) !== false){
18913             var d = new Date(this.parseDate(v) ).clearTime();
18914         
18915             if(isNaN(d.getTime())){
18916                 this.date = this.viewDate = '';
18917                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18918                 return;
18919             }
18920
18921             v = this.formatDate(d);
18922
18923             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18924
18925             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18926
18927             this.update();
18928
18929             this.fireEvent('select', this, this.date);
18930         }
18931     },
18932     
18933     getValue: function()
18934     {
18935         return this.formatDate(this.date);
18936     },
18937     
18938     fireKey: function(e)
18939     {
18940         if (!this.picker().isVisible()){
18941             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18942                 this.showPopup();
18943             }
18944             return;
18945         }
18946         
18947         var dateChanged = false,
18948         dir, day, month,
18949         newDate, newViewDate;
18950         
18951         switch(e.keyCode){
18952             case 27: // escape
18953                 this.hidePopup();
18954                 e.preventDefault();
18955                 break;
18956             case 37: // left
18957             case 39: // right
18958                 if (!this.keyboardNavigation) {
18959                     break;
18960                 }
18961                 dir = e.keyCode == 37 ? -1 : 1;
18962                 
18963                 if (e.ctrlKey){
18964                     newDate = this.moveYear(this.date, dir);
18965                     newViewDate = this.moveYear(this.viewDate, dir);
18966                 } else if (e.shiftKey){
18967                     newDate = this.moveMonth(this.date, dir);
18968                     newViewDate = this.moveMonth(this.viewDate, dir);
18969                 } else {
18970                     newDate = new Date(this.date);
18971                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18972                     newViewDate = new Date(this.viewDate);
18973                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18974                 }
18975                 if (this.dateWithinRange(newDate)){
18976                     this.date = newDate;
18977                     this.viewDate = newViewDate;
18978                     this.setValue(this.formatDate(this.date));
18979 //                    this.update();
18980                     e.preventDefault();
18981                     dateChanged = true;
18982                 }
18983                 break;
18984             case 38: // up
18985             case 40: // down
18986                 if (!this.keyboardNavigation) {
18987                     break;
18988                 }
18989                 dir = e.keyCode == 38 ? -1 : 1;
18990                 if (e.ctrlKey){
18991                     newDate = this.moveYear(this.date, dir);
18992                     newViewDate = this.moveYear(this.viewDate, dir);
18993                 } else if (e.shiftKey){
18994                     newDate = this.moveMonth(this.date, dir);
18995                     newViewDate = this.moveMonth(this.viewDate, dir);
18996                 } else {
18997                     newDate = new Date(this.date);
18998                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18999                     newViewDate = new Date(this.viewDate);
19000                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19001                 }
19002                 if (this.dateWithinRange(newDate)){
19003                     this.date = newDate;
19004                     this.viewDate = newViewDate;
19005                     this.setValue(this.formatDate(this.date));
19006 //                    this.update();
19007                     e.preventDefault();
19008                     dateChanged = true;
19009                 }
19010                 break;
19011             case 13: // enter
19012                 this.setValue(this.formatDate(this.date));
19013                 this.hidePopup();
19014                 e.preventDefault();
19015                 break;
19016             case 9: // tab
19017                 this.setValue(this.formatDate(this.date));
19018                 this.hidePopup();
19019                 break;
19020             case 16: // shift
19021             case 17: // ctrl
19022             case 18: // alt
19023                 break;
19024             default :
19025                 this.hide();
19026                 
19027         }
19028     },
19029     
19030     
19031     onClick: function(e) 
19032     {
19033         e.stopPropagation();
19034         e.preventDefault();
19035         
19036         var target = e.getTarget();
19037         
19038         if(target.nodeName.toLowerCase() === 'i'){
19039             target = Roo.get(target).dom.parentNode;
19040         }
19041         
19042         var nodeName = target.nodeName;
19043         var className = target.className;
19044         var html = target.innerHTML;
19045         //Roo.log(nodeName);
19046         
19047         switch(nodeName.toLowerCase()) {
19048             case 'th':
19049                 switch(className) {
19050                     case 'switch':
19051                         this.showMode(1);
19052                         break;
19053                     case 'prev':
19054                     case 'next':
19055                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19056                         switch(this.viewMode){
19057                                 case 0:
19058                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19059                                         break;
19060                                 case 1:
19061                                 case 2:
19062                                         this.viewDate = this.moveYear(this.viewDate, dir);
19063                                         break;
19064                         }
19065                         this.fill();
19066                         break;
19067                     case 'today':
19068                         var date = new Date();
19069                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19070 //                        this.fill()
19071                         this.setValue(this.formatDate(this.date));
19072                         
19073                         this.hidePopup();
19074                         break;
19075                 }
19076                 break;
19077             case 'span':
19078                 if (className.indexOf('disabled') < 0) {
19079                     this.viewDate.setUTCDate(1);
19080                     if (className.indexOf('month') > -1) {
19081                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19082                     } else {
19083                         var year = parseInt(html, 10) || 0;
19084                         this.viewDate.setUTCFullYear(year);
19085                         
19086                     }
19087                     
19088                     if(this.singleMode){
19089                         this.setValue(this.formatDate(this.viewDate));
19090                         this.hidePopup();
19091                         return;
19092                     }
19093                     
19094                     this.showMode(-1);
19095                     this.fill();
19096                 }
19097                 break;
19098                 
19099             case 'td':
19100                 //Roo.log(className);
19101                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19102                     var day = parseInt(html, 10) || 1;
19103                     var year = this.viewDate.getUTCFullYear(),
19104                         month = this.viewDate.getUTCMonth();
19105
19106                     if (className.indexOf('old') > -1) {
19107                         if(month === 0 ){
19108                             month = 11;
19109                             year -= 1;
19110                         }else{
19111                             month -= 1;
19112                         }
19113                     } else if (className.indexOf('new') > -1) {
19114                         if (month == 11) {
19115                             month = 0;
19116                             year += 1;
19117                         } else {
19118                             month += 1;
19119                         }
19120                     }
19121                     //Roo.log([year,month,day]);
19122                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19123                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19124 //                    this.fill();
19125                     //Roo.log(this.formatDate(this.date));
19126                     this.setValue(this.formatDate(this.date));
19127                     this.hidePopup();
19128                 }
19129                 break;
19130         }
19131     },
19132     
19133     setStartDate: function(startDate)
19134     {
19135         this.startDate = startDate || -Infinity;
19136         if (this.startDate !== -Infinity) {
19137             this.startDate = this.parseDate(this.startDate);
19138         }
19139         this.update();
19140         this.updateNavArrows();
19141     },
19142
19143     setEndDate: function(endDate)
19144     {
19145         this.endDate = endDate || Infinity;
19146         if (this.endDate !== Infinity) {
19147             this.endDate = this.parseDate(this.endDate);
19148         }
19149         this.update();
19150         this.updateNavArrows();
19151     },
19152     
19153     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19154     {
19155         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19156         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19157             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19158         }
19159         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19160             return parseInt(d, 10);
19161         });
19162         this.update();
19163         this.updateNavArrows();
19164     },
19165     
19166     updateNavArrows: function() 
19167     {
19168         if(this.singleMode){
19169             return;
19170         }
19171         
19172         var d = new Date(this.viewDate),
19173         year = d.getUTCFullYear(),
19174         month = d.getUTCMonth();
19175         
19176         Roo.each(this.picker().select('.prev', true).elements, function(v){
19177             v.show();
19178             switch (this.viewMode) {
19179                 case 0:
19180
19181                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19182                         v.hide();
19183                     }
19184                     break;
19185                 case 1:
19186                 case 2:
19187                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19188                         v.hide();
19189                     }
19190                     break;
19191             }
19192         });
19193         
19194         Roo.each(this.picker().select('.next', true).elements, function(v){
19195             v.show();
19196             switch (this.viewMode) {
19197                 case 0:
19198
19199                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19200                         v.hide();
19201                     }
19202                     break;
19203                 case 1:
19204                 case 2:
19205                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19206                         v.hide();
19207                     }
19208                     break;
19209             }
19210         })
19211     },
19212     
19213     moveMonth: function(date, dir)
19214     {
19215         if (!dir) {
19216             return date;
19217         }
19218         var new_date = new Date(date.valueOf()),
19219         day = new_date.getUTCDate(),
19220         month = new_date.getUTCMonth(),
19221         mag = Math.abs(dir),
19222         new_month, test;
19223         dir = dir > 0 ? 1 : -1;
19224         if (mag == 1){
19225             test = dir == -1
19226             // If going back one month, make sure month is not current month
19227             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19228             ? function(){
19229                 return new_date.getUTCMonth() == month;
19230             }
19231             // If going forward one month, make sure month is as expected
19232             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19233             : function(){
19234                 return new_date.getUTCMonth() != new_month;
19235             };
19236             new_month = month + dir;
19237             new_date.setUTCMonth(new_month);
19238             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19239             if (new_month < 0 || new_month > 11) {
19240                 new_month = (new_month + 12) % 12;
19241             }
19242         } else {
19243             // For magnitudes >1, move one month at a time...
19244             for (var i=0; i<mag; i++) {
19245                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19246                 new_date = this.moveMonth(new_date, dir);
19247             }
19248             // ...then reset the day, keeping it in the new month
19249             new_month = new_date.getUTCMonth();
19250             new_date.setUTCDate(day);
19251             test = function(){
19252                 return new_month != new_date.getUTCMonth();
19253             };
19254         }
19255         // Common date-resetting loop -- if date is beyond end of month, make it
19256         // end of month
19257         while (test()){
19258             new_date.setUTCDate(--day);
19259             new_date.setUTCMonth(new_month);
19260         }
19261         return new_date;
19262     },
19263
19264     moveYear: function(date, dir)
19265     {
19266         return this.moveMonth(date, dir*12);
19267     },
19268
19269     dateWithinRange: function(date)
19270     {
19271         return date >= this.startDate && date <= this.endDate;
19272     },
19273
19274     
19275     remove: function() 
19276     {
19277         this.picker().remove();
19278     },
19279     
19280     validateValue : function(value)
19281     {
19282         if(this.getVisibilityEl().hasClass('hidden')){
19283             return true;
19284         }
19285         
19286         if(value.length < 1)  {
19287             if(this.allowBlank){
19288                 return true;
19289             }
19290             return false;
19291         }
19292         
19293         if(value.length < this.minLength){
19294             return false;
19295         }
19296         if(value.length > this.maxLength){
19297             return false;
19298         }
19299         if(this.vtype){
19300             var vt = Roo.form.VTypes;
19301             if(!vt[this.vtype](value, this)){
19302                 return false;
19303             }
19304         }
19305         if(typeof this.validator == "function"){
19306             var msg = this.validator(value);
19307             if(msg !== true){
19308                 return false;
19309             }
19310         }
19311         
19312         if(this.regex && !this.regex.test(value)){
19313             return false;
19314         }
19315         
19316         if(typeof(this.parseDate(value)) == 'undefined'){
19317             return false;
19318         }
19319         
19320         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19321             return false;
19322         }      
19323         
19324         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19325             return false;
19326         } 
19327         
19328         
19329         return true;
19330     },
19331     
19332     reset : function()
19333     {
19334         this.date = this.viewDate = '';
19335         
19336         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19337     }
19338    
19339 });
19340
19341 Roo.apply(Roo.bootstrap.DateField,  {
19342     
19343     head : {
19344         tag: 'thead',
19345         cn: [
19346         {
19347             tag: 'tr',
19348             cn: [
19349             {
19350                 tag: 'th',
19351                 cls: 'prev',
19352                 html: '<i class="fa fa-arrow-left"/>'
19353             },
19354             {
19355                 tag: 'th',
19356                 cls: 'switch',
19357                 colspan: '5'
19358             },
19359             {
19360                 tag: 'th',
19361                 cls: 'next',
19362                 html: '<i class="fa fa-arrow-right"/>'
19363             }
19364
19365             ]
19366         }
19367         ]
19368     },
19369     
19370     content : {
19371         tag: 'tbody',
19372         cn: [
19373         {
19374             tag: 'tr',
19375             cn: [
19376             {
19377                 tag: 'td',
19378                 colspan: '7'
19379             }
19380             ]
19381         }
19382         ]
19383     },
19384     
19385     footer : {
19386         tag: 'tfoot',
19387         cn: [
19388         {
19389             tag: 'tr',
19390             cn: [
19391             {
19392                 tag: 'th',
19393                 colspan: '7',
19394                 cls: 'today'
19395             }
19396                     
19397             ]
19398         }
19399         ]
19400     },
19401     
19402     dates:{
19403         en: {
19404             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19405             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19406             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19407             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19408             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19409             today: "Today"
19410         }
19411     },
19412     
19413     modes: [
19414     {
19415         clsName: 'days',
19416         navFnc: 'Month',
19417         navStep: 1
19418     },
19419     {
19420         clsName: 'months',
19421         navFnc: 'FullYear',
19422         navStep: 1
19423     },
19424     {
19425         clsName: 'years',
19426         navFnc: 'FullYear',
19427         navStep: 10
19428     }]
19429 });
19430
19431 Roo.apply(Roo.bootstrap.DateField,  {
19432   
19433     template : {
19434         tag: 'div',
19435         cls: 'datepicker dropdown-menu roo-dynamic',
19436         cn: [
19437         {
19438             tag: 'div',
19439             cls: 'datepicker-days',
19440             cn: [
19441             {
19442                 tag: 'table',
19443                 cls: 'table-condensed',
19444                 cn:[
19445                 Roo.bootstrap.DateField.head,
19446                 {
19447                     tag: 'tbody'
19448                 },
19449                 Roo.bootstrap.DateField.footer
19450                 ]
19451             }
19452             ]
19453         },
19454         {
19455             tag: 'div',
19456             cls: 'datepicker-months',
19457             cn: [
19458             {
19459                 tag: 'table',
19460                 cls: 'table-condensed',
19461                 cn:[
19462                 Roo.bootstrap.DateField.head,
19463                 Roo.bootstrap.DateField.content,
19464                 Roo.bootstrap.DateField.footer
19465                 ]
19466             }
19467             ]
19468         },
19469         {
19470             tag: 'div',
19471             cls: 'datepicker-years',
19472             cn: [
19473             {
19474                 tag: 'table',
19475                 cls: 'table-condensed',
19476                 cn:[
19477                 Roo.bootstrap.DateField.head,
19478                 Roo.bootstrap.DateField.content,
19479                 Roo.bootstrap.DateField.footer
19480                 ]
19481             }
19482             ]
19483         }
19484         ]
19485     }
19486 });
19487
19488  
19489
19490  /*
19491  * - LGPL
19492  *
19493  * TimeField
19494  * 
19495  */
19496
19497 /**
19498  * @class Roo.bootstrap.TimeField
19499  * @extends Roo.bootstrap.Input
19500  * Bootstrap DateField class
19501  * 
19502  * 
19503  * @constructor
19504  * Create a new TimeField
19505  * @param {Object} config The config object
19506  */
19507
19508 Roo.bootstrap.TimeField = function(config){
19509     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19510     this.addEvents({
19511             /**
19512              * @event show
19513              * Fires when this field show.
19514              * @param {Roo.bootstrap.DateField} thisthis
19515              * @param {Mixed} date The date value
19516              */
19517             show : true,
19518             /**
19519              * @event show
19520              * Fires when this field hide.
19521              * @param {Roo.bootstrap.DateField} this
19522              * @param {Mixed} date The date value
19523              */
19524             hide : true,
19525             /**
19526              * @event select
19527              * Fires when select a date.
19528              * @param {Roo.bootstrap.DateField} this
19529              * @param {Mixed} date The date value
19530              */
19531             select : true
19532         });
19533 };
19534
19535 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19536     
19537     /**
19538      * @cfg {String} format
19539      * The default time format string which can be overriden for localization support.  The format must be
19540      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19541      */
19542     format : "H:i",
19543        
19544     onRender: function(ct, position)
19545     {
19546         
19547         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19548                 
19549         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19550         
19551         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19552         
19553         this.pop = this.picker().select('>.datepicker-time',true).first();
19554         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19555         
19556         this.picker().on('mousedown', this.onMousedown, this);
19557         this.picker().on('click', this.onClick, this);
19558         
19559         this.picker().addClass('datepicker-dropdown');
19560     
19561         this.fillTime();
19562         this.update();
19563             
19564         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19565         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19566         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19567         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19568         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19569         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19570
19571     },
19572     
19573     fireKey: function(e){
19574         if (!this.picker().isVisible()){
19575             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19576                 this.show();
19577             }
19578             return;
19579         }
19580
19581         e.preventDefault();
19582         
19583         switch(e.keyCode){
19584             case 27: // escape
19585                 this.hide();
19586                 break;
19587             case 37: // left
19588             case 39: // right
19589                 this.onTogglePeriod();
19590                 break;
19591             case 38: // up
19592                 this.onIncrementMinutes();
19593                 break;
19594             case 40: // down
19595                 this.onDecrementMinutes();
19596                 break;
19597             case 13: // enter
19598             case 9: // tab
19599                 this.setTime();
19600                 break;
19601         }
19602     },
19603     
19604     onClick: function(e) {
19605         e.stopPropagation();
19606         e.preventDefault();
19607     },
19608     
19609     picker : function()
19610     {
19611         return this.el.select('.datepicker', true).first();
19612     },
19613     
19614     fillTime: function()
19615     {    
19616         var time = this.pop.select('tbody', true).first();
19617         
19618         time.dom.innerHTML = '';
19619         
19620         time.createChild({
19621             tag: 'tr',
19622             cn: [
19623                 {
19624                     tag: 'td',
19625                     cn: [
19626                         {
19627                             tag: 'a',
19628                             href: '#',
19629                             cls: 'btn',
19630                             cn: [
19631                                 {
19632                                     tag: 'span',
19633                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19634                                 }
19635                             ]
19636                         } 
19637                     ]
19638                 },
19639                 {
19640                     tag: 'td',
19641                     cls: 'separator'
19642                 },
19643                 {
19644                     tag: 'td',
19645                     cn: [
19646                         {
19647                             tag: 'a',
19648                             href: '#',
19649                             cls: 'btn',
19650                             cn: [
19651                                 {
19652                                     tag: 'span',
19653                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19654                                 }
19655                             ]
19656                         }
19657                     ]
19658                 },
19659                 {
19660                     tag: 'td',
19661                     cls: 'separator'
19662                 }
19663             ]
19664         });
19665         
19666         time.createChild({
19667             tag: 'tr',
19668             cn: [
19669                 {
19670                     tag: 'td',
19671                     cn: [
19672                         {
19673                             tag: 'span',
19674                             cls: 'timepicker-hour',
19675                             html: '00'
19676                         }  
19677                     ]
19678                 },
19679                 {
19680                     tag: 'td',
19681                     cls: 'separator',
19682                     html: ':'
19683                 },
19684                 {
19685                     tag: 'td',
19686                     cn: [
19687                         {
19688                             tag: 'span',
19689                             cls: 'timepicker-minute',
19690                             html: '00'
19691                         }  
19692                     ]
19693                 },
19694                 {
19695                     tag: 'td',
19696                     cls: 'separator'
19697                 },
19698                 {
19699                     tag: 'td',
19700                     cn: [
19701                         {
19702                             tag: 'button',
19703                             type: 'button',
19704                             cls: 'btn btn-primary period',
19705                             html: 'AM'
19706                             
19707                         }
19708                     ]
19709                 }
19710             ]
19711         });
19712         
19713         time.createChild({
19714             tag: 'tr',
19715             cn: [
19716                 {
19717                     tag: 'td',
19718                     cn: [
19719                         {
19720                             tag: 'a',
19721                             href: '#',
19722                             cls: 'btn',
19723                             cn: [
19724                                 {
19725                                     tag: 'span',
19726                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19727                                 }
19728                             ]
19729                         }
19730                     ]
19731                 },
19732                 {
19733                     tag: 'td',
19734                     cls: 'separator'
19735                 },
19736                 {
19737                     tag: 'td',
19738                     cn: [
19739                         {
19740                             tag: 'a',
19741                             href: '#',
19742                             cls: 'btn',
19743                             cn: [
19744                                 {
19745                                     tag: 'span',
19746                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19747                                 }
19748                             ]
19749                         }
19750                     ]
19751                 },
19752                 {
19753                     tag: 'td',
19754                     cls: 'separator'
19755                 }
19756             ]
19757         });
19758         
19759     },
19760     
19761     update: function()
19762     {
19763         
19764         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19765         
19766         this.fill();
19767     },
19768     
19769     fill: function() 
19770     {
19771         var hours = this.time.getHours();
19772         var minutes = this.time.getMinutes();
19773         var period = 'AM';
19774         
19775         if(hours > 11){
19776             period = 'PM';
19777         }
19778         
19779         if(hours == 0){
19780             hours = 12;
19781         }
19782         
19783         
19784         if(hours > 12){
19785             hours = hours - 12;
19786         }
19787         
19788         if(hours < 10){
19789             hours = '0' + hours;
19790         }
19791         
19792         if(minutes < 10){
19793             minutes = '0' + minutes;
19794         }
19795         
19796         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19797         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19798         this.pop.select('button', true).first().dom.innerHTML = period;
19799         
19800     },
19801     
19802     place: function()
19803     {   
19804         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19805         
19806         var cls = ['bottom'];
19807         
19808         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19809             cls.pop();
19810             cls.push('top');
19811         }
19812         
19813         cls.push('right');
19814         
19815         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19816             cls.pop();
19817             cls.push('left');
19818         }
19819         
19820         this.picker().addClass(cls.join('-'));
19821         
19822         var _this = this;
19823         
19824         Roo.each(cls, function(c){
19825             if(c == 'bottom'){
19826                 _this.picker().setTop(_this.inputEl().getHeight());
19827                 return;
19828             }
19829             if(c == 'top'){
19830                 _this.picker().setTop(0 - _this.picker().getHeight());
19831                 return;
19832             }
19833             
19834             if(c == 'left'){
19835                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19836                 return;
19837             }
19838             if(c == 'right'){
19839                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19840                 return;
19841             }
19842         });
19843         
19844     },
19845   
19846     onFocus : function()
19847     {
19848         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19849         this.show();
19850     },
19851     
19852     onBlur : function()
19853     {
19854         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19855         this.hide();
19856     },
19857     
19858     show : function()
19859     {
19860         this.picker().show();
19861         this.pop.show();
19862         this.update();
19863         this.place();
19864         
19865         this.fireEvent('show', this, this.date);
19866     },
19867     
19868     hide : function()
19869     {
19870         this.picker().hide();
19871         this.pop.hide();
19872         
19873         this.fireEvent('hide', this, this.date);
19874     },
19875     
19876     setTime : function()
19877     {
19878         this.hide();
19879         this.setValue(this.time.format(this.format));
19880         
19881         this.fireEvent('select', this, this.date);
19882         
19883         
19884     },
19885     
19886     onMousedown: function(e){
19887         e.stopPropagation();
19888         e.preventDefault();
19889     },
19890     
19891     onIncrementHours: function()
19892     {
19893         Roo.log('onIncrementHours');
19894         this.time = this.time.add(Date.HOUR, 1);
19895         this.update();
19896         
19897     },
19898     
19899     onDecrementHours: function()
19900     {
19901         Roo.log('onDecrementHours');
19902         this.time = this.time.add(Date.HOUR, -1);
19903         this.update();
19904     },
19905     
19906     onIncrementMinutes: function()
19907     {
19908         Roo.log('onIncrementMinutes');
19909         this.time = this.time.add(Date.MINUTE, 1);
19910         this.update();
19911     },
19912     
19913     onDecrementMinutes: function()
19914     {
19915         Roo.log('onDecrementMinutes');
19916         this.time = this.time.add(Date.MINUTE, -1);
19917         this.update();
19918     },
19919     
19920     onTogglePeriod: function()
19921     {
19922         Roo.log('onTogglePeriod');
19923         this.time = this.time.add(Date.HOUR, 12);
19924         this.update();
19925     }
19926     
19927    
19928 });
19929
19930 Roo.apply(Roo.bootstrap.TimeField,  {
19931     
19932     content : {
19933         tag: 'tbody',
19934         cn: [
19935             {
19936                 tag: 'tr',
19937                 cn: [
19938                 {
19939                     tag: 'td',
19940                     colspan: '7'
19941                 }
19942                 ]
19943             }
19944         ]
19945     },
19946     
19947     footer : {
19948         tag: 'tfoot',
19949         cn: [
19950             {
19951                 tag: 'tr',
19952                 cn: [
19953                 {
19954                     tag: 'th',
19955                     colspan: '7',
19956                     cls: '',
19957                     cn: [
19958                         {
19959                             tag: 'button',
19960                             cls: 'btn btn-info ok',
19961                             html: 'OK'
19962                         }
19963                     ]
19964                 }
19965
19966                 ]
19967             }
19968         ]
19969     }
19970 });
19971
19972 Roo.apply(Roo.bootstrap.TimeField,  {
19973   
19974     template : {
19975         tag: 'div',
19976         cls: 'datepicker dropdown-menu',
19977         cn: [
19978             {
19979                 tag: 'div',
19980                 cls: 'datepicker-time',
19981                 cn: [
19982                 {
19983                     tag: 'table',
19984                     cls: 'table-condensed',
19985                     cn:[
19986                     Roo.bootstrap.TimeField.content,
19987                     Roo.bootstrap.TimeField.footer
19988                     ]
19989                 }
19990                 ]
19991             }
19992         ]
19993     }
19994 });
19995
19996  
19997
19998  /*
19999  * - LGPL
20000  *
20001  * MonthField
20002  * 
20003  */
20004
20005 /**
20006  * @class Roo.bootstrap.MonthField
20007  * @extends Roo.bootstrap.Input
20008  * Bootstrap MonthField class
20009  * 
20010  * @cfg {String} language default en
20011  * 
20012  * @constructor
20013  * Create a new MonthField
20014  * @param {Object} config The config object
20015  */
20016
20017 Roo.bootstrap.MonthField = function(config){
20018     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20019     
20020     this.addEvents({
20021         /**
20022          * @event show
20023          * Fires when this field show.
20024          * @param {Roo.bootstrap.MonthField} this
20025          * @param {Mixed} date The date value
20026          */
20027         show : true,
20028         /**
20029          * @event show
20030          * Fires when this field hide.
20031          * @param {Roo.bootstrap.MonthField} this
20032          * @param {Mixed} date The date value
20033          */
20034         hide : true,
20035         /**
20036          * @event select
20037          * Fires when select a date.
20038          * @param {Roo.bootstrap.MonthField} this
20039          * @param {String} oldvalue The old value
20040          * @param {String} newvalue The new value
20041          */
20042         select : true
20043     });
20044 };
20045
20046 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20047     
20048     onRender: function(ct, position)
20049     {
20050         
20051         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20052         
20053         this.language = this.language || 'en';
20054         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20055         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20056         
20057         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20058         this.isInline = false;
20059         this.isInput = true;
20060         this.component = this.el.select('.add-on', true).first() || false;
20061         this.component = (this.component && this.component.length === 0) ? false : this.component;
20062         this.hasInput = this.component && this.inputEL().length;
20063         
20064         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20065         
20066         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20067         
20068         this.picker().on('mousedown', this.onMousedown, this);
20069         this.picker().on('click', this.onClick, this);
20070         
20071         this.picker().addClass('datepicker-dropdown');
20072         
20073         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20074             v.setStyle('width', '189px');
20075         });
20076         
20077         this.fillMonths();
20078         
20079         this.update();
20080         
20081         if(this.isInline) {
20082             this.show();
20083         }
20084         
20085     },
20086     
20087     setValue: function(v, suppressEvent)
20088     {   
20089         var o = this.getValue();
20090         
20091         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20092         
20093         this.update();
20094
20095         if(suppressEvent !== true){
20096             this.fireEvent('select', this, o, v);
20097         }
20098         
20099     },
20100     
20101     getValue: function()
20102     {
20103         return this.value;
20104     },
20105     
20106     onClick: function(e) 
20107     {
20108         e.stopPropagation();
20109         e.preventDefault();
20110         
20111         var target = e.getTarget();
20112         
20113         if(target.nodeName.toLowerCase() === 'i'){
20114             target = Roo.get(target).dom.parentNode;
20115         }
20116         
20117         var nodeName = target.nodeName;
20118         var className = target.className;
20119         var html = target.innerHTML;
20120         
20121         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20122             return;
20123         }
20124         
20125         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20126         
20127         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20128         
20129         this.hide();
20130                         
20131     },
20132     
20133     picker : function()
20134     {
20135         return this.pickerEl;
20136     },
20137     
20138     fillMonths: function()
20139     {    
20140         var i = 0;
20141         var months = this.picker().select('>.datepicker-months td', true).first();
20142         
20143         months.dom.innerHTML = '';
20144         
20145         while (i < 12) {
20146             var month = {
20147                 tag: 'span',
20148                 cls: 'month',
20149                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20150             };
20151             
20152             months.createChild(month);
20153         }
20154         
20155     },
20156     
20157     update: function()
20158     {
20159         var _this = this;
20160         
20161         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20162             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20163         }
20164         
20165         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20166             e.removeClass('active');
20167             
20168             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20169                 e.addClass('active');
20170             }
20171         })
20172     },
20173     
20174     place: function()
20175     {
20176         if(this.isInline) {
20177             return;
20178         }
20179         
20180         this.picker().removeClass(['bottom', 'top']);
20181         
20182         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20183             /*
20184              * place to the top of element!
20185              *
20186              */
20187             
20188             this.picker().addClass('top');
20189             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20190             
20191             return;
20192         }
20193         
20194         this.picker().addClass('bottom');
20195         
20196         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20197     },
20198     
20199     onFocus : function()
20200     {
20201         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20202         this.show();
20203     },
20204     
20205     onBlur : function()
20206     {
20207         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20208         
20209         var d = this.inputEl().getValue();
20210         
20211         this.setValue(d);
20212                 
20213         this.hide();
20214     },
20215     
20216     show : function()
20217     {
20218         this.picker().show();
20219         this.picker().select('>.datepicker-months', true).first().show();
20220         this.update();
20221         this.place();
20222         
20223         this.fireEvent('show', this, this.date);
20224     },
20225     
20226     hide : function()
20227     {
20228         if(this.isInline) {
20229             return;
20230         }
20231         this.picker().hide();
20232         this.fireEvent('hide', this, this.date);
20233         
20234     },
20235     
20236     onMousedown: function(e)
20237     {
20238         e.stopPropagation();
20239         e.preventDefault();
20240     },
20241     
20242     keyup: function(e)
20243     {
20244         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20245         this.update();
20246     },
20247
20248     fireKey: function(e)
20249     {
20250         if (!this.picker().isVisible()){
20251             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20252                 this.show();
20253             }
20254             return;
20255         }
20256         
20257         var dir;
20258         
20259         switch(e.keyCode){
20260             case 27: // escape
20261                 this.hide();
20262                 e.preventDefault();
20263                 break;
20264             case 37: // left
20265             case 39: // right
20266                 dir = e.keyCode == 37 ? -1 : 1;
20267                 
20268                 this.vIndex = this.vIndex + dir;
20269                 
20270                 if(this.vIndex < 0){
20271                     this.vIndex = 0;
20272                 }
20273                 
20274                 if(this.vIndex > 11){
20275                     this.vIndex = 11;
20276                 }
20277                 
20278                 if(isNaN(this.vIndex)){
20279                     this.vIndex = 0;
20280                 }
20281                 
20282                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20283                 
20284                 break;
20285             case 38: // up
20286             case 40: // down
20287                 
20288                 dir = e.keyCode == 38 ? -1 : 1;
20289                 
20290                 this.vIndex = this.vIndex + dir * 4;
20291                 
20292                 if(this.vIndex < 0){
20293                     this.vIndex = 0;
20294                 }
20295                 
20296                 if(this.vIndex > 11){
20297                     this.vIndex = 11;
20298                 }
20299                 
20300                 if(isNaN(this.vIndex)){
20301                     this.vIndex = 0;
20302                 }
20303                 
20304                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20305                 break;
20306                 
20307             case 13: // enter
20308                 
20309                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20310                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20311                 }
20312                 
20313                 this.hide();
20314                 e.preventDefault();
20315                 break;
20316             case 9: // tab
20317                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20318                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20319                 }
20320                 this.hide();
20321                 break;
20322             case 16: // shift
20323             case 17: // ctrl
20324             case 18: // alt
20325                 break;
20326             default :
20327                 this.hide();
20328                 
20329         }
20330     },
20331     
20332     remove: function() 
20333     {
20334         this.picker().remove();
20335     }
20336    
20337 });
20338
20339 Roo.apply(Roo.bootstrap.MonthField,  {
20340     
20341     content : {
20342         tag: 'tbody',
20343         cn: [
20344         {
20345             tag: 'tr',
20346             cn: [
20347             {
20348                 tag: 'td',
20349                 colspan: '7'
20350             }
20351             ]
20352         }
20353         ]
20354     },
20355     
20356     dates:{
20357         en: {
20358             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20359             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20360         }
20361     }
20362 });
20363
20364 Roo.apply(Roo.bootstrap.MonthField,  {
20365   
20366     template : {
20367         tag: 'div',
20368         cls: 'datepicker dropdown-menu roo-dynamic',
20369         cn: [
20370             {
20371                 tag: 'div',
20372                 cls: 'datepicker-months',
20373                 cn: [
20374                 {
20375                     tag: 'table',
20376                     cls: 'table-condensed',
20377                     cn:[
20378                         Roo.bootstrap.DateField.content
20379                     ]
20380                 }
20381                 ]
20382             }
20383         ]
20384     }
20385 });
20386
20387  
20388
20389  
20390  /*
20391  * - LGPL
20392  *
20393  * CheckBox
20394  * 
20395  */
20396
20397 /**
20398  * @class Roo.bootstrap.CheckBox
20399  * @extends Roo.bootstrap.Input
20400  * Bootstrap CheckBox class
20401  * 
20402  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20403  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20404  * @cfg {String} boxLabel The text that appears beside the checkbox
20405  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20406  * @cfg {Boolean} checked initnal the element
20407  * @cfg {Boolean} inline inline the element (default false)
20408  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20409  * @cfg {String} tooltip label tooltip
20410  * 
20411  * @constructor
20412  * Create a new CheckBox
20413  * @param {Object} config The config object
20414  */
20415
20416 Roo.bootstrap.CheckBox = function(config){
20417     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20418    
20419     this.addEvents({
20420         /**
20421         * @event check
20422         * Fires when the element is checked or unchecked.
20423         * @param {Roo.bootstrap.CheckBox} this This input
20424         * @param {Boolean} checked The new checked value
20425         */
20426        check : true,
20427        /**
20428         * @event click
20429         * Fires when the element is click.
20430         * @param {Roo.bootstrap.CheckBox} this This input
20431         */
20432        click : true
20433     });
20434     
20435 };
20436
20437 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20438   
20439     inputType: 'checkbox',
20440     inputValue: 1,
20441     valueOff: 0,
20442     boxLabel: false,
20443     checked: false,
20444     weight : false,
20445     inline: false,
20446     tooltip : '',
20447     
20448     getAutoCreate : function()
20449     {
20450         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20451         
20452         var id = Roo.id();
20453         
20454         var cfg = {};
20455         
20456         cfg.cls = 'form-group ' + this.inputType; //input-group
20457         
20458         if(this.inline){
20459             cfg.cls += ' ' + this.inputType + '-inline';
20460         }
20461         
20462         var input =  {
20463             tag: 'input',
20464             id : id,
20465             type : this.inputType,
20466             value : this.inputValue,
20467             cls : 'roo-' + this.inputType, //'form-box',
20468             placeholder : this.placeholder || ''
20469             
20470         };
20471         
20472         if(this.inputType != 'radio'){
20473             var hidden =  {
20474                 tag: 'input',
20475                 type : 'hidden',
20476                 cls : 'roo-hidden-value',
20477                 value : this.checked ? this.inputValue : this.valueOff
20478             };
20479         }
20480         
20481             
20482         if (this.weight) { // Validity check?
20483             cfg.cls += " " + this.inputType + "-" + this.weight;
20484         }
20485         
20486         if (this.disabled) {
20487             input.disabled=true;
20488         }
20489         
20490         if(this.checked){
20491             input.checked = this.checked;
20492         }
20493         
20494         if (this.name) {
20495             
20496             input.name = this.name;
20497             
20498             if(this.inputType != 'radio'){
20499                 hidden.name = this.name;
20500                 input.name = '_hidden_' + this.name;
20501             }
20502         }
20503         
20504         if (this.size) {
20505             input.cls += ' input-' + this.size;
20506         }
20507         
20508         var settings=this;
20509         
20510         ['xs','sm','md','lg'].map(function(size){
20511             if (settings[size]) {
20512                 cfg.cls += ' col-' + size + '-' + settings[size];
20513             }
20514         });
20515         
20516         var inputblock = input;
20517          
20518         if (this.before || this.after) {
20519             
20520             inputblock = {
20521                 cls : 'input-group',
20522                 cn :  [] 
20523             };
20524             
20525             if (this.before) {
20526                 inputblock.cn.push({
20527                     tag :'span',
20528                     cls : 'input-group-addon',
20529                     html : this.before
20530                 });
20531             }
20532             
20533             inputblock.cn.push(input);
20534             
20535             if(this.inputType != 'radio'){
20536                 inputblock.cn.push(hidden);
20537             }
20538             
20539             if (this.after) {
20540                 inputblock.cn.push({
20541                     tag :'span',
20542                     cls : 'input-group-addon',
20543                     html : this.after
20544                 });
20545             }
20546             
20547         }
20548         
20549         if (align ==='left' && this.fieldLabel.length) {
20550 //                Roo.log("left and has label");
20551             cfg.cn = [
20552                 {
20553                     tag: 'label',
20554                     'for' :  id,
20555                     cls : 'control-label',
20556                     html : this.fieldLabel
20557                 },
20558                 {
20559                     cls : "", 
20560                     cn: [
20561                         inputblock
20562                     ]
20563                 }
20564             ];
20565             
20566             if(this.labelWidth > 12){
20567                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20568             }
20569             
20570             if(this.labelWidth < 13 && this.labelmd == 0){
20571                 this.labelmd = this.labelWidth;
20572             }
20573             
20574             if(this.labellg > 0){
20575                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20576                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20577             }
20578             
20579             if(this.labelmd > 0){
20580                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20581                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20582             }
20583             
20584             if(this.labelsm > 0){
20585                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20586                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20587             }
20588             
20589             if(this.labelxs > 0){
20590                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20591                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20592             }
20593             
20594         } else if ( this.fieldLabel.length) {
20595 //                Roo.log(" label");
20596                 cfg.cn = [
20597                    
20598                     {
20599                         tag: this.boxLabel ? 'span' : 'label',
20600                         'for': id,
20601                         cls: 'control-label box-input-label',
20602                         //cls : 'input-group-addon',
20603                         html : this.fieldLabel
20604                     },
20605                     
20606                     inputblock
20607                     
20608                 ];
20609
20610         } else {
20611             
20612 //                Roo.log(" no label && no align");
20613                 cfg.cn = [  inputblock ] ;
20614                 
20615                 
20616         }
20617         
20618         if(this.boxLabel){
20619              var boxLabelCfg = {
20620                 tag: 'label',
20621                 //'for': id, // box label is handled by onclick - so no for...
20622                 cls: 'box-label',
20623                 html: this.boxLabel
20624             };
20625             
20626             if(this.tooltip){
20627                 boxLabelCfg.tooltip = this.tooltip;
20628             }
20629              
20630             cfg.cn.push(boxLabelCfg);
20631         }
20632         
20633         if(this.inputType != 'radio'){
20634             cfg.cn.push(hidden);
20635         }
20636         
20637         return cfg;
20638         
20639     },
20640     
20641     /**
20642      * return the real input element.
20643      */
20644     inputEl: function ()
20645     {
20646         return this.el.select('input.roo-' + this.inputType,true).first();
20647     },
20648     hiddenEl: function ()
20649     {
20650         return this.el.select('input.roo-hidden-value',true).first();
20651     },
20652     
20653     labelEl: function()
20654     {
20655         return this.el.select('label.control-label',true).first();
20656     },
20657     /* depricated... */
20658     
20659     label: function()
20660     {
20661         return this.labelEl();
20662     },
20663     
20664     boxLabelEl: function()
20665     {
20666         return this.el.select('label.box-label',true).first();
20667     },
20668     
20669     initEvents : function()
20670     {
20671 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20672         
20673         this.inputEl().on('click', this.onClick,  this);
20674         
20675         if (this.boxLabel) { 
20676             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20677         }
20678         
20679         this.startValue = this.getValue();
20680         
20681         if(this.groupId){
20682             Roo.bootstrap.CheckBox.register(this);
20683         }
20684     },
20685     
20686     onClick : function(e)
20687     {   
20688         if(this.fireEvent('click', this, e) !== false){
20689             this.setChecked(!this.checked);
20690         }
20691         
20692     },
20693     
20694     setChecked : function(state,suppressEvent)
20695     {
20696         this.startValue = this.getValue();
20697
20698         if(this.inputType == 'radio'){
20699             
20700             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20701                 e.dom.checked = false;
20702             });
20703             
20704             this.inputEl().dom.checked = true;
20705             
20706             this.inputEl().dom.value = this.inputValue;
20707             
20708             if(suppressEvent !== true){
20709                 this.fireEvent('check', this, true);
20710             }
20711             
20712             this.validate();
20713             
20714             return;
20715         }
20716         
20717         this.checked = state;
20718         
20719         this.inputEl().dom.checked = state;
20720         
20721         
20722         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20723         
20724         if(suppressEvent !== true){
20725             this.fireEvent('check', this, state);
20726         }
20727         
20728         this.validate();
20729     },
20730     
20731     getValue : function()
20732     {
20733         if(this.inputType == 'radio'){
20734             return this.getGroupValue();
20735         }
20736         
20737         return this.hiddenEl().dom.value;
20738         
20739     },
20740     
20741     getGroupValue : function()
20742     {
20743         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20744             return '';
20745         }
20746         
20747         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20748     },
20749     
20750     setValue : function(v,suppressEvent)
20751     {
20752         if(this.inputType == 'radio'){
20753             this.setGroupValue(v, suppressEvent);
20754             return;
20755         }
20756         
20757         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20758         
20759         this.validate();
20760     },
20761     
20762     setGroupValue : function(v, suppressEvent)
20763     {
20764         this.startValue = this.getValue();
20765         
20766         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20767             e.dom.checked = false;
20768             
20769             if(e.dom.value == v){
20770                 e.dom.checked = true;
20771             }
20772         });
20773         
20774         if(suppressEvent !== true){
20775             this.fireEvent('check', this, true);
20776         }
20777
20778         this.validate();
20779         
20780         return;
20781     },
20782     
20783     validate : function()
20784     {
20785         if(this.getVisibilityEl().hasClass('hidden')){
20786             return true;
20787         }
20788         
20789         if(
20790                 this.disabled || 
20791                 (this.inputType == 'radio' && this.validateRadio()) ||
20792                 (this.inputType == 'checkbox' && this.validateCheckbox())
20793         ){
20794             this.markValid();
20795             return true;
20796         }
20797         
20798         this.markInvalid();
20799         return false;
20800     },
20801     
20802     validateRadio : function()
20803     {
20804         if(this.getVisibilityEl().hasClass('hidden')){
20805             return true;
20806         }
20807         
20808         if(this.allowBlank){
20809             return true;
20810         }
20811         
20812         var valid = false;
20813         
20814         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20815             if(!e.dom.checked){
20816                 return;
20817             }
20818             
20819             valid = true;
20820             
20821             return false;
20822         });
20823         
20824         return valid;
20825     },
20826     
20827     validateCheckbox : function()
20828     {
20829         if(!this.groupId){
20830             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20831             //return (this.getValue() == this.inputValue) ? true : false;
20832         }
20833         
20834         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20835         
20836         if(!group){
20837             return false;
20838         }
20839         
20840         var r = false;
20841         
20842         for(var i in group){
20843             if(group[i].el.isVisible(true)){
20844                 r = false;
20845                 break;
20846             }
20847             
20848             r = true;
20849         }
20850         
20851         for(var i in group){
20852             if(r){
20853                 break;
20854             }
20855             
20856             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20857         }
20858         
20859         return r;
20860     },
20861     
20862     /**
20863      * Mark this field as valid
20864      */
20865     markValid : function()
20866     {
20867         var _this = this;
20868         
20869         this.fireEvent('valid', this);
20870         
20871         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20872         
20873         if(this.groupId){
20874             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20875         }
20876         
20877         if(label){
20878             label.markValid();
20879         }
20880
20881         if(this.inputType == 'radio'){
20882             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20883                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20884                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20885             });
20886             
20887             return;
20888         }
20889
20890         if(!this.groupId){
20891             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20892             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20893             return;
20894         }
20895         
20896         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20897         
20898         if(!group){
20899             return;
20900         }
20901         
20902         for(var i in group){
20903             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20904             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20905         }
20906     },
20907     
20908      /**
20909      * Mark this field as invalid
20910      * @param {String} msg The validation message
20911      */
20912     markInvalid : function(msg)
20913     {
20914         if(this.allowBlank){
20915             return;
20916         }
20917         
20918         var _this = this;
20919         
20920         this.fireEvent('invalid', this, msg);
20921         
20922         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20923         
20924         if(this.groupId){
20925             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20926         }
20927         
20928         if(label){
20929             label.markInvalid();
20930         }
20931             
20932         if(this.inputType == 'radio'){
20933             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20934                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20935                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20936             });
20937             
20938             return;
20939         }
20940         
20941         if(!this.groupId){
20942             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20943             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20944             return;
20945         }
20946         
20947         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20948         
20949         if(!group){
20950             return;
20951         }
20952         
20953         for(var i in group){
20954             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20955             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20956         }
20957         
20958     },
20959     
20960     clearInvalid : function()
20961     {
20962         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20963         
20964         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20965         
20966         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20967         
20968         if (label && label.iconEl) {
20969             label.iconEl.removeClass(label.validClass);
20970             label.iconEl.removeClass(label.invalidClass);
20971         }
20972     },
20973     
20974     disable : function()
20975     {
20976         if(this.inputType != 'radio'){
20977             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20978             return;
20979         }
20980         
20981         var _this = this;
20982         
20983         if(this.rendered){
20984             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20985                 _this.getActionEl().addClass(this.disabledClass);
20986                 e.dom.disabled = true;
20987             });
20988         }
20989         
20990         this.disabled = true;
20991         this.fireEvent("disable", this);
20992         return this;
20993     },
20994
20995     enable : function()
20996     {
20997         if(this.inputType != 'radio'){
20998             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20999             return;
21000         }
21001         
21002         var _this = this;
21003         
21004         if(this.rendered){
21005             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21006                 _this.getActionEl().removeClass(this.disabledClass);
21007                 e.dom.disabled = false;
21008             });
21009         }
21010         
21011         this.disabled = false;
21012         this.fireEvent("enable", this);
21013         return this;
21014     },
21015     
21016     setBoxLabel : function(v)
21017     {
21018         this.boxLabel = v;
21019         
21020         if(this.rendered){
21021             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21022         }
21023     }
21024
21025 });
21026
21027 Roo.apply(Roo.bootstrap.CheckBox, {
21028     
21029     groups: {},
21030     
21031      /**
21032     * register a CheckBox Group
21033     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21034     */
21035     register : function(checkbox)
21036     {
21037         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21038             this.groups[checkbox.groupId] = {};
21039         }
21040         
21041         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21042             return;
21043         }
21044         
21045         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21046         
21047     },
21048     /**
21049     * fetch a CheckBox Group based on the group ID
21050     * @param {string} the group ID
21051     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21052     */
21053     get: function(groupId) {
21054         if (typeof(this.groups[groupId]) == 'undefined') {
21055             return false;
21056         }
21057         
21058         return this.groups[groupId] ;
21059     }
21060     
21061     
21062 });
21063 /*
21064  * - LGPL
21065  *
21066  * RadioItem
21067  * 
21068  */
21069
21070 /**
21071  * @class Roo.bootstrap.Radio
21072  * @extends Roo.bootstrap.Component
21073  * Bootstrap Radio class
21074  * @cfg {String} boxLabel - the label associated
21075  * @cfg {String} value - the value of radio
21076  * 
21077  * @constructor
21078  * Create a new Radio
21079  * @param {Object} config The config object
21080  */
21081 Roo.bootstrap.Radio = function(config){
21082     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21083     
21084 };
21085
21086 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21087     
21088     boxLabel : '',
21089     
21090     value : '',
21091     
21092     getAutoCreate : function()
21093     {
21094         var cfg = {
21095             tag : 'div',
21096             cls : 'form-group radio',
21097             cn : [
21098                 {
21099                     tag : 'label',
21100                     cls : 'box-label',
21101                     html : this.boxLabel
21102                 }
21103             ]
21104         };
21105         
21106         return cfg;
21107     },
21108     
21109     initEvents : function() 
21110     {
21111         this.parent().register(this);
21112         
21113         this.el.on('click', this.onClick, this);
21114         
21115     },
21116     
21117     onClick : function(e)
21118     {
21119         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21120             this.setChecked(true);
21121         }
21122     },
21123     
21124     setChecked : function(state, suppressEvent)
21125     {
21126         this.parent().setValue(this.value, suppressEvent);
21127         
21128     },
21129     
21130     setBoxLabel : function(v)
21131     {
21132         this.boxLabel = v;
21133         
21134         if(this.rendered){
21135             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21136         }
21137     }
21138     
21139 });
21140  
21141
21142  /*
21143  * - LGPL
21144  *
21145  * Input
21146  * 
21147  */
21148
21149 /**
21150  * @class Roo.bootstrap.SecurePass
21151  * @extends Roo.bootstrap.Input
21152  * Bootstrap SecurePass class
21153  *
21154  * 
21155  * @constructor
21156  * Create a new SecurePass
21157  * @param {Object} config The config object
21158  */
21159  
21160 Roo.bootstrap.SecurePass = function (config) {
21161     // these go here, so the translation tool can replace them..
21162     this.errors = {
21163         PwdEmpty: "Please type a password, and then retype it to confirm.",
21164         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21165         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21166         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21167         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21168         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21169         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21170         TooWeak: "Your password is Too Weak."
21171     },
21172     this.meterLabel = "Password strength:";
21173     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21174     this.meterClass = [
21175         "roo-password-meter-tooweak", 
21176         "roo-password-meter-weak", 
21177         "roo-password-meter-medium", 
21178         "roo-password-meter-strong", 
21179         "roo-password-meter-grey"
21180     ];
21181     
21182     this.errors = {};
21183     
21184     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21185 }
21186
21187 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21188     /**
21189      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21190      * {
21191      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21192      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21193      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21194      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21195      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21196      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21197      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21198      * })
21199      */
21200     // private
21201     
21202     meterWidth: 300,
21203     errorMsg :'',    
21204     errors: false,
21205     imageRoot: '/',
21206     /**
21207      * @cfg {String/Object} Label for the strength meter (defaults to
21208      * 'Password strength:')
21209      */
21210     // private
21211     meterLabel: '',
21212     /**
21213      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21214      * ['Weak', 'Medium', 'Strong'])
21215      */
21216     // private    
21217     pwdStrengths: false,    
21218     // private
21219     strength: 0,
21220     // private
21221     _lastPwd: null,
21222     // private
21223     kCapitalLetter: 0,
21224     kSmallLetter: 1,
21225     kDigit: 2,
21226     kPunctuation: 3,
21227     
21228     insecure: false,
21229     // private
21230     initEvents: function ()
21231     {
21232         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21233
21234         if (this.el.is('input[type=password]') && Roo.isSafari) {
21235             this.el.on('keydown', this.SafariOnKeyDown, this);
21236         }
21237
21238         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21239     },
21240     // private
21241     onRender: function (ct, position)
21242     {
21243         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21244         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21245         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21246
21247         this.trigger.createChild({
21248                    cn: [
21249                     {
21250                     //id: 'PwdMeter',
21251                     tag: 'div',
21252                     cls: 'roo-password-meter-grey col-xs-12',
21253                     style: {
21254                         //width: 0,
21255                         //width: this.meterWidth + 'px'                                                
21256                         }
21257                     },
21258                     {                            
21259                          cls: 'roo-password-meter-text'                          
21260                     }
21261                 ]            
21262         });
21263
21264          
21265         if (this.hideTrigger) {
21266             this.trigger.setDisplayed(false);
21267         }
21268         this.setSize(this.width || '', this.height || '');
21269     },
21270     // private
21271     onDestroy: function ()
21272     {
21273         if (this.trigger) {
21274             this.trigger.removeAllListeners();
21275             this.trigger.remove();
21276         }
21277         if (this.wrap) {
21278             this.wrap.remove();
21279         }
21280         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21281     },
21282     // private
21283     checkStrength: function ()
21284     {
21285         var pwd = this.inputEl().getValue();
21286         if (pwd == this._lastPwd) {
21287             return;
21288         }
21289
21290         var strength;
21291         if (this.ClientSideStrongPassword(pwd)) {
21292             strength = 3;
21293         } else if (this.ClientSideMediumPassword(pwd)) {
21294             strength = 2;
21295         } else if (this.ClientSideWeakPassword(pwd)) {
21296             strength = 1;
21297         } else {
21298             strength = 0;
21299         }
21300         
21301         Roo.log('strength1: ' + strength);
21302         
21303         //var pm = this.trigger.child('div/div/div').dom;
21304         var pm = this.trigger.child('div/div');
21305         pm.removeClass(this.meterClass);
21306         pm.addClass(this.meterClass[strength]);
21307                 
21308         
21309         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21310                 
21311         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21312         
21313         this._lastPwd = pwd;
21314     },
21315     reset: function ()
21316     {
21317         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21318         
21319         this._lastPwd = '';
21320         
21321         var pm = this.trigger.child('div/div');
21322         pm.removeClass(this.meterClass);
21323         pm.addClass('roo-password-meter-grey');        
21324         
21325         
21326         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21327         
21328         pt.innerHTML = '';
21329         this.inputEl().dom.type='password';
21330     },
21331     // private
21332     validateValue: function (value)
21333     {
21334         
21335         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21336             return false;
21337         }
21338         if (value.length == 0) {
21339             if (this.allowBlank) {
21340                 this.clearInvalid();
21341                 return true;
21342             }
21343
21344             this.markInvalid(this.errors.PwdEmpty);
21345             this.errorMsg = this.errors.PwdEmpty;
21346             return false;
21347         }
21348         
21349         if(this.insecure){
21350             return true;
21351         }
21352         
21353         if ('[\x21-\x7e]*'.match(value)) {
21354             this.markInvalid(this.errors.PwdBadChar);
21355             this.errorMsg = this.errors.PwdBadChar;
21356             return false;
21357         }
21358         if (value.length < 6) {
21359             this.markInvalid(this.errors.PwdShort);
21360             this.errorMsg = this.errors.PwdShort;
21361             return false;
21362         }
21363         if (value.length > 16) {
21364             this.markInvalid(this.errors.PwdLong);
21365             this.errorMsg = this.errors.PwdLong;
21366             return false;
21367         }
21368         var strength;
21369         if (this.ClientSideStrongPassword(value)) {
21370             strength = 3;
21371         } else if (this.ClientSideMediumPassword(value)) {
21372             strength = 2;
21373         } else if (this.ClientSideWeakPassword(value)) {
21374             strength = 1;
21375         } else {
21376             strength = 0;
21377         }
21378
21379         
21380         if (strength < 2) {
21381             //this.markInvalid(this.errors.TooWeak);
21382             this.errorMsg = this.errors.TooWeak;
21383             //return false;
21384         }
21385         
21386         
21387         console.log('strength2: ' + strength);
21388         
21389         //var pm = this.trigger.child('div/div/div').dom;
21390         
21391         var pm = this.trigger.child('div/div');
21392         pm.removeClass(this.meterClass);
21393         pm.addClass(this.meterClass[strength]);
21394                 
21395         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21396                 
21397         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21398         
21399         this.errorMsg = ''; 
21400         return true;
21401     },
21402     // private
21403     CharacterSetChecks: function (type)
21404     {
21405         this.type = type;
21406         this.fResult = false;
21407     },
21408     // private
21409     isctype: function (character, type)
21410     {
21411         switch (type) {  
21412             case this.kCapitalLetter:
21413                 if (character >= 'A' && character <= 'Z') {
21414                     return true;
21415                 }
21416                 break;
21417             
21418             case this.kSmallLetter:
21419                 if (character >= 'a' && character <= 'z') {
21420                     return true;
21421                 }
21422                 break;
21423             
21424             case this.kDigit:
21425                 if (character >= '0' && character <= '9') {
21426                     return true;
21427                 }
21428                 break;
21429             
21430             case this.kPunctuation:
21431                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21432                     return true;
21433                 }
21434                 break;
21435             
21436             default:
21437                 return false;
21438         }
21439
21440     },
21441     // private
21442     IsLongEnough: function (pwd, size)
21443     {
21444         return !(pwd == null || isNaN(size) || pwd.length < size);
21445     },
21446     // private
21447     SpansEnoughCharacterSets: function (word, nb)
21448     {
21449         if (!this.IsLongEnough(word, nb))
21450         {
21451             return false;
21452         }
21453
21454         var characterSetChecks = new Array(
21455             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21456             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21457         );
21458         
21459         for (var index = 0; index < word.length; ++index) {
21460             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21461                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21462                     characterSetChecks[nCharSet].fResult = true;
21463                     break;
21464                 }
21465             }
21466         }
21467
21468         var nCharSets = 0;
21469         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21470             if (characterSetChecks[nCharSet].fResult) {
21471                 ++nCharSets;
21472             }
21473         }
21474
21475         if (nCharSets < nb) {
21476             return false;
21477         }
21478         return true;
21479     },
21480     // private
21481     ClientSideStrongPassword: function (pwd)
21482     {
21483         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21484     },
21485     // private
21486     ClientSideMediumPassword: function (pwd)
21487     {
21488         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21489     },
21490     // private
21491     ClientSideWeakPassword: function (pwd)
21492     {
21493         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21494     }
21495           
21496 })//<script type="text/javascript">
21497
21498 /*
21499  * Based  Ext JS Library 1.1.1
21500  * Copyright(c) 2006-2007, Ext JS, LLC.
21501  * LGPL
21502  *
21503  */
21504  
21505 /**
21506  * @class Roo.HtmlEditorCore
21507  * @extends Roo.Component
21508  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21509  *
21510  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21511  */
21512
21513 Roo.HtmlEditorCore = function(config){
21514     
21515     
21516     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21517     
21518     
21519     this.addEvents({
21520         /**
21521          * @event initialize
21522          * Fires when the editor is fully initialized (including the iframe)
21523          * @param {Roo.HtmlEditorCore} this
21524          */
21525         initialize: true,
21526         /**
21527          * @event activate
21528          * Fires when the editor is first receives the focus. Any insertion must wait
21529          * until after this event.
21530          * @param {Roo.HtmlEditorCore} this
21531          */
21532         activate: true,
21533          /**
21534          * @event beforesync
21535          * Fires before the textarea is updated with content from the editor iframe. Return false
21536          * to cancel the sync.
21537          * @param {Roo.HtmlEditorCore} this
21538          * @param {String} html
21539          */
21540         beforesync: true,
21541          /**
21542          * @event beforepush
21543          * Fires before the iframe editor is updated with content from the textarea. Return false
21544          * to cancel the push.
21545          * @param {Roo.HtmlEditorCore} this
21546          * @param {String} html
21547          */
21548         beforepush: true,
21549          /**
21550          * @event sync
21551          * Fires when the textarea is updated with content from the editor iframe.
21552          * @param {Roo.HtmlEditorCore} this
21553          * @param {String} html
21554          */
21555         sync: true,
21556          /**
21557          * @event push
21558          * Fires when the iframe editor is updated with content from the textarea.
21559          * @param {Roo.HtmlEditorCore} this
21560          * @param {String} html
21561          */
21562         push: true,
21563         
21564         /**
21565          * @event editorevent
21566          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21567          * @param {Roo.HtmlEditorCore} this
21568          */
21569         editorevent: true
21570         
21571     });
21572     
21573     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21574     
21575     // defaults : white / black...
21576     this.applyBlacklists();
21577     
21578     
21579     
21580 };
21581
21582
21583 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21584
21585
21586      /**
21587      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21588      */
21589     
21590     owner : false,
21591     
21592      /**
21593      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21594      *                        Roo.resizable.
21595      */
21596     resizable : false,
21597      /**
21598      * @cfg {Number} height (in pixels)
21599      */   
21600     height: 300,
21601    /**
21602      * @cfg {Number} width (in pixels)
21603      */   
21604     width: 500,
21605     
21606     /**
21607      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21608      * 
21609      */
21610     stylesheets: false,
21611     
21612     // id of frame..
21613     frameId: false,
21614     
21615     // private properties
21616     validationEvent : false,
21617     deferHeight: true,
21618     initialized : false,
21619     activated : false,
21620     sourceEditMode : false,
21621     onFocus : Roo.emptyFn,
21622     iframePad:3,
21623     hideMode:'offsets',
21624     
21625     clearUp: true,
21626     
21627     // blacklist + whitelisted elements..
21628     black: false,
21629     white: false,
21630      
21631     bodyCls : '',
21632
21633     /**
21634      * Protected method that will not generally be called directly. It
21635      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21636      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21637      */
21638     getDocMarkup : function(){
21639         // body styles..
21640         var st = '';
21641         
21642         // inherit styels from page...?? 
21643         if (this.stylesheets === false) {
21644             
21645             Roo.get(document.head).select('style').each(function(node) {
21646                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21647             });
21648             
21649             Roo.get(document.head).select('link').each(function(node) { 
21650                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21651             });
21652             
21653         } else if (!this.stylesheets.length) {
21654                 // simple..
21655                 st = '<style type="text/css">' +
21656                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21657                    '</style>';
21658         } else { 
21659             st = '<style type="text/css">' +
21660                     this.stylesheets +
21661                 '</style>';
21662         }
21663         
21664         st +=  '<style type="text/css">' +
21665             'IMG { cursor: pointer } ' +
21666         '</style>';
21667
21668         var cls = 'roo-htmleditor-body';
21669         
21670         if(this.bodyCls.length){
21671             cls += ' ' + this.bodyCls;
21672         }
21673         
21674         return '<html><head>' + st  +
21675             //<style type="text/css">' +
21676             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21677             //'</style>' +
21678             ' </head><body class="' +  cls + '"></body></html>';
21679     },
21680
21681     // private
21682     onRender : function(ct, position)
21683     {
21684         var _t = this;
21685         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21686         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21687         
21688         
21689         this.el.dom.style.border = '0 none';
21690         this.el.dom.setAttribute('tabIndex', -1);
21691         this.el.addClass('x-hidden hide');
21692         
21693         
21694         
21695         if(Roo.isIE){ // fix IE 1px bogus margin
21696             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21697         }
21698        
21699         
21700         this.frameId = Roo.id();
21701         
21702          
21703         
21704         var iframe = this.owner.wrap.createChild({
21705             tag: 'iframe',
21706             cls: 'form-control', // bootstrap..
21707             id: this.frameId,
21708             name: this.frameId,
21709             frameBorder : 'no',
21710             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21711         }, this.el
21712         );
21713         
21714         
21715         this.iframe = iframe.dom;
21716
21717          this.assignDocWin();
21718         
21719         this.doc.designMode = 'on';
21720        
21721         this.doc.open();
21722         this.doc.write(this.getDocMarkup());
21723         this.doc.close();
21724
21725         
21726         var task = { // must defer to wait for browser to be ready
21727             run : function(){
21728                 //console.log("run task?" + this.doc.readyState);
21729                 this.assignDocWin();
21730                 if(this.doc.body || this.doc.readyState == 'complete'){
21731                     try {
21732                         this.doc.designMode="on";
21733                     } catch (e) {
21734                         return;
21735                     }
21736                     Roo.TaskMgr.stop(task);
21737                     this.initEditor.defer(10, this);
21738                 }
21739             },
21740             interval : 10,
21741             duration: 10000,
21742             scope: this
21743         };
21744         Roo.TaskMgr.start(task);
21745
21746     },
21747
21748     // private
21749     onResize : function(w, h)
21750     {
21751          Roo.log('resize: ' +w + ',' + h );
21752         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21753         if(!this.iframe){
21754             return;
21755         }
21756         if(typeof w == 'number'){
21757             
21758             this.iframe.style.width = w + 'px';
21759         }
21760         if(typeof h == 'number'){
21761             
21762             this.iframe.style.height = h + 'px';
21763             if(this.doc){
21764                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21765             }
21766         }
21767         
21768     },
21769
21770     /**
21771      * Toggles the editor between standard and source edit mode.
21772      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21773      */
21774     toggleSourceEdit : function(sourceEditMode){
21775         
21776         this.sourceEditMode = sourceEditMode === true;
21777         
21778         if(this.sourceEditMode){
21779  
21780             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21781             
21782         }else{
21783             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21784             //this.iframe.className = '';
21785             this.deferFocus();
21786         }
21787         //this.setSize(this.owner.wrap.getSize());
21788         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21789     },
21790
21791     
21792   
21793
21794     /**
21795      * Protected method that will not generally be called directly. If you need/want
21796      * custom HTML cleanup, this is the method you should override.
21797      * @param {String} html The HTML to be cleaned
21798      * return {String} The cleaned HTML
21799      */
21800     cleanHtml : function(html){
21801         html = String(html);
21802         if(html.length > 5){
21803             if(Roo.isSafari){ // strip safari nonsense
21804                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21805             }
21806         }
21807         if(html == '&nbsp;'){
21808             html = '';
21809         }
21810         return html;
21811     },
21812
21813     /**
21814      * HTML Editor -> Textarea
21815      * Protected method that will not generally be called directly. Syncs the contents
21816      * of the editor iframe with the textarea.
21817      */
21818     syncValue : function(){
21819         if(this.initialized){
21820             var bd = (this.doc.body || this.doc.documentElement);
21821             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21822             var html = bd.innerHTML;
21823             if(Roo.isSafari){
21824                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21825                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21826                 if(m && m[1]){
21827                     html = '<div style="'+m[0]+'">' + html + '</div>';
21828                 }
21829             }
21830             html = this.cleanHtml(html);
21831             // fix up the special chars.. normaly like back quotes in word...
21832             // however we do not want to do this with chinese..
21833             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21834                 var cc = b.charCodeAt();
21835                 if (
21836                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21837                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21838                     (cc >= 0xf900 && cc < 0xfb00 )
21839                 ) {
21840                         return b;
21841                 }
21842                 return "&#"+cc+";" 
21843             });
21844             if(this.owner.fireEvent('beforesync', this, html) !== false){
21845                 this.el.dom.value = html;
21846                 this.owner.fireEvent('sync', this, html);
21847             }
21848         }
21849     },
21850
21851     /**
21852      * Protected method that will not generally be called directly. Pushes the value of the textarea
21853      * into the iframe editor.
21854      */
21855     pushValue : function(){
21856         if(this.initialized){
21857             var v = this.el.dom.value.trim();
21858             
21859 //            if(v.length < 1){
21860 //                v = '&#160;';
21861 //            }
21862             
21863             if(this.owner.fireEvent('beforepush', this, v) !== false){
21864                 var d = (this.doc.body || this.doc.documentElement);
21865                 d.innerHTML = v;
21866                 this.cleanUpPaste();
21867                 this.el.dom.value = d.innerHTML;
21868                 this.owner.fireEvent('push', this, v);
21869             }
21870         }
21871     },
21872
21873     // private
21874     deferFocus : function(){
21875         this.focus.defer(10, this);
21876     },
21877
21878     // doc'ed in Field
21879     focus : function(){
21880         if(this.win && !this.sourceEditMode){
21881             this.win.focus();
21882         }else{
21883             this.el.focus();
21884         }
21885     },
21886     
21887     assignDocWin: function()
21888     {
21889         var iframe = this.iframe;
21890         
21891          if(Roo.isIE){
21892             this.doc = iframe.contentWindow.document;
21893             this.win = iframe.contentWindow;
21894         } else {
21895 //            if (!Roo.get(this.frameId)) {
21896 //                return;
21897 //            }
21898 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21899 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21900             
21901             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21902                 return;
21903             }
21904             
21905             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21906             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21907         }
21908     },
21909     
21910     // private
21911     initEditor : function(){
21912         //console.log("INIT EDITOR");
21913         this.assignDocWin();
21914         
21915         
21916         
21917         this.doc.designMode="on";
21918         this.doc.open();
21919         this.doc.write(this.getDocMarkup());
21920         this.doc.close();
21921         
21922         var dbody = (this.doc.body || this.doc.documentElement);
21923         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21924         // this copies styles from the containing element into thsi one..
21925         // not sure why we need all of this..
21926         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21927         
21928         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21929         //ss['background-attachment'] = 'fixed'; // w3c
21930         dbody.bgProperties = 'fixed'; // ie
21931         //Roo.DomHelper.applyStyles(dbody, ss);
21932         Roo.EventManager.on(this.doc, {
21933             //'mousedown': this.onEditorEvent,
21934             'mouseup': this.onEditorEvent,
21935             'dblclick': this.onEditorEvent,
21936             'click': this.onEditorEvent,
21937             'keyup': this.onEditorEvent,
21938             buffer:100,
21939             scope: this
21940         });
21941         if(Roo.isGecko){
21942             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21943         }
21944         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21945             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21946         }
21947         this.initialized = true;
21948
21949         this.owner.fireEvent('initialize', this);
21950         this.pushValue();
21951     },
21952
21953     // private
21954     onDestroy : function(){
21955         
21956         
21957         
21958         if(this.rendered){
21959             
21960             //for (var i =0; i < this.toolbars.length;i++) {
21961             //    // fixme - ask toolbars for heights?
21962             //    this.toolbars[i].onDestroy();
21963            // }
21964             
21965             //this.wrap.dom.innerHTML = '';
21966             //this.wrap.remove();
21967         }
21968     },
21969
21970     // private
21971     onFirstFocus : function(){
21972         
21973         this.assignDocWin();
21974         
21975         
21976         this.activated = true;
21977          
21978     
21979         if(Roo.isGecko){ // prevent silly gecko errors
21980             this.win.focus();
21981             var s = this.win.getSelection();
21982             if(!s.focusNode || s.focusNode.nodeType != 3){
21983                 var r = s.getRangeAt(0);
21984                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21985                 r.collapse(true);
21986                 this.deferFocus();
21987             }
21988             try{
21989                 this.execCmd('useCSS', true);
21990                 this.execCmd('styleWithCSS', false);
21991             }catch(e){}
21992         }
21993         this.owner.fireEvent('activate', this);
21994     },
21995
21996     // private
21997     adjustFont: function(btn){
21998         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21999         //if(Roo.isSafari){ // safari
22000         //    adjust *= 2;
22001        // }
22002         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22003         if(Roo.isSafari){ // safari
22004             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22005             v =  (v < 10) ? 10 : v;
22006             v =  (v > 48) ? 48 : v;
22007             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22008             
22009         }
22010         
22011         
22012         v = Math.max(1, v+adjust);
22013         
22014         this.execCmd('FontSize', v  );
22015     },
22016
22017     onEditorEvent : function(e)
22018     {
22019         this.owner.fireEvent('editorevent', this, e);
22020       //  this.updateToolbar();
22021         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22022     },
22023
22024     insertTag : function(tg)
22025     {
22026         // could be a bit smarter... -> wrap the current selected tRoo..
22027         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22028             
22029             range = this.createRange(this.getSelection());
22030             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22031             wrappingNode.appendChild(range.extractContents());
22032             range.insertNode(wrappingNode);
22033
22034             return;
22035             
22036             
22037             
22038         }
22039         this.execCmd("formatblock",   tg);
22040         
22041     },
22042     
22043     insertText : function(txt)
22044     {
22045         
22046         
22047         var range = this.createRange();
22048         range.deleteContents();
22049                //alert(Sender.getAttribute('label'));
22050                
22051         range.insertNode(this.doc.createTextNode(txt));
22052     } ,
22053     
22054      
22055
22056     /**
22057      * Executes a Midas editor command on the editor document and performs necessary focus and
22058      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22059      * @param {String} cmd The Midas command
22060      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22061      */
22062     relayCmd : function(cmd, value){
22063         this.win.focus();
22064         this.execCmd(cmd, value);
22065         this.owner.fireEvent('editorevent', this);
22066         //this.updateToolbar();
22067         this.owner.deferFocus();
22068     },
22069
22070     /**
22071      * Executes a Midas editor command directly on the editor document.
22072      * For visual commands, you should use {@link #relayCmd} instead.
22073      * <b>This should only be called after the editor is initialized.</b>
22074      * @param {String} cmd The Midas command
22075      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22076      */
22077     execCmd : function(cmd, value){
22078         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22079         this.syncValue();
22080     },
22081  
22082  
22083    
22084     /**
22085      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22086      * to insert tRoo.
22087      * @param {String} text | dom node.. 
22088      */
22089     insertAtCursor : function(text)
22090     {
22091         
22092         if(!this.activated){
22093             return;
22094         }
22095         /*
22096         if(Roo.isIE){
22097             this.win.focus();
22098             var r = this.doc.selection.createRange();
22099             if(r){
22100                 r.collapse(true);
22101                 r.pasteHTML(text);
22102                 this.syncValue();
22103                 this.deferFocus();
22104             
22105             }
22106             return;
22107         }
22108         */
22109         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22110             this.win.focus();
22111             
22112             
22113             // from jquery ui (MIT licenced)
22114             var range, node;
22115             var win = this.win;
22116             
22117             if (win.getSelection && win.getSelection().getRangeAt) {
22118                 range = win.getSelection().getRangeAt(0);
22119                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22120                 range.insertNode(node);
22121             } else if (win.document.selection && win.document.selection.createRange) {
22122                 // no firefox support
22123                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22124                 win.document.selection.createRange().pasteHTML(txt);
22125             } else {
22126                 // no firefox support
22127                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22128                 this.execCmd('InsertHTML', txt);
22129             } 
22130             
22131             this.syncValue();
22132             
22133             this.deferFocus();
22134         }
22135     },
22136  // private
22137     mozKeyPress : function(e){
22138         if(e.ctrlKey){
22139             var c = e.getCharCode(), cmd;
22140           
22141             if(c > 0){
22142                 c = String.fromCharCode(c).toLowerCase();
22143                 switch(c){
22144                     case 'b':
22145                         cmd = 'bold';
22146                         break;
22147                     case 'i':
22148                         cmd = 'italic';
22149                         break;
22150                     
22151                     case 'u':
22152                         cmd = 'underline';
22153                         break;
22154                     
22155                     case 'v':
22156                         this.cleanUpPaste.defer(100, this);
22157                         return;
22158                         
22159                 }
22160                 if(cmd){
22161                     this.win.focus();
22162                     this.execCmd(cmd);
22163                     this.deferFocus();
22164                     e.preventDefault();
22165                 }
22166                 
22167             }
22168         }
22169     },
22170
22171     // private
22172     fixKeys : function(){ // load time branching for fastest keydown performance
22173         if(Roo.isIE){
22174             return function(e){
22175                 var k = e.getKey(), r;
22176                 if(k == e.TAB){
22177                     e.stopEvent();
22178                     r = this.doc.selection.createRange();
22179                     if(r){
22180                         r.collapse(true);
22181                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22182                         this.deferFocus();
22183                     }
22184                     return;
22185                 }
22186                 
22187                 if(k == e.ENTER){
22188                     r = this.doc.selection.createRange();
22189                     if(r){
22190                         var target = r.parentElement();
22191                         if(!target || target.tagName.toLowerCase() != 'li'){
22192                             e.stopEvent();
22193                             r.pasteHTML('<br />');
22194                             r.collapse(false);
22195                             r.select();
22196                         }
22197                     }
22198                 }
22199                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22200                     this.cleanUpPaste.defer(100, this);
22201                     return;
22202                 }
22203                 
22204                 
22205             };
22206         }else if(Roo.isOpera){
22207             return function(e){
22208                 var k = e.getKey();
22209                 if(k == e.TAB){
22210                     e.stopEvent();
22211                     this.win.focus();
22212                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22213                     this.deferFocus();
22214                 }
22215                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22216                     this.cleanUpPaste.defer(100, this);
22217                     return;
22218                 }
22219                 
22220             };
22221         }else if(Roo.isSafari){
22222             return function(e){
22223                 var k = e.getKey();
22224                 
22225                 if(k == e.TAB){
22226                     e.stopEvent();
22227                     this.execCmd('InsertText','\t');
22228                     this.deferFocus();
22229                     return;
22230                 }
22231                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22232                     this.cleanUpPaste.defer(100, this);
22233                     return;
22234                 }
22235                 
22236              };
22237         }
22238     }(),
22239     
22240     getAllAncestors: function()
22241     {
22242         var p = this.getSelectedNode();
22243         var a = [];
22244         if (!p) {
22245             a.push(p); // push blank onto stack..
22246             p = this.getParentElement();
22247         }
22248         
22249         
22250         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22251             a.push(p);
22252             p = p.parentNode;
22253         }
22254         a.push(this.doc.body);
22255         return a;
22256     },
22257     lastSel : false,
22258     lastSelNode : false,
22259     
22260     
22261     getSelection : function() 
22262     {
22263         this.assignDocWin();
22264         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22265     },
22266     
22267     getSelectedNode: function() 
22268     {
22269         // this may only work on Gecko!!!
22270         
22271         // should we cache this!!!!
22272         
22273         
22274         
22275          
22276         var range = this.createRange(this.getSelection()).cloneRange();
22277         
22278         if (Roo.isIE) {
22279             var parent = range.parentElement();
22280             while (true) {
22281                 var testRange = range.duplicate();
22282                 testRange.moveToElementText(parent);
22283                 if (testRange.inRange(range)) {
22284                     break;
22285                 }
22286                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22287                     break;
22288                 }
22289                 parent = parent.parentElement;
22290             }
22291             return parent;
22292         }
22293         
22294         // is ancestor a text element.
22295         var ac =  range.commonAncestorContainer;
22296         if (ac.nodeType == 3) {
22297             ac = ac.parentNode;
22298         }
22299         
22300         var ar = ac.childNodes;
22301          
22302         var nodes = [];
22303         var other_nodes = [];
22304         var has_other_nodes = false;
22305         for (var i=0;i<ar.length;i++) {
22306             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22307                 continue;
22308             }
22309             // fullly contained node.
22310             
22311             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22312                 nodes.push(ar[i]);
22313                 continue;
22314             }
22315             
22316             // probably selected..
22317             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22318                 other_nodes.push(ar[i]);
22319                 continue;
22320             }
22321             // outer..
22322             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22323                 continue;
22324             }
22325             
22326             
22327             has_other_nodes = true;
22328         }
22329         if (!nodes.length && other_nodes.length) {
22330             nodes= other_nodes;
22331         }
22332         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22333             return false;
22334         }
22335         
22336         return nodes[0];
22337     },
22338     createRange: function(sel)
22339     {
22340         // this has strange effects when using with 
22341         // top toolbar - not sure if it's a great idea.
22342         //this.editor.contentWindow.focus();
22343         if (typeof sel != "undefined") {
22344             try {
22345                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22346             } catch(e) {
22347                 return this.doc.createRange();
22348             }
22349         } else {
22350             return this.doc.createRange();
22351         }
22352     },
22353     getParentElement: function()
22354     {
22355         
22356         this.assignDocWin();
22357         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22358         
22359         var range = this.createRange(sel);
22360          
22361         try {
22362             var p = range.commonAncestorContainer;
22363             while (p.nodeType == 3) { // text node
22364                 p = p.parentNode;
22365             }
22366             return p;
22367         } catch (e) {
22368             return null;
22369         }
22370     
22371     },
22372     /***
22373      *
22374      * Range intersection.. the hard stuff...
22375      *  '-1' = before
22376      *  '0' = hits..
22377      *  '1' = after.
22378      *         [ -- selected range --- ]
22379      *   [fail]                        [fail]
22380      *
22381      *    basically..
22382      *      if end is before start or  hits it. fail.
22383      *      if start is after end or hits it fail.
22384      *
22385      *   if either hits (but other is outside. - then it's not 
22386      *   
22387      *    
22388      **/
22389     
22390     
22391     // @see http://www.thismuchiknow.co.uk/?p=64.
22392     rangeIntersectsNode : function(range, node)
22393     {
22394         var nodeRange = node.ownerDocument.createRange();
22395         try {
22396             nodeRange.selectNode(node);
22397         } catch (e) {
22398             nodeRange.selectNodeContents(node);
22399         }
22400     
22401         var rangeStartRange = range.cloneRange();
22402         rangeStartRange.collapse(true);
22403     
22404         var rangeEndRange = range.cloneRange();
22405         rangeEndRange.collapse(false);
22406     
22407         var nodeStartRange = nodeRange.cloneRange();
22408         nodeStartRange.collapse(true);
22409     
22410         var nodeEndRange = nodeRange.cloneRange();
22411         nodeEndRange.collapse(false);
22412     
22413         return rangeStartRange.compareBoundaryPoints(
22414                  Range.START_TO_START, nodeEndRange) == -1 &&
22415                rangeEndRange.compareBoundaryPoints(
22416                  Range.START_TO_START, nodeStartRange) == 1;
22417         
22418          
22419     },
22420     rangeCompareNode : function(range, node)
22421     {
22422         var nodeRange = node.ownerDocument.createRange();
22423         try {
22424             nodeRange.selectNode(node);
22425         } catch (e) {
22426             nodeRange.selectNodeContents(node);
22427         }
22428         
22429         
22430         range.collapse(true);
22431     
22432         nodeRange.collapse(true);
22433      
22434         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22435         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22436          
22437         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22438         
22439         var nodeIsBefore   =  ss == 1;
22440         var nodeIsAfter    = ee == -1;
22441         
22442         if (nodeIsBefore && nodeIsAfter) {
22443             return 0; // outer
22444         }
22445         if (!nodeIsBefore && nodeIsAfter) {
22446             return 1; //right trailed.
22447         }
22448         
22449         if (nodeIsBefore && !nodeIsAfter) {
22450             return 2;  // left trailed.
22451         }
22452         // fully contined.
22453         return 3;
22454     },
22455
22456     // private? - in a new class?
22457     cleanUpPaste :  function()
22458     {
22459         // cleans up the whole document..
22460         Roo.log('cleanuppaste');
22461         
22462         this.cleanUpChildren(this.doc.body);
22463         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22464         if (clean != this.doc.body.innerHTML) {
22465             this.doc.body.innerHTML = clean;
22466         }
22467         
22468     },
22469     
22470     cleanWordChars : function(input) {// change the chars to hex code
22471         var he = Roo.HtmlEditorCore;
22472         
22473         var output = input;
22474         Roo.each(he.swapCodes, function(sw) { 
22475             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22476             
22477             output = output.replace(swapper, sw[1]);
22478         });
22479         
22480         return output;
22481     },
22482     
22483     
22484     cleanUpChildren : function (n)
22485     {
22486         if (!n.childNodes.length) {
22487             return;
22488         }
22489         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22490            this.cleanUpChild(n.childNodes[i]);
22491         }
22492     },
22493     
22494     
22495         
22496     
22497     cleanUpChild : function (node)
22498     {
22499         var ed = this;
22500         //console.log(node);
22501         if (node.nodeName == "#text") {
22502             // clean up silly Windows -- stuff?
22503             return; 
22504         }
22505         if (node.nodeName == "#comment") {
22506             node.parentNode.removeChild(node);
22507             // clean up silly Windows -- stuff?
22508             return; 
22509         }
22510         var lcname = node.tagName.toLowerCase();
22511         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22512         // whitelist of tags..
22513         
22514         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22515             // remove node.
22516             node.parentNode.removeChild(node);
22517             return;
22518             
22519         }
22520         
22521         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22522         
22523         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22524         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22525         
22526         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22527         //    remove_keep_children = true;
22528         //}
22529         
22530         if (remove_keep_children) {
22531             this.cleanUpChildren(node);
22532             // inserts everything just before this node...
22533             while (node.childNodes.length) {
22534                 var cn = node.childNodes[0];
22535                 node.removeChild(cn);
22536                 node.parentNode.insertBefore(cn, node);
22537             }
22538             node.parentNode.removeChild(node);
22539             return;
22540         }
22541         
22542         if (!node.attributes || !node.attributes.length) {
22543             this.cleanUpChildren(node);
22544             return;
22545         }
22546         
22547         function cleanAttr(n,v)
22548         {
22549             
22550             if (v.match(/^\./) || v.match(/^\//)) {
22551                 return;
22552             }
22553             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22554                 return;
22555             }
22556             if (v.match(/^#/)) {
22557                 return;
22558             }
22559 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22560             node.removeAttribute(n);
22561             
22562         }
22563         
22564         var cwhite = this.cwhite;
22565         var cblack = this.cblack;
22566             
22567         function cleanStyle(n,v)
22568         {
22569             if (v.match(/expression/)) { //XSS?? should we even bother..
22570                 node.removeAttribute(n);
22571                 return;
22572             }
22573             
22574             var parts = v.split(/;/);
22575             var clean = [];
22576             
22577             Roo.each(parts, function(p) {
22578                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22579                 if (!p.length) {
22580                     return true;
22581                 }
22582                 var l = p.split(':').shift().replace(/\s+/g,'');
22583                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22584                 
22585                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22586 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22587                     //node.removeAttribute(n);
22588                     return true;
22589                 }
22590                 //Roo.log()
22591                 // only allow 'c whitelisted system attributes'
22592                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22593 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22594                     //node.removeAttribute(n);
22595                     return true;
22596                 }
22597                 
22598                 
22599                  
22600                 
22601                 clean.push(p);
22602                 return true;
22603             });
22604             if (clean.length) { 
22605                 node.setAttribute(n, clean.join(';'));
22606             } else {
22607                 node.removeAttribute(n);
22608             }
22609             
22610         }
22611         
22612         
22613         for (var i = node.attributes.length-1; i > -1 ; i--) {
22614             var a = node.attributes[i];
22615             //console.log(a);
22616             
22617             if (a.name.toLowerCase().substr(0,2)=='on')  {
22618                 node.removeAttribute(a.name);
22619                 continue;
22620             }
22621             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22622                 node.removeAttribute(a.name);
22623                 continue;
22624             }
22625             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22626                 cleanAttr(a.name,a.value); // fixme..
22627                 continue;
22628             }
22629             if (a.name == 'style') {
22630                 cleanStyle(a.name,a.value);
22631                 continue;
22632             }
22633             /// clean up MS crap..
22634             // tecnically this should be a list of valid class'es..
22635             
22636             
22637             if (a.name == 'class') {
22638                 if (a.value.match(/^Mso/)) {
22639                     node.className = '';
22640                 }
22641                 
22642                 if (a.value.match(/^body$/)) {
22643                     node.className = '';
22644                 }
22645                 continue;
22646             }
22647             
22648             // style cleanup!?
22649             // class cleanup?
22650             
22651         }
22652         
22653         
22654         this.cleanUpChildren(node);
22655         
22656         
22657     },
22658     
22659     /**
22660      * Clean up MS wordisms...
22661      */
22662     cleanWord : function(node)
22663     {
22664         
22665         
22666         if (!node) {
22667             this.cleanWord(this.doc.body);
22668             return;
22669         }
22670         if (node.nodeName == "#text") {
22671             // clean up silly Windows -- stuff?
22672             return; 
22673         }
22674         if (node.nodeName == "#comment") {
22675             node.parentNode.removeChild(node);
22676             // clean up silly Windows -- stuff?
22677             return; 
22678         }
22679         
22680         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22681             node.parentNode.removeChild(node);
22682             return;
22683         }
22684         
22685         // remove - but keep children..
22686         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22687             while (node.childNodes.length) {
22688                 var cn = node.childNodes[0];
22689                 node.removeChild(cn);
22690                 node.parentNode.insertBefore(cn, node);
22691             }
22692             node.parentNode.removeChild(node);
22693             this.iterateChildren(node, this.cleanWord);
22694             return;
22695         }
22696         // clean styles
22697         if (node.className.length) {
22698             
22699             var cn = node.className.split(/\W+/);
22700             var cna = [];
22701             Roo.each(cn, function(cls) {
22702                 if (cls.match(/Mso[a-zA-Z]+/)) {
22703                     return;
22704                 }
22705                 cna.push(cls);
22706             });
22707             node.className = cna.length ? cna.join(' ') : '';
22708             if (!cna.length) {
22709                 node.removeAttribute("class");
22710             }
22711         }
22712         
22713         if (node.hasAttribute("lang")) {
22714             node.removeAttribute("lang");
22715         }
22716         
22717         if (node.hasAttribute("style")) {
22718             
22719             var styles = node.getAttribute("style").split(";");
22720             var nstyle = [];
22721             Roo.each(styles, function(s) {
22722                 if (!s.match(/:/)) {
22723                     return;
22724                 }
22725                 var kv = s.split(":");
22726                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22727                     return;
22728                 }
22729                 // what ever is left... we allow.
22730                 nstyle.push(s);
22731             });
22732             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22733             if (!nstyle.length) {
22734                 node.removeAttribute('style');
22735             }
22736         }
22737         this.iterateChildren(node, this.cleanWord);
22738         
22739         
22740         
22741     },
22742     /**
22743      * iterateChildren of a Node, calling fn each time, using this as the scole..
22744      * @param {DomNode} node node to iterate children of.
22745      * @param {Function} fn method of this class to call on each item.
22746      */
22747     iterateChildren : function(node, fn)
22748     {
22749         if (!node.childNodes.length) {
22750                 return;
22751         }
22752         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22753            fn.call(this, node.childNodes[i])
22754         }
22755     },
22756     
22757     
22758     /**
22759      * cleanTableWidths.
22760      *
22761      * Quite often pasting from word etc.. results in tables with column and widths.
22762      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22763      *
22764      */
22765     cleanTableWidths : function(node)
22766     {
22767          
22768          
22769         if (!node) {
22770             this.cleanTableWidths(this.doc.body);
22771             return;
22772         }
22773         
22774         // ignore list...
22775         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22776             return; 
22777         }
22778         Roo.log(node.tagName);
22779         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22780             this.iterateChildren(node, this.cleanTableWidths);
22781             return;
22782         }
22783         if (node.hasAttribute('width')) {
22784             node.removeAttribute('width');
22785         }
22786         
22787          
22788         if (node.hasAttribute("style")) {
22789             // pretty basic...
22790             
22791             var styles = node.getAttribute("style").split(";");
22792             var nstyle = [];
22793             Roo.each(styles, function(s) {
22794                 if (!s.match(/:/)) {
22795                     return;
22796                 }
22797                 var kv = s.split(":");
22798                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22799                     return;
22800                 }
22801                 // what ever is left... we allow.
22802                 nstyle.push(s);
22803             });
22804             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22805             if (!nstyle.length) {
22806                 node.removeAttribute('style');
22807             }
22808         }
22809         
22810         this.iterateChildren(node, this.cleanTableWidths);
22811         
22812         
22813     },
22814     
22815     
22816     
22817     
22818     domToHTML : function(currentElement, depth, nopadtext) {
22819         
22820         depth = depth || 0;
22821         nopadtext = nopadtext || false;
22822     
22823         if (!currentElement) {
22824             return this.domToHTML(this.doc.body);
22825         }
22826         
22827         //Roo.log(currentElement);
22828         var j;
22829         var allText = false;
22830         var nodeName = currentElement.nodeName;
22831         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22832         
22833         if  (nodeName == '#text') {
22834             
22835             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22836         }
22837         
22838         
22839         var ret = '';
22840         if (nodeName != 'BODY') {
22841              
22842             var i = 0;
22843             // Prints the node tagName, such as <A>, <IMG>, etc
22844             if (tagName) {
22845                 var attr = [];
22846                 for(i = 0; i < currentElement.attributes.length;i++) {
22847                     // quoting?
22848                     var aname = currentElement.attributes.item(i).name;
22849                     if (!currentElement.attributes.item(i).value.length) {
22850                         continue;
22851                     }
22852                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22853                 }
22854                 
22855                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22856             } 
22857             else {
22858                 
22859                 // eack
22860             }
22861         } else {
22862             tagName = false;
22863         }
22864         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22865             return ret;
22866         }
22867         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22868             nopadtext = true;
22869         }
22870         
22871         
22872         // Traverse the tree
22873         i = 0;
22874         var currentElementChild = currentElement.childNodes.item(i);
22875         var allText = true;
22876         var innerHTML  = '';
22877         lastnode = '';
22878         while (currentElementChild) {
22879             // Formatting code (indent the tree so it looks nice on the screen)
22880             var nopad = nopadtext;
22881             if (lastnode == 'SPAN') {
22882                 nopad  = true;
22883             }
22884             // text
22885             if  (currentElementChild.nodeName == '#text') {
22886                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22887                 toadd = nopadtext ? toadd : toadd.trim();
22888                 if (!nopad && toadd.length > 80) {
22889                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22890                 }
22891                 innerHTML  += toadd;
22892                 
22893                 i++;
22894                 currentElementChild = currentElement.childNodes.item(i);
22895                 lastNode = '';
22896                 continue;
22897             }
22898             allText = false;
22899             
22900             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22901                 
22902             // Recursively traverse the tree structure of the child node
22903             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22904             lastnode = currentElementChild.nodeName;
22905             i++;
22906             currentElementChild=currentElement.childNodes.item(i);
22907         }
22908         
22909         ret += innerHTML;
22910         
22911         if (!allText) {
22912                 // The remaining code is mostly for formatting the tree
22913             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22914         }
22915         
22916         
22917         if (tagName) {
22918             ret+= "</"+tagName+">";
22919         }
22920         return ret;
22921         
22922     },
22923         
22924     applyBlacklists : function()
22925     {
22926         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22927         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22928         
22929         this.white = [];
22930         this.black = [];
22931         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22932             if (b.indexOf(tag) > -1) {
22933                 return;
22934             }
22935             this.white.push(tag);
22936             
22937         }, this);
22938         
22939         Roo.each(w, function(tag) {
22940             if (b.indexOf(tag) > -1) {
22941                 return;
22942             }
22943             if (this.white.indexOf(tag) > -1) {
22944                 return;
22945             }
22946             this.white.push(tag);
22947             
22948         }, this);
22949         
22950         
22951         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22952             if (w.indexOf(tag) > -1) {
22953                 return;
22954             }
22955             this.black.push(tag);
22956             
22957         }, this);
22958         
22959         Roo.each(b, function(tag) {
22960             if (w.indexOf(tag) > -1) {
22961                 return;
22962             }
22963             if (this.black.indexOf(tag) > -1) {
22964                 return;
22965             }
22966             this.black.push(tag);
22967             
22968         }, this);
22969         
22970         
22971         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22972         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22973         
22974         this.cwhite = [];
22975         this.cblack = [];
22976         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22977             if (b.indexOf(tag) > -1) {
22978                 return;
22979             }
22980             this.cwhite.push(tag);
22981             
22982         }, this);
22983         
22984         Roo.each(w, function(tag) {
22985             if (b.indexOf(tag) > -1) {
22986                 return;
22987             }
22988             if (this.cwhite.indexOf(tag) > -1) {
22989                 return;
22990             }
22991             this.cwhite.push(tag);
22992             
22993         }, this);
22994         
22995         
22996         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22997             if (w.indexOf(tag) > -1) {
22998                 return;
22999             }
23000             this.cblack.push(tag);
23001             
23002         }, this);
23003         
23004         Roo.each(b, function(tag) {
23005             if (w.indexOf(tag) > -1) {
23006                 return;
23007             }
23008             if (this.cblack.indexOf(tag) > -1) {
23009                 return;
23010             }
23011             this.cblack.push(tag);
23012             
23013         }, this);
23014     },
23015     
23016     setStylesheets : function(stylesheets)
23017     {
23018         if(typeof(stylesheets) == 'string'){
23019             Roo.get(this.iframe.contentDocument.head).createChild({
23020                 tag : 'link',
23021                 rel : 'stylesheet',
23022                 type : 'text/css',
23023                 href : stylesheets
23024             });
23025             
23026             return;
23027         }
23028         var _this = this;
23029      
23030         Roo.each(stylesheets, function(s) {
23031             if(!s.length){
23032                 return;
23033             }
23034             
23035             Roo.get(_this.iframe.contentDocument.head).createChild({
23036                 tag : 'link',
23037                 rel : 'stylesheet',
23038                 type : 'text/css',
23039                 href : s
23040             });
23041         });
23042
23043         
23044     },
23045     
23046     removeStylesheets : function()
23047     {
23048         var _this = this;
23049         
23050         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23051             s.remove();
23052         });
23053     },
23054     
23055     setStyle : function(style)
23056     {
23057         Roo.get(this.iframe.contentDocument.head).createChild({
23058             tag : 'style',
23059             type : 'text/css',
23060             html : style
23061         });
23062
23063         return;
23064     }
23065     
23066     // hide stuff that is not compatible
23067     /**
23068      * @event blur
23069      * @hide
23070      */
23071     /**
23072      * @event change
23073      * @hide
23074      */
23075     /**
23076      * @event focus
23077      * @hide
23078      */
23079     /**
23080      * @event specialkey
23081      * @hide
23082      */
23083     /**
23084      * @cfg {String} fieldClass @hide
23085      */
23086     /**
23087      * @cfg {String} focusClass @hide
23088      */
23089     /**
23090      * @cfg {String} autoCreate @hide
23091      */
23092     /**
23093      * @cfg {String} inputType @hide
23094      */
23095     /**
23096      * @cfg {String} invalidClass @hide
23097      */
23098     /**
23099      * @cfg {String} invalidText @hide
23100      */
23101     /**
23102      * @cfg {String} msgFx @hide
23103      */
23104     /**
23105      * @cfg {String} validateOnBlur @hide
23106      */
23107 });
23108
23109 Roo.HtmlEditorCore.white = [
23110         'area', 'br', 'img', 'input', 'hr', 'wbr',
23111         
23112        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23113        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23114        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23115        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23116        'table',   'ul',         'xmp', 
23117        
23118        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23119       'thead',   'tr', 
23120      
23121       'dir', 'menu', 'ol', 'ul', 'dl',
23122        
23123       'embed',  'object'
23124 ];
23125
23126
23127 Roo.HtmlEditorCore.black = [
23128     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23129         'applet', // 
23130         'base',   'basefont', 'bgsound', 'blink',  'body', 
23131         'frame',  'frameset', 'head',    'html',   'ilayer', 
23132         'iframe', 'layer',  'link',     'meta',    'object',   
23133         'script', 'style' ,'title',  'xml' // clean later..
23134 ];
23135 Roo.HtmlEditorCore.clean = [
23136     'script', 'style', 'title', 'xml'
23137 ];
23138 Roo.HtmlEditorCore.remove = [
23139     'font'
23140 ];
23141 // attributes..
23142
23143 Roo.HtmlEditorCore.ablack = [
23144     'on'
23145 ];
23146     
23147 Roo.HtmlEditorCore.aclean = [ 
23148     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23149 ];
23150
23151 // protocols..
23152 Roo.HtmlEditorCore.pwhite= [
23153         'http',  'https',  'mailto'
23154 ];
23155
23156 // white listed style attributes.
23157 Roo.HtmlEditorCore.cwhite= [
23158       //  'text-align', /// default is to allow most things..
23159       
23160          
23161 //        'font-size'//??
23162 ];
23163
23164 // black listed style attributes.
23165 Roo.HtmlEditorCore.cblack= [
23166       //  'font-size' -- this can be set by the project 
23167 ];
23168
23169
23170 Roo.HtmlEditorCore.swapCodes   =[ 
23171     [    8211, "--" ], 
23172     [    8212, "--" ], 
23173     [    8216,  "'" ],  
23174     [    8217, "'" ],  
23175     [    8220, '"' ],  
23176     [    8221, '"' ],  
23177     [    8226, "*" ],  
23178     [    8230, "..." ]
23179 ]; 
23180
23181     /*
23182  * - LGPL
23183  *
23184  * HtmlEditor
23185  * 
23186  */
23187
23188 /**
23189  * @class Roo.bootstrap.HtmlEditor
23190  * @extends Roo.bootstrap.TextArea
23191  * Bootstrap HtmlEditor class
23192
23193  * @constructor
23194  * Create a new HtmlEditor
23195  * @param {Object} config The config object
23196  */
23197
23198 Roo.bootstrap.HtmlEditor = function(config){
23199     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23200     if (!this.toolbars) {
23201         this.toolbars = [];
23202     }
23203     
23204     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23205     this.addEvents({
23206             /**
23207              * @event initialize
23208              * Fires when the editor is fully initialized (including the iframe)
23209              * @param {HtmlEditor} this
23210              */
23211             initialize: true,
23212             /**
23213              * @event activate
23214              * Fires when the editor is first receives the focus. Any insertion must wait
23215              * until after this event.
23216              * @param {HtmlEditor} this
23217              */
23218             activate: true,
23219              /**
23220              * @event beforesync
23221              * Fires before the textarea is updated with content from the editor iframe. Return false
23222              * to cancel the sync.
23223              * @param {HtmlEditor} this
23224              * @param {String} html
23225              */
23226             beforesync: true,
23227              /**
23228              * @event beforepush
23229              * Fires before the iframe editor is updated with content from the textarea. Return false
23230              * to cancel the push.
23231              * @param {HtmlEditor} this
23232              * @param {String} html
23233              */
23234             beforepush: true,
23235              /**
23236              * @event sync
23237              * Fires when the textarea is updated with content from the editor iframe.
23238              * @param {HtmlEditor} this
23239              * @param {String} html
23240              */
23241             sync: true,
23242              /**
23243              * @event push
23244              * Fires when the iframe editor is updated with content from the textarea.
23245              * @param {HtmlEditor} this
23246              * @param {String} html
23247              */
23248             push: true,
23249              /**
23250              * @event editmodechange
23251              * Fires when the editor switches edit modes
23252              * @param {HtmlEditor} this
23253              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23254              */
23255             editmodechange: true,
23256             /**
23257              * @event editorevent
23258              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23259              * @param {HtmlEditor} this
23260              */
23261             editorevent: true,
23262             /**
23263              * @event firstfocus
23264              * Fires when on first focus - needed by toolbars..
23265              * @param {HtmlEditor} this
23266              */
23267             firstfocus: true,
23268             /**
23269              * @event autosave
23270              * Auto save the htmlEditor value as a file into Events
23271              * @param {HtmlEditor} this
23272              */
23273             autosave: true,
23274             /**
23275              * @event savedpreview
23276              * preview the saved version of htmlEditor
23277              * @param {HtmlEditor} this
23278              */
23279             savedpreview: true
23280         });
23281 };
23282
23283
23284 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23285     
23286     
23287       /**
23288      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23289      */
23290     toolbars : false,
23291     
23292      /**
23293     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23294     */
23295     btns : [],
23296    
23297      /**
23298      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23299      *                        Roo.resizable.
23300      */
23301     resizable : false,
23302      /**
23303      * @cfg {Number} height (in pixels)
23304      */   
23305     height: 300,
23306    /**
23307      * @cfg {Number} width (in pixels)
23308      */   
23309     width: false,
23310     
23311     /**
23312      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23313      * 
23314      */
23315     stylesheets: false,
23316     
23317     // id of frame..
23318     frameId: false,
23319     
23320     // private properties
23321     validationEvent : false,
23322     deferHeight: true,
23323     initialized : false,
23324     activated : false,
23325     
23326     onFocus : Roo.emptyFn,
23327     iframePad:3,
23328     hideMode:'offsets',
23329     
23330     tbContainer : false,
23331     
23332     bodyCls : '',
23333     
23334     toolbarContainer :function() {
23335         return this.wrap.select('.x-html-editor-tb',true).first();
23336     },
23337
23338     /**
23339      * Protected method that will not generally be called directly. It
23340      * is called when the editor creates its toolbar. Override this method if you need to
23341      * add custom toolbar buttons.
23342      * @param {HtmlEditor} editor
23343      */
23344     createToolbar : function(){
23345         Roo.log('renewing');
23346         Roo.log("create toolbars");
23347         
23348         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23349         this.toolbars[0].render(this.toolbarContainer());
23350         
23351         return;
23352         
23353 //        if (!editor.toolbars || !editor.toolbars.length) {
23354 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23355 //        }
23356 //        
23357 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23358 //            editor.toolbars[i] = Roo.factory(
23359 //                    typeof(editor.toolbars[i]) == 'string' ?
23360 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23361 //                Roo.bootstrap.HtmlEditor);
23362 //            editor.toolbars[i].init(editor);
23363 //        }
23364     },
23365
23366      
23367     // private
23368     onRender : function(ct, position)
23369     {
23370        // Roo.log("Call onRender: " + this.xtype);
23371         var _t = this;
23372         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23373       
23374         this.wrap = this.inputEl().wrap({
23375             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23376         });
23377         
23378         this.editorcore.onRender(ct, position);
23379          
23380         if (this.resizable) {
23381             this.resizeEl = new Roo.Resizable(this.wrap, {
23382                 pinned : true,
23383                 wrap: true,
23384                 dynamic : true,
23385                 minHeight : this.height,
23386                 height: this.height,
23387                 handles : this.resizable,
23388                 width: this.width,
23389                 listeners : {
23390                     resize : function(r, w, h) {
23391                         _t.onResize(w,h); // -something
23392                     }
23393                 }
23394             });
23395             
23396         }
23397         this.createToolbar(this);
23398        
23399         
23400         if(!this.width && this.resizable){
23401             this.setSize(this.wrap.getSize());
23402         }
23403         if (this.resizeEl) {
23404             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23405             // should trigger onReize..
23406         }
23407         
23408     },
23409
23410     // private
23411     onResize : function(w, h)
23412     {
23413         Roo.log('resize: ' +w + ',' + h );
23414         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23415         var ew = false;
23416         var eh = false;
23417         
23418         if(this.inputEl() ){
23419             if(typeof w == 'number'){
23420                 var aw = w - this.wrap.getFrameWidth('lr');
23421                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23422                 ew = aw;
23423             }
23424             if(typeof h == 'number'){
23425                  var tbh = -11;  // fixme it needs to tool bar size!
23426                 for (var i =0; i < this.toolbars.length;i++) {
23427                     // fixme - ask toolbars for heights?
23428                     tbh += this.toolbars[i].el.getHeight();
23429                     //if (this.toolbars[i].footer) {
23430                     //    tbh += this.toolbars[i].footer.el.getHeight();
23431                     //}
23432                 }
23433               
23434                 
23435                 
23436                 
23437                 
23438                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23439                 ah -= 5; // knock a few pixes off for look..
23440                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23441                 var eh = ah;
23442             }
23443         }
23444         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23445         this.editorcore.onResize(ew,eh);
23446         
23447     },
23448
23449     /**
23450      * Toggles the editor between standard and source edit mode.
23451      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23452      */
23453     toggleSourceEdit : function(sourceEditMode)
23454     {
23455         this.editorcore.toggleSourceEdit(sourceEditMode);
23456         
23457         if(this.editorcore.sourceEditMode){
23458             Roo.log('editor - showing textarea');
23459             
23460 //            Roo.log('in');
23461 //            Roo.log(this.syncValue());
23462             this.syncValue();
23463             this.inputEl().removeClass(['hide', 'x-hidden']);
23464             this.inputEl().dom.removeAttribute('tabIndex');
23465             this.inputEl().focus();
23466         }else{
23467             Roo.log('editor - hiding textarea');
23468 //            Roo.log('out')
23469 //            Roo.log(this.pushValue()); 
23470             this.pushValue();
23471             
23472             this.inputEl().addClass(['hide', 'x-hidden']);
23473             this.inputEl().dom.setAttribute('tabIndex', -1);
23474             //this.deferFocus();
23475         }
23476          
23477         if(this.resizable){
23478             this.setSize(this.wrap.getSize());
23479         }
23480         
23481         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23482     },
23483  
23484     // private (for BoxComponent)
23485     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23486
23487     // private (for BoxComponent)
23488     getResizeEl : function(){
23489         return this.wrap;
23490     },
23491
23492     // private (for BoxComponent)
23493     getPositionEl : function(){
23494         return this.wrap;
23495     },
23496
23497     // private
23498     initEvents : function(){
23499         this.originalValue = this.getValue();
23500     },
23501
23502 //    /**
23503 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23504 //     * @method
23505 //     */
23506 //    markInvalid : Roo.emptyFn,
23507 //    /**
23508 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23509 //     * @method
23510 //     */
23511 //    clearInvalid : Roo.emptyFn,
23512
23513     setValue : function(v){
23514         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23515         this.editorcore.pushValue();
23516     },
23517
23518      
23519     // private
23520     deferFocus : function(){
23521         this.focus.defer(10, this);
23522     },
23523
23524     // doc'ed in Field
23525     focus : function(){
23526         this.editorcore.focus();
23527         
23528     },
23529       
23530
23531     // private
23532     onDestroy : function(){
23533         
23534         
23535         
23536         if(this.rendered){
23537             
23538             for (var i =0; i < this.toolbars.length;i++) {
23539                 // fixme - ask toolbars for heights?
23540                 this.toolbars[i].onDestroy();
23541             }
23542             
23543             this.wrap.dom.innerHTML = '';
23544             this.wrap.remove();
23545         }
23546     },
23547
23548     // private
23549     onFirstFocus : function(){
23550         //Roo.log("onFirstFocus");
23551         this.editorcore.onFirstFocus();
23552          for (var i =0; i < this.toolbars.length;i++) {
23553             this.toolbars[i].onFirstFocus();
23554         }
23555         
23556     },
23557     
23558     // private
23559     syncValue : function()
23560     {   
23561         this.editorcore.syncValue();
23562     },
23563     
23564     pushValue : function()
23565     {   
23566         this.editorcore.pushValue();
23567     }
23568      
23569     
23570     // hide stuff that is not compatible
23571     /**
23572      * @event blur
23573      * @hide
23574      */
23575     /**
23576      * @event change
23577      * @hide
23578      */
23579     /**
23580      * @event focus
23581      * @hide
23582      */
23583     /**
23584      * @event specialkey
23585      * @hide
23586      */
23587     /**
23588      * @cfg {String} fieldClass @hide
23589      */
23590     /**
23591      * @cfg {String} focusClass @hide
23592      */
23593     /**
23594      * @cfg {String} autoCreate @hide
23595      */
23596     /**
23597      * @cfg {String} inputType @hide
23598      */
23599     /**
23600      * @cfg {String} invalidClass @hide
23601      */
23602     /**
23603      * @cfg {String} invalidText @hide
23604      */
23605     /**
23606      * @cfg {String} msgFx @hide
23607      */
23608     /**
23609      * @cfg {String} validateOnBlur @hide
23610      */
23611 });
23612  
23613     
23614    
23615    
23616    
23617       
23618 Roo.namespace('Roo.bootstrap.htmleditor');
23619 /**
23620  * @class Roo.bootstrap.HtmlEditorToolbar1
23621  * Basic Toolbar
23622  * 
23623  * Usage:
23624  *
23625  new Roo.bootstrap.HtmlEditor({
23626     ....
23627     toolbars : [
23628         new Roo.bootstrap.HtmlEditorToolbar1({
23629             disable : { fonts: 1 , format: 1, ..., ... , ...],
23630             btns : [ .... ]
23631         })
23632     }
23633      
23634  * 
23635  * @cfg {Object} disable List of elements to disable..
23636  * @cfg {Array} btns List of additional buttons.
23637  * 
23638  * 
23639  * NEEDS Extra CSS? 
23640  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23641  */
23642  
23643 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23644 {
23645     
23646     Roo.apply(this, config);
23647     
23648     // default disabled, based on 'good practice'..
23649     this.disable = this.disable || {};
23650     Roo.applyIf(this.disable, {
23651         fontSize : true,
23652         colors : true,
23653         specialElements : true
23654     });
23655     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23656     
23657     this.editor = config.editor;
23658     this.editorcore = config.editor.editorcore;
23659     
23660     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23661     
23662     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23663     // dont call parent... till later.
23664 }
23665 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23666      
23667     bar : true,
23668     
23669     editor : false,
23670     editorcore : false,
23671     
23672     
23673     formats : [
23674         "p" ,  
23675         "h1","h2","h3","h4","h5","h6", 
23676         "pre", "code", 
23677         "abbr", "acronym", "address", "cite", "samp", "var",
23678         'div','span'
23679     ],
23680     
23681     onRender : function(ct, position)
23682     {
23683        // Roo.log("Call onRender: " + this.xtype);
23684         
23685        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23686        Roo.log(this.el);
23687        this.el.dom.style.marginBottom = '0';
23688        var _this = this;
23689        var editorcore = this.editorcore;
23690        var editor= this.editor;
23691        
23692        var children = [];
23693        var btn = function(id,cmd , toggle, handler, html){
23694        
23695             var  event = toggle ? 'toggle' : 'click';
23696        
23697             var a = {
23698                 size : 'sm',
23699                 xtype: 'Button',
23700                 xns: Roo.bootstrap,
23701                 glyphicon : id,
23702                 cmd : id || cmd,
23703                 enableToggle:toggle !== false,
23704                 html : html || '',
23705                 pressed : toggle ? false : null,
23706                 listeners : {}
23707             };
23708             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23709                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23710             };
23711             children.push(a);
23712             return a;
23713        }
23714        
23715     //    var cb_box = function...
23716         
23717         var style = {
23718                 xtype: 'Button',
23719                 size : 'sm',
23720                 xns: Roo.bootstrap,
23721                 glyphicon : 'font',
23722                 //html : 'submit'
23723                 menu : {
23724                     xtype: 'Menu',
23725                     xns: Roo.bootstrap,
23726                     items:  []
23727                 }
23728         };
23729         Roo.each(this.formats, function(f) {
23730             style.menu.items.push({
23731                 xtype :'MenuItem',
23732                 xns: Roo.bootstrap,
23733                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23734                 tagname : f,
23735                 listeners : {
23736                     click : function()
23737                     {
23738                         editorcore.insertTag(this.tagname);
23739                         editor.focus();
23740                     }
23741                 }
23742                 
23743             });
23744         });
23745         children.push(style);   
23746         
23747         btn('bold',false,true);
23748         btn('italic',false,true);
23749         btn('align-left', 'justifyleft',true);
23750         btn('align-center', 'justifycenter',true);
23751         btn('align-right' , 'justifyright',true);
23752         btn('link', false, false, function(btn) {
23753             //Roo.log("create link?");
23754             var url = prompt(this.createLinkText, this.defaultLinkValue);
23755             if(url && url != 'http:/'+'/'){
23756                 this.editorcore.relayCmd('createlink', url);
23757             }
23758         }),
23759         btn('list','insertunorderedlist',true);
23760         btn('pencil', false,true, function(btn){
23761                 Roo.log(this);
23762                 this.toggleSourceEdit(btn.pressed);
23763         });
23764         
23765         if (this.editor.btns.length > 0) {
23766             for (var i = 0; i<this.editor.btns.length; i++) {
23767                 children.push(this.editor.btns[i]);
23768             }
23769         }
23770         
23771         /*
23772         var cog = {
23773                 xtype: 'Button',
23774                 size : 'sm',
23775                 xns: Roo.bootstrap,
23776                 glyphicon : 'cog',
23777                 //html : 'submit'
23778                 menu : {
23779                     xtype: 'Menu',
23780                     xns: Roo.bootstrap,
23781                     items:  []
23782                 }
23783         };
23784         
23785         cog.menu.items.push({
23786             xtype :'MenuItem',
23787             xns: Roo.bootstrap,
23788             html : Clean styles,
23789             tagname : f,
23790             listeners : {
23791                 click : function()
23792                 {
23793                     editorcore.insertTag(this.tagname);
23794                     editor.focus();
23795                 }
23796             }
23797             
23798         });
23799        */
23800         
23801          
23802        this.xtype = 'NavSimplebar';
23803         
23804         for(var i=0;i< children.length;i++) {
23805             
23806             this.buttons.add(this.addxtypeChild(children[i]));
23807             
23808         }
23809         
23810         editor.on('editorevent', this.updateToolbar, this);
23811     },
23812     onBtnClick : function(id)
23813     {
23814        this.editorcore.relayCmd(id);
23815        this.editorcore.focus();
23816     },
23817     
23818     /**
23819      * Protected method that will not generally be called directly. It triggers
23820      * a toolbar update by reading the markup state of the current selection in the editor.
23821      */
23822     updateToolbar: function(){
23823
23824         if(!this.editorcore.activated){
23825             this.editor.onFirstFocus(); // is this neeed?
23826             return;
23827         }
23828
23829         var btns = this.buttons; 
23830         var doc = this.editorcore.doc;
23831         btns.get('bold').setActive(doc.queryCommandState('bold'));
23832         btns.get('italic').setActive(doc.queryCommandState('italic'));
23833         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23834         
23835         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23836         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23837         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23838         
23839         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23840         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23841          /*
23842         
23843         var ans = this.editorcore.getAllAncestors();
23844         if (this.formatCombo) {
23845             
23846             
23847             var store = this.formatCombo.store;
23848             this.formatCombo.setValue("");
23849             for (var i =0; i < ans.length;i++) {
23850                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23851                     // select it..
23852                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23853                     break;
23854                 }
23855             }
23856         }
23857         
23858         
23859         
23860         // hides menus... - so this cant be on a menu...
23861         Roo.bootstrap.MenuMgr.hideAll();
23862         */
23863         Roo.bootstrap.MenuMgr.hideAll();
23864         //this.editorsyncValue();
23865     },
23866     onFirstFocus: function() {
23867         this.buttons.each(function(item){
23868            item.enable();
23869         });
23870     },
23871     toggleSourceEdit : function(sourceEditMode){
23872         
23873           
23874         if(sourceEditMode){
23875             Roo.log("disabling buttons");
23876            this.buttons.each( function(item){
23877                 if(item.cmd != 'pencil'){
23878                     item.disable();
23879                 }
23880             });
23881           
23882         }else{
23883             Roo.log("enabling buttons");
23884             if(this.editorcore.initialized){
23885                 this.buttons.each( function(item){
23886                     item.enable();
23887                 });
23888             }
23889             
23890         }
23891         Roo.log("calling toggole on editor");
23892         // tell the editor that it's been pressed..
23893         this.editor.toggleSourceEdit(sourceEditMode);
23894        
23895     }
23896 });
23897
23898
23899
23900
23901
23902 /**
23903  * @class Roo.bootstrap.Table.AbstractSelectionModel
23904  * @extends Roo.util.Observable
23905  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23906  * implemented by descendant classes.  This class should not be directly instantiated.
23907  * @constructor
23908  */
23909 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23910     this.locked = false;
23911     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23912 };
23913
23914
23915 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23916     /** @ignore Called by the grid automatically. Do not call directly. */
23917     init : function(grid){
23918         this.grid = grid;
23919         this.initEvents();
23920     },
23921
23922     /**
23923      * Locks the selections.
23924      */
23925     lock : function(){
23926         this.locked = true;
23927     },
23928
23929     /**
23930      * Unlocks the selections.
23931      */
23932     unlock : function(){
23933         this.locked = false;
23934     },
23935
23936     /**
23937      * Returns true if the selections are locked.
23938      * @return {Boolean}
23939      */
23940     isLocked : function(){
23941         return this.locked;
23942     }
23943 });
23944 /**
23945  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23946  * @class Roo.bootstrap.Table.RowSelectionModel
23947  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23948  * It supports multiple selections and keyboard selection/navigation. 
23949  * @constructor
23950  * @param {Object} config
23951  */
23952
23953 Roo.bootstrap.Table.RowSelectionModel = function(config){
23954     Roo.apply(this, config);
23955     this.selections = new Roo.util.MixedCollection(false, function(o){
23956         return o.id;
23957     });
23958
23959     this.last = false;
23960     this.lastActive = false;
23961
23962     this.addEvents({
23963         /**
23964              * @event selectionchange
23965              * Fires when the selection changes
23966              * @param {SelectionModel} this
23967              */
23968             "selectionchange" : true,
23969         /**
23970              * @event afterselectionchange
23971              * Fires after the selection changes (eg. by key press or clicking)
23972              * @param {SelectionModel} this
23973              */
23974             "afterselectionchange" : true,
23975         /**
23976              * @event beforerowselect
23977              * Fires when a row is selected being selected, return false to cancel.
23978              * @param {SelectionModel} this
23979              * @param {Number} rowIndex The selected index
23980              * @param {Boolean} keepExisting False if other selections will be cleared
23981              */
23982             "beforerowselect" : true,
23983         /**
23984              * @event rowselect
23985              * Fires when a row is selected.
23986              * @param {SelectionModel} this
23987              * @param {Number} rowIndex The selected index
23988              * @param {Roo.data.Record} r The record
23989              */
23990             "rowselect" : true,
23991         /**
23992              * @event rowdeselect
23993              * Fires when a row is deselected.
23994              * @param {SelectionModel} this
23995              * @param {Number} rowIndex The selected index
23996              */
23997         "rowdeselect" : true
23998     });
23999     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24000     this.locked = false;
24001  };
24002
24003 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24004     /**
24005      * @cfg {Boolean} singleSelect
24006      * True to allow selection of only one row at a time (defaults to false)
24007      */
24008     singleSelect : false,
24009
24010     // private
24011     initEvents : function()
24012     {
24013
24014         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24015         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24016         //}else{ // allow click to work like normal
24017          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24018         //}
24019         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24020         this.grid.on("rowclick", this.handleMouseDown, this);
24021         
24022         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24023             "up" : function(e){
24024                 if(!e.shiftKey){
24025                     this.selectPrevious(e.shiftKey);
24026                 }else if(this.last !== false && this.lastActive !== false){
24027                     var last = this.last;
24028                     this.selectRange(this.last,  this.lastActive-1);
24029                     this.grid.getView().focusRow(this.lastActive);
24030                     if(last !== false){
24031                         this.last = last;
24032                     }
24033                 }else{
24034                     this.selectFirstRow();
24035                 }
24036                 this.fireEvent("afterselectionchange", this);
24037             },
24038             "down" : function(e){
24039                 if(!e.shiftKey){
24040                     this.selectNext(e.shiftKey);
24041                 }else if(this.last !== false && this.lastActive !== false){
24042                     var last = this.last;
24043                     this.selectRange(this.last,  this.lastActive+1);
24044                     this.grid.getView().focusRow(this.lastActive);
24045                     if(last !== false){
24046                         this.last = last;
24047                     }
24048                 }else{
24049                     this.selectFirstRow();
24050                 }
24051                 this.fireEvent("afterselectionchange", this);
24052             },
24053             scope: this
24054         });
24055         this.grid.store.on('load', function(){
24056             this.selections.clear();
24057         },this);
24058         /*
24059         var view = this.grid.view;
24060         view.on("refresh", this.onRefresh, this);
24061         view.on("rowupdated", this.onRowUpdated, this);
24062         view.on("rowremoved", this.onRemove, this);
24063         */
24064     },
24065
24066     // private
24067     onRefresh : function()
24068     {
24069         var ds = this.grid.store, i, v = this.grid.view;
24070         var s = this.selections;
24071         s.each(function(r){
24072             if((i = ds.indexOfId(r.id)) != -1){
24073                 v.onRowSelect(i);
24074             }else{
24075                 s.remove(r);
24076             }
24077         });
24078     },
24079
24080     // private
24081     onRemove : function(v, index, r){
24082         this.selections.remove(r);
24083     },
24084
24085     // private
24086     onRowUpdated : function(v, index, r){
24087         if(this.isSelected(r)){
24088             v.onRowSelect(index);
24089         }
24090     },
24091
24092     /**
24093      * Select records.
24094      * @param {Array} records The records to select
24095      * @param {Boolean} keepExisting (optional) True to keep existing selections
24096      */
24097     selectRecords : function(records, keepExisting)
24098     {
24099         if(!keepExisting){
24100             this.clearSelections();
24101         }
24102             var ds = this.grid.store;
24103         for(var i = 0, len = records.length; i < len; i++){
24104             this.selectRow(ds.indexOf(records[i]), true);
24105         }
24106     },
24107
24108     /**
24109      * Gets the number of selected rows.
24110      * @return {Number}
24111      */
24112     getCount : function(){
24113         return this.selections.length;
24114     },
24115
24116     /**
24117      * Selects the first row in the grid.
24118      */
24119     selectFirstRow : function(){
24120         this.selectRow(0);
24121     },
24122
24123     /**
24124      * Select the last row.
24125      * @param {Boolean} keepExisting (optional) True to keep existing selections
24126      */
24127     selectLastRow : function(keepExisting){
24128         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24129         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24130     },
24131
24132     /**
24133      * Selects the row immediately following the last selected row.
24134      * @param {Boolean} keepExisting (optional) True to keep existing selections
24135      */
24136     selectNext : function(keepExisting)
24137     {
24138             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24139             this.selectRow(this.last+1, keepExisting);
24140             this.grid.getView().focusRow(this.last);
24141         }
24142     },
24143
24144     /**
24145      * Selects the row that precedes the last selected row.
24146      * @param {Boolean} keepExisting (optional) True to keep existing selections
24147      */
24148     selectPrevious : function(keepExisting){
24149         if(this.last){
24150             this.selectRow(this.last-1, keepExisting);
24151             this.grid.getView().focusRow(this.last);
24152         }
24153     },
24154
24155     /**
24156      * Returns the selected records
24157      * @return {Array} Array of selected records
24158      */
24159     getSelections : function(){
24160         return [].concat(this.selections.items);
24161     },
24162
24163     /**
24164      * Returns the first selected record.
24165      * @return {Record}
24166      */
24167     getSelected : function(){
24168         return this.selections.itemAt(0);
24169     },
24170
24171
24172     /**
24173      * Clears all selections.
24174      */
24175     clearSelections : function(fast)
24176     {
24177         if(this.locked) {
24178             return;
24179         }
24180         if(fast !== true){
24181                 var ds = this.grid.store;
24182             var s = this.selections;
24183             s.each(function(r){
24184                 this.deselectRow(ds.indexOfId(r.id));
24185             }, this);
24186             s.clear();
24187         }else{
24188             this.selections.clear();
24189         }
24190         this.last = false;
24191     },
24192
24193
24194     /**
24195      * Selects all rows.
24196      */
24197     selectAll : function(){
24198         if(this.locked) {
24199             return;
24200         }
24201         this.selections.clear();
24202         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24203             this.selectRow(i, true);
24204         }
24205     },
24206
24207     /**
24208      * Returns True if there is a selection.
24209      * @return {Boolean}
24210      */
24211     hasSelection : function(){
24212         return this.selections.length > 0;
24213     },
24214
24215     /**
24216      * Returns True if the specified row is selected.
24217      * @param {Number/Record} record The record or index of the record to check
24218      * @return {Boolean}
24219      */
24220     isSelected : function(index){
24221             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24222         return (r && this.selections.key(r.id) ? true : false);
24223     },
24224
24225     /**
24226      * Returns True if the specified record id is selected.
24227      * @param {String} id The id of record to check
24228      * @return {Boolean}
24229      */
24230     isIdSelected : function(id){
24231         return (this.selections.key(id) ? true : false);
24232     },
24233
24234
24235     // private
24236     handleMouseDBClick : function(e, t){
24237         
24238     },
24239     // private
24240     handleMouseDown : function(e, t)
24241     {
24242             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24243         if(this.isLocked() || rowIndex < 0 ){
24244             return;
24245         };
24246         if(e.shiftKey && this.last !== false){
24247             var last = this.last;
24248             this.selectRange(last, rowIndex, e.ctrlKey);
24249             this.last = last; // reset the last
24250             t.focus();
24251     
24252         }else{
24253             var isSelected = this.isSelected(rowIndex);
24254             //Roo.log("select row:" + rowIndex);
24255             if(isSelected){
24256                 this.deselectRow(rowIndex);
24257             } else {
24258                         this.selectRow(rowIndex, true);
24259             }
24260     
24261             /*
24262                 if(e.button !== 0 && isSelected){
24263                 alert('rowIndex 2: ' + rowIndex);
24264                     view.focusRow(rowIndex);
24265                 }else if(e.ctrlKey && isSelected){
24266                     this.deselectRow(rowIndex);
24267                 }else if(!isSelected){
24268                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24269                     view.focusRow(rowIndex);
24270                 }
24271             */
24272         }
24273         this.fireEvent("afterselectionchange", this);
24274     },
24275     // private
24276     handleDragableRowClick :  function(grid, rowIndex, e) 
24277     {
24278         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24279             this.selectRow(rowIndex, false);
24280             grid.view.focusRow(rowIndex);
24281              this.fireEvent("afterselectionchange", this);
24282         }
24283     },
24284     
24285     /**
24286      * Selects multiple rows.
24287      * @param {Array} rows Array of the indexes of the row to select
24288      * @param {Boolean} keepExisting (optional) True to keep existing selections
24289      */
24290     selectRows : function(rows, keepExisting){
24291         if(!keepExisting){
24292             this.clearSelections();
24293         }
24294         for(var i = 0, len = rows.length; i < len; i++){
24295             this.selectRow(rows[i], true);
24296         }
24297     },
24298
24299     /**
24300      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24301      * @param {Number} startRow The index of the first row in the range
24302      * @param {Number} endRow The index of the last row in the range
24303      * @param {Boolean} keepExisting (optional) True to retain existing selections
24304      */
24305     selectRange : function(startRow, endRow, keepExisting){
24306         if(this.locked) {
24307             return;
24308         }
24309         if(!keepExisting){
24310             this.clearSelections();
24311         }
24312         if(startRow <= endRow){
24313             for(var i = startRow; i <= endRow; i++){
24314                 this.selectRow(i, true);
24315             }
24316         }else{
24317             for(var i = startRow; i >= endRow; i--){
24318                 this.selectRow(i, true);
24319             }
24320         }
24321     },
24322
24323     /**
24324      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24325      * @param {Number} startRow The index of the first row in the range
24326      * @param {Number} endRow The index of the last row in the range
24327      */
24328     deselectRange : function(startRow, endRow, preventViewNotify){
24329         if(this.locked) {
24330             return;
24331         }
24332         for(var i = startRow; i <= endRow; i++){
24333             this.deselectRow(i, preventViewNotify);
24334         }
24335     },
24336
24337     /**
24338      * Selects a row.
24339      * @param {Number} row The index of the row to select
24340      * @param {Boolean} keepExisting (optional) True to keep existing selections
24341      */
24342     selectRow : function(index, keepExisting, preventViewNotify)
24343     {
24344             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24345             return;
24346         }
24347         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24348             if(!keepExisting || this.singleSelect){
24349                 this.clearSelections();
24350             }
24351             
24352             var r = this.grid.store.getAt(index);
24353             //console.log('selectRow - record id :' + r.id);
24354             
24355             this.selections.add(r);
24356             this.last = this.lastActive = index;
24357             if(!preventViewNotify){
24358                 var proxy = new Roo.Element(
24359                                 this.grid.getRowDom(index)
24360                 );
24361                 proxy.addClass('bg-info info');
24362             }
24363             this.fireEvent("rowselect", this, index, r);
24364             this.fireEvent("selectionchange", this);
24365         }
24366     },
24367
24368     /**
24369      * Deselects a row.
24370      * @param {Number} row The index of the row to deselect
24371      */
24372     deselectRow : function(index, preventViewNotify)
24373     {
24374         if(this.locked) {
24375             return;
24376         }
24377         if(this.last == index){
24378             this.last = false;
24379         }
24380         if(this.lastActive == index){
24381             this.lastActive = false;
24382         }
24383         
24384         var r = this.grid.store.getAt(index);
24385         if (!r) {
24386             return;
24387         }
24388         
24389         this.selections.remove(r);
24390         //.console.log('deselectRow - record id :' + r.id);
24391         if(!preventViewNotify){
24392         
24393             var proxy = new Roo.Element(
24394                 this.grid.getRowDom(index)
24395             );
24396             proxy.removeClass('bg-info info');
24397         }
24398         this.fireEvent("rowdeselect", this, index);
24399         this.fireEvent("selectionchange", this);
24400     },
24401
24402     // private
24403     restoreLast : function(){
24404         if(this._last){
24405             this.last = this._last;
24406         }
24407     },
24408
24409     // private
24410     acceptsNav : function(row, col, cm){
24411         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24412     },
24413
24414     // private
24415     onEditorKey : function(field, e){
24416         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24417         if(k == e.TAB){
24418             e.stopEvent();
24419             ed.completeEdit();
24420             if(e.shiftKey){
24421                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24422             }else{
24423                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24424             }
24425         }else if(k == e.ENTER && !e.ctrlKey){
24426             e.stopEvent();
24427             ed.completeEdit();
24428             if(e.shiftKey){
24429                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24430             }else{
24431                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24432             }
24433         }else if(k == e.ESC){
24434             ed.cancelEdit();
24435         }
24436         if(newCell){
24437             g.startEditing(newCell[0], newCell[1]);
24438         }
24439     }
24440 });
24441 /*
24442  * Based on:
24443  * Ext JS Library 1.1.1
24444  * Copyright(c) 2006-2007, Ext JS, LLC.
24445  *
24446  * Originally Released Under LGPL - original licence link has changed is not relivant.
24447  *
24448  * Fork - LGPL
24449  * <script type="text/javascript">
24450  */
24451  
24452 /**
24453  * @class Roo.bootstrap.PagingToolbar
24454  * @extends Roo.bootstrap.NavSimplebar
24455  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24456  * @constructor
24457  * Create a new PagingToolbar
24458  * @param {Object} config The config object
24459  * @param {Roo.data.Store} store
24460  */
24461 Roo.bootstrap.PagingToolbar = function(config)
24462 {
24463     // old args format still supported... - xtype is prefered..
24464         // created from xtype...
24465     
24466     this.ds = config.dataSource;
24467     
24468     if (config.store && !this.ds) {
24469         this.store= Roo.factory(config.store, Roo.data);
24470         this.ds = this.store;
24471         this.ds.xmodule = this.xmodule || false;
24472     }
24473     
24474     this.toolbarItems = [];
24475     if (config.items) {
24476         this.toolbarItems = config.items;
24477     }
24478     
24479     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24480     
24481     this.cursor = 0;
24482     
24483     if (this.ds) { 
24484         this.bind(this.ds);
24485     }
24486     
24487     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24488     
24489 };
24490
24491 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24492     /**
24493      * @cfg {Roo.data.Store} dataSource
24494      * The underlying data store providing the paged data
24495      */
24496     /**
24497      * @cfg {String/HTMLElement/Element} container
24498      * container The id or element that will contain the toolbar
24499      */
24500     /**
24501      * @cfg {Boolean} displayInfo
24502      * True to display the displayMsg (defaults to false)
24503      */
24504     /**
24505      * @cfg {Number} pageSize
24506      * The number of records to display per page (defaults to 20)
24507      */
24508     pageSize: 20,
24509     /**
24510      * @cfg {String} displayMsg
24511      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24512      */
24513     displayMsg : 'Displaying {0} - {1} of {2}',
24514     /**
24515      * @cfg {String} emptyMsg
24516      * The message to display when no records are found (defaults to "No data to display")
24517      */
24518     emptyMsg : 'No data to display',
24519     /**
24520      * Customizable piece of the default paging text (defaults to "Page")
24521      * @type String
24522      */
24523     beforePageText : "Page",
24524     /**
24525      * Customizable piece of the default paging text (defaults to "of %0")
24526      * @type String
24527      */
24528     afterPageText : "of {0}",
24529     /**
24530      * Customizable piece of the default paging text (defaults to "First Page")
24531      * @type String
24532      */
24533     firstText : "First Page",
24534     /**
24535      * Customizable piece of the default paging text (defaults to "Previous Page")
24536      * @type String
24537      */
24538     prevText : "Previous Page",
24539     /**
24540      * Customizable piece of the default paging text (defaults to "Next Page")
24541      * @type String
24542      */
24543     nextText : "Next Page",
24544     /**
24545      * Customizable piece of the default paging text (defaults to "Last Page")
24546      * @type String
24547      */
24548     lastText : "Last Page",
24549     /**
24550      * Customizable piece of the default paging text (defaults to "Refresh")
24551      * @type String
24552      */
24553     refreshText : "Refresh",
24554
24555     buttons : false,
24556     // private
24557     onRender : function(ct, position) 
24558     {
24559         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24560         this.navgroup.parentId = this.id;
24561         this.navgroup.onRender(this.el, null);
24562         // add the buttons to the navgroup
24563         
24564         if(this.displayInfo){
24565             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24566             this.displayEl = this.el.select('.x-paging-info', true).first();
24567 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24568 //            this.displayEl = navel.el.select('span',true).first();
24569         }
24570         
24571         var _this = this;
24572         
24573         if(this.buttons){
24574             Roo.each(_this.buttons, function(e){ // this might need to use render????
24575                Roo.factory(e).render(_this.el);
24576             });
24577         }
24578             
24579         Roo.each(_this.toolbarItems, function(e) {
24580             _this.navgroup.addItem(e);
24581         });
24582         
24583         
24584         this.first = this.navgroup.addItem({
24585             tooltip: this.firstText,
24586             cls: "prev",
24587             icon : 'fa fa-backward',
24588             disabled: true,
24589             preventDefault: true,
24590             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24591         });
24592         
24593         this.prev =  this.navgroup.addItem({
24594             tooltip: this.prevText,
24595             cls: "prev",
24596             icon : 'fa fa-step-backward',
24597             disabled: true,
24598             preventDefault: true,
24599             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24600         });
24601     //this.addSeparator();
24602         
24603         
24604         var field = this.navgroup.addItem( {
24605             tagtype : 'span',
24606             cls : 'x-paging-position',
24607             
24608             html : this.beforePageText  +
24609                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24610                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24611          } ); //?? escaped?
24612         
24613         this.field = field.el.select('input', true).first();
24614         this.field.on("keydown", this.onPagingKeydown, this);
24615         this.field.on("focus", function(){this.dom.select();});
24616     
24617     
24618         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24619         //this.field.setHeight(18);
24620         //this.addSeparator();
24621         this.next = this.navgroup.addItem({
24622             tooltip: this.nextText,
24623             cls: "next",
24624             html : ' <i class="fa fa-step-forward">',
24625             disabled: true,
24626             preventDefault: true,
24627             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24628         });
24629         this.last = this.navgroup.addItem({
24630             tooltip: this.lastText,
24631             icon : 'fa fa-forward',
24632             cls: "next",
24633             disabled: true,
24634             preventDefault: true,
24635             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24636         });
24637     //this.addSeparator();
24638         this.loading = this.navgroup.addItem({
24639             tooltip: this.refreshText,
24640             icon: 'fa fa-refresh',
24641             preventDefault: true,
24642             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24643         });
24644         
24645     },
24646
24647     // private
24648     updateInfo : function(){
24649         if(this.displayEl){
24650             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24651             var msg = count == 0 ?
24652                 this.emptyMsg :
24653                 String.format(
24654                     this.displayMsg,
24655                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24656                 );
24657             this.displayEl.update(msg);
24658         }
24659     },
24660
24661     // private
24662     onLoad : function(ds, r, o)
24663     {
24664         this.cursor = o.params.start ? o.params.start : 0;
24665         
24666         var d = this.getPageData(),
24667             ap = d.activePage,
24668             ps = d.pages;
24669         
24670         
24671         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24672         this.field.dom.value = ap;
24673         this.first.setDisabled(ap == 1);
24674         this.prev.setDisabled(ap == 1);
24675         this.next.setDisabled(ap == ps);
24676         this.last.setDisabled(ap == ps);
24677         this.loading.enable();
24678         this.updateInfo();
24679     },
24680
24681     // private
24682     getPageData : function(){
24683         var total = this.ds.getTotalCount();
24684         return {
24685             total : total,
24686             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24687             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24688         };
24689     },
24690
24691     // private
24692     onLoadError : function(){
24693         this.loading.enable();
24694     },
24695
24696     // private
24697     onPagingKeydown : function(e){
24698         var k = e.getKey();
24699         var d = this.getPageData();
24700         if(k == e.RETURN){
24701             var v = this.field.dom.value, pageNum;
24702             if(!v || isNaN(pageNum = parseInt(v, 10))){
24703                 this.field.dom.value = d.activePage;
24704                 return;
24705             }
24706             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24707             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24708             e.stopEvent();
24709         }
24710         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))
24711         {
24712           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24713           this.field.dom.value = pageNum;
24714           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24715           e.stopEvent();
24716         }
24717         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24718         {
24719           var v = this.field.dom.value, pageNum; 
24720           var increment = (e.shiftKey) ? 10 : 1;
24721           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24722                 increment *= -1;
24723           }
24724           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24725             this.field.dom.value = d.activePage;
24726             return;
24727           }
24728           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24729           {
24730             this.field.dom.value = parseInt(v, 10) + increment;
24731             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24732             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24733           }
24734           e.stopEvent();
24735         }
24736     },
24737
24738     // private
24739     beforeLoad : function(){
24740         if(this.loading){
24741             this.loading.disable();
24742         }
24743     },
24744
24745     // private
24746     onClick : function(which){
24747         
24748         var ds = this.ds;
24749         if (!ds) {
24750             return;
24751         }
24752         
24753         switch(which){
24754             case "first":
24755                 ds.load({params:{start: 0, limit: this.pageSize}});
24756             break;
24757             case "prev":
24758                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24759             break;
24760             case "next":
24761                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24762             break;
24763             case "last":
24764                 var total = ds.getTotalCount();
24765                 var extra = total % this.pageSize;
24766                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24767                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24768             break;
24769             case "refresh":
24770                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24771             break;
24772         }
24773     },
24774
24775     /**
24776      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24777      * @param {Roo.data.Store} store The data store to unbind
24778      */
24779     unbind : function(ds){
24780         ds.un("beforeload", this.beforeLoad, this);
24781         ds.un("load", this.onLoad, this);
24782         ds.un("loadexception", this.onLoadError, this);
24783         ds.un("remove", this.updateInfo, this);
24784         ds.un("add", this.updateInfo, this);
24785         this.ds = undefined;
24786     },
24787
24788     /**
24789      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24790      * @param {Roo.data.Store} store The data store to bind
24791      */
24792     bind : function(ds){
24793         ds.on("beforeload", this.beforeLoad, this);
24794         ds.on("load", this.onLoad, this);
24795         ds.on("loadexception", this.onLoadError, this);
24796         ds.on("remove", this.updateInfo, this);
24797         ds.on("add", this.updateInfo, this);
24798         this.ds = ds;
24799     }
24800 });/*
24801  * - LGPL
24802  *
24803  * element
24804  * 
24805  */
24806
24807 /**
24808  * @class Roo.bootstrap.MessageBar
24809  * @extends Roo.bootstrap.Component
24810  * Bootstrap MessageBar class
24811  * @cfg {String} html contents of the MessageBar
24812  * @cfg {String} weight (info | success | warning | danger) default info
24813  * @cfg {String} beforeClass insert the bar before the given class
24814  * @cfg {Boolean} closable (true | false) default false
24815  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24816  * 
24817  * @constructor
24818  * Create a new Element
24819  * @param {Object} config The config object
24820  */
24821
24822 Roo.bootstrap.MessageBar = function(config){
24823     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24824 };
24825
24826 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24827     
24828     html: '',
24829     weight: 'info',
24830     closable: false,
24831     fixed: false,
24832     beforeClass: 'bootstrap-sticky-wrap',
24833     
24834     getAutoCreate : function(){
24835         
24836         var cfg = {
24837             tag: 'div',
24838             cls: 'alert alert-dismissable alert-' + this.weight,
24839             cn: [
24840                 {
24841                     tag: 'span',
24842                     cls: 'message',
24843                     html: this.html || ''
24844                 }
24845             ]
24846         };
24847         
24848         if(this.fixed){
24849             cfg.cls += ' alert-messages-fixed';
24850         }
24851         
24852         if(this.closable){
24853             cfg.cn.push({
24854                 tag: 'button',
24855                 cls: 'close',
24856                 html: 'x'
24857             });
24858         }
24859         
24860         return cfg;
24861     },
24862     
24863     onRender : function(ct, position)
24864     {
24865         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24866         
24867         if(!this.el){
24868             var cfg = Roo.apply({},  this.getAutoCreate());
24869             cfg.id = Roo.id();
24870             
24871             if (this.cls) {
24872                 cfg.cls += ' ' + this.cls;
24873             }
24874             if (this.style) {
24875                 cfg.style = this.style;
24876             }
24877             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24878             
24879             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24880         }
24881         
24882         this.el.select('>button.close').on('click', this.hide, this);
24883         
24884     },
24885     
24886     show : function()
24887     {
24888         if (!this.rendered) {
24889             this.render();
24890         }
24891         
24892         this.el.show();
24893         
24894         this.fireEvent('show', this);
24895         
24896     },
24897     
24898     hide : function()
24899     {
24900         if (!this.rendered) {
24901             this.render();
24902         }
24903         
24904         this.el.hide();
24905         
24906         this.fireEvent('hide', this);
24907     },
24908     
24909     update : function()
24910     {
24911 //        var e = this.el.dom.firstChild;
24912 //        
24913 //        if(this.closable){
24914 //            e = e.nextSibling;
24915 //        }
24916 //        
24917 //        e.data = this.html || '';
24918
24919         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24920     }
24921    
24922 });
24923
24924  
24925
24926      /*
24927  * - LGPL
24928  *
24929  * Graph
24930  * 
24931  */
24932
24933
24934 /**
24935  * @class Roo.bootstrap.Graph
24936  * @extends Roo.bootstrap.Component
24937  * Bootstrap Graph class
24938 > Prameters
24939  -sm {number} sm 4
24940  -md {number} md 5
24941  @cfg {String} graphtype  bar | vbar | pie
24942  @cfg {number} g_x coodinator | centre x (pie)
24943  @cfg {number} g_y coodinator | centre y (pie)
24944  @cfg {number} g_r radius (pie)
24945  @cfg {number} g_height height of the chart (respected by all elements in the set)
24946  @cfg {number} g_width width of the chart (respected by all elements in the set)
24947  @cfg {Object} title The title of the chart
24948     
24949  -{Array}  values
24950  -opts (object) options for the chart 
24951      o {
24952      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24953      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24954      o vgutter (number)
24955      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.
24956      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24957      o to
24958      o stretch (boolean)
24959      o }
24960  -opts (object) options for the pie
24961      o{
24962      o cut
24963      o startAngle (number)
24964      o endAngle (number)
24965      } 
24966  *
24967  * @constructor
24968  * Create a new Input
24969  * @param {Object} config The config object
24970  */
24971
24972 Roo.bootstrap.Graph = function(config){
24973     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24974     
24975     this.addEvents({
24976         // img events
24977         /**
24978          * @event click
24979          * The img click event for the img.
24980          * @param {Roo.EventObject} e
24981          */
24982         "click" : true
24983     });
24984 };
24985
24986 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24987     
24988     sm: 4,
24989     md: 5,
24990     graphtype: 'bar',
24991     g_height: 250,
24992     g_width: 400,
24993     g_x: 50,
24994     g_y: 50,
24995     g_r: 30,
24996     opts:{
24997         //g_colors: this.colors,
24998         g_type: 'soft',
24999         g_gutter: '20%'
25000
25001     },
25002     title : false,
25003
25004     getAutoCreate : function(){
25005         
25006         var cfg = {
25007             tag: 'div',
25008             html : null
25009         };
25010         
25011         
25012         return  cfg;
25013     },
25014
25015     onRender : function(ct,position){
25016         
25017         
25018         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25019         
25020         if (typeof(Raphael) == 'undefined') {
25021             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25022             return;
25023         }
25024         
25025         this.raphael = Raphael(this.el.dom);
25026         
25027                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25028                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25029                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25030                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25031                 /*
25032                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25033                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25034                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25035                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25036                 
25037                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25038                 r.barchart(330, 10, 300, 220, data1);
25039                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25040                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25041                 */
25042                 
25043                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25044                 // r.barchart(30, 30, 560, 250,  xdata, {
25045                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25046                 //     axis : "0 0 1 1",
25047                 //     axisxlabels :  xdata
25048                 //     //yvalues : cols,
25049                    
25050                 // });
25051 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25052 //        
25053 //        this.load(null,xdata,{
25054 //                axis : "0 0 1 1",
25055 //                axisxlabels :  xdata
25056 //                });
25057
25058     },
25059
25060     load : function(graphtype,xdata,opts)
25061     {
25062         this.raphael.clear();
25063         if(!graphtype) {
25064             graphtype = this.graphtype;
25065         }
25066         if(!opts){
25067             opts = this.opts;
25068         }
25069         var r = this.raphael,
25070             fin = function () {
25071                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25072             },
25073             fout = function () {
25074                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25075             },
25076             pfin = function() {
25077                 this.sector.stop();
25078                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25079
25080                 if (this.label) {
25081                     this.label[0].stop();
25082                     this.label[0].attr({ r: 7.5 });
25083                     this.label[1].attr({ "font-weight": 800 });
25084                 }
25085             },
25086             pfout = function() {
25087                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25088
25089                 if (this.label) {
25090                     this.label[0].animate({ r: 5 }, 500, "bounce");
25091                     this.label[1].attr({ "font-weight": 400 });
25092                 }
25093             };
25094
25095         switch(graphtype){
25096             case 'bar':
25097                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25098                 break;
25099             case 'hbar':
25100                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25101                 break;
25102             case 'pie':
25103 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25104 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25105 //            
25106                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25107                 
25108                 break;
25109
25110         }
25111         
25112         if(this.title){
25113             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25114         }
25115         
25116     },
25117     
25118     setTitle: function(o)
25119     {
25120         this.title = o;
25121     },
25122     
25123     initEvents: function() {
25124         
25125         if(!this.href){
25126             this.el.on('click', this.onClick, this);
25127         }
25128     },
25129     
25130     onClick : function(e)
25131     {
25132         Roo.log('img onclick');
25133         this.fireEvent('click', this, e);
25134     }
25135    
25136 });
25137
25138  
25139 /*
25140  * - LGPL
25141  *
25142  * numberBox
25143  * 
25144  */
25145 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25146
25147 /**
25148  * @class Roo.bootstrap.dash.NumberBox
25149  * @extends Roo.bootstrap.Component
25150  * Bootstrap NumberBox class
25151  * @cfg {String} headline Box headline
25152  * @cfg {String} content Box content
25153  * @cfg {String} icon Box icon
25154  * @cfg {String} footer Footer text
25155  * @cfg {String} fhref Footer href
25156  * 
25157  * @constructor
25158  * Create a new NumberBox
25159  * @param {Object} config The config object
25160  */
25161
25162
25163 Roo.bootstrap.dash.NumberBox = function(config){
25164     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25165     
25166 };
25167
25168 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25169     
25170     headline : '',
25171     content : '',
25172     icon : '',
25173     footer : '',
25174     fhref : '',
25175     ficon : '',
25176     
25177     getAutoCreate : function(){
25178         
25179         var cfg = {
25180             tag : 'div',
25181             cls : 'small-box ',
25182             cn : [
25183                 {
25184                     tag : 'div',
25185                     cls : 'inner',
25186                     cn :[
25187                         {
25188                             tag : 'h3',
25189                             cls : 'roo-headline',
25190                             html : this.headline
25191                         },
25192                         {
25193                             tag : 'p',
25194                             cls : 'roo-content',
25195                             html : this.content
25196                         }
25197                     ]
25198                 }
25199             ]
25200         };
25201         
25202         if(this.icon){
25203             cfg.cn.push({
25204                 tag : 'div',
25205                 cls : 'icon',
25206                 cn :[
25207                     {
25208                         tag : 'i',
25209                         cls : 'ion ' + this.icon
25210                     }
25211                 ]
25212             });
25213         }
25214         
25215         if(this.footer){
25216             var footer = {
25217                 tag : 'a',
25218                 cls : 'small-box-footer',
25219                 href : this.fhref || '#',
25220                 html : this.footer
25221             };
25222             
25223             cfg.cn.push(footer);
25224             
25225         }
25226         
25227         return  cfg;
25228     },
25229
25230     onRender : function(ct,position){
25231         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25232
25233
25234        
25235                 
25236     },
25237
25238     setHeadline: function (value)
25239     {
25240         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25241     },
25242     
25243     setFooter: function (value, href)
25244     {
25245         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25246         
25247         if(href){
25248             this.el.select('a.small-box-footer',true).first().attr('href', href);
25249         }
25250         
25251     },
25252
25253     setContent: function (value)
25254     {
25255         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25256     },
25257
25258     initEvents: function() 
25259     {   
25260         
25261     }
25262     
25263 });
25264
25265  
25266 /*
25267  * - LGPL
25268  *
25269  * TabBox
25270  * 
25271  */
25272 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25273
25274 /**
25275  * @class Roo.bootstrap.dash.TabBox
25276  * @extends Roo.bootstrap.Component
25277  * Bootstrap TabBox class
25278  * @cfg {String} title Title of the TabBox
25279  * @cfg {String} icon Icon of the TabBox
25280  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25281  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25282  * 
25283  * @constructor
25284  * Create a new TabBox
25285  * @param {Object} config The config object
25286  */
25287
25288
25289 Roo.bootstrap.dash.TabBox = function(config){
25290     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25291     this.addEvents({
25292         // raw events
25293         /**
25294          * @event addpane
25295          * When a pane is added
25296          * @param {Roo.bootstrap.dash.TabPane} pane
25297          */
25298         "addpane" : true,
25299         /**
25300          * @event activatepane
25301          * When a pane is activated
25302          * @param {Roo.bootstrap.dash.TabPane} pane
25303          */
25304         "activatepane" : true
25305         
25306          
25307     });
25308     
25309     this.panes = [];
25310 };
25311
25312 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25313
25314     title : '',
25315     icon : false,
25316     showtabs : true,
25317     tabScrollable : false,
25318     
25319     getChildContainer : function()
25320     {
25321         return this.el.select('.tab-content', true).first();
25322     },
25323     
25324     getAutoCreate : function(){
25325         
25326         var header = {
25327             tag: 'li',
25328             cls: 'pull-left header',
25329             html: this.title,
25330             cn : []
25331         };
25332         
25333         if(this.icon){
25334             header.cn.push({
25335                 tag: 'i',
25336                 cls: 'fa ' + this.icon
25337             });
25338         }
25339         
25340         var h = {
25341             tag: 'ul',
25342             cls: 'nav nav-tabs pull-right',
25343             cn: [
25344                 header
25345             ]
25346         };
25347         
25348         if(this.tabScrollable){
25349             h = {
25350                 tag: 'div',
25351                 cls: 'tab-header',
25352                 cn: [
25353                     {
25354                         tag: 'ul',
25355                         cls: 'nav nav-tabs pull-right',
25356                         cn: [
25357                             header
25358                         ]
25359                     }
25360                 ]
25361             };
25362         }
25363         
25364         var cfg = {
25365             tag: 'div',
25366             cls: 'nav-tabs-custom',
25367             cn: [
25368                 h,
25369                 {
25370                     tag: 'div',
25371                     cls: 'tab-content no-padding',
25372                     cn: []
25373                 }
25374             ]
25375         };
25376
25377         return  cfg;
25378     },
25379     initEvents : function()
25380     {
25381         //Roo.log('add add pane handler');
25382         this.on('addpane', this.onAddPane, this);
25383     },
25384      /**
25385      * Updates the box title
25386      * @param {String} html to set the title to.
25387      */
25388     setTitle : function(value)
25389     {
25390         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25391     },
25392     onAddPane : function(pane)
25393     {
25394         this.panes.push(pane);
25395         //Roo.log('addpane');
25396         //Roo.log(pane);
25397         // tabs are rendere left to right..
25398         if(!this.showtabs){
25399             return;
25400         }
25401         
25402         var ctr = this.el.select('.nav-tabs', true).first();
25403          
25404          
25405         var existing = ctr.select('.nav-tab',true);
25406         var qty = existing.getCount();;
25407         
25408         
25409         var tab = ctr.createChild({
25410             tag : 'li',
25411             cls : 'nav-tab' + (qty ? '' : ' active'),
25412             cn : [
25413                 {
25414                     tag : 'a',
25415                     href:'#',
25416                     html : pane.title
25417                 }
25418             ]
25419         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25420         pane.tab = tab;
25421         
25422         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25423         if (!qty) {
25424             pane.el.addClass('active');
25425         }
25426         
25427                 
25428     },
25429     onTabClick : function(ev,un,ob,pane)
25430     {
25431         //Roo.log('tab - prev default');
25432         ev.preventDefault();
25433         
25434         
25435         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25436         pane.tab.addClass('active');
25437         //Roo.log(pane.title);
25438         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25439         // technically we should have a deactivate event.. but maybe add later.
25440         // and it should not de-activate the selected tab...
25441         this.fireEvent('activatepane', pane);
25442         pane.el.addClass('active');
25443         pane.fireEvent('activate');
25444         
25445         
25446     },
25447     
25448     getActivePane : function()
25449     {
25450         var r = false;
25451         Roo.each(this.panes, function(p) {
25452             if(p.el.hasClass('active')){
25453                 r = p;
25454                 return false;
25455             }
25456             
25457             return;
25458         });
25459         
25460         return r;
25461     }
25462     
25463     
25464 });
25465
25466  
25467 /*
25468  * - LGPL
25469  *
25470  * Tab pane
25471  * 
25472  */
25473 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25474 /**
25475  * @class Roo.bootstrap.TabPane
25476  * @extends Roo.bootstrap.Component
25477  * Bootstrap TabPane class
25478  * @cfg {Boolean} active (false | true) Default false
25479  * @cfg {String} title title of panel
25480
25481  * 
25482  * @constructor
25483  * Create a new TabPane
25484  * @param {Object} config The config object
25485  */
25486
25487 Roo.bootstrap.dash.TabPane = function(config){
25488     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25489     
25490     this.addEvents({
25491         // raw events
25492         /**
25493          * @event activate
25494          * When a pane is activated
25495          * @param {Roo.bootstrap.dash.TabPane} pane
25496          */
25497         "activate" : true
25498          
25499     });
25500 };
25501
25502 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25503     
25504     active : false,
25505     title : '',
25506     
25507     // the tabBox that this is attached to.
25508     tab : false,
25509      
25510     getAutoCreate : function() 
25511     {
25512         var cfg = {
25513             tag: 'div',
25514             cls: 'tab-pane'
25515         };
25516         
25517         if(this.active){
25518             cfg.cls += ' active';
25519         }
25520         
25521         return cfg;
25522     },
25523     initEvents  : function()
25524     {
25525         //Roo.log('trigger add pane handler');
25526         this.parent().fireEvent('addpane', this)
25527     },
25528     
25529      /**
25530      * Updates the tab title 
25531      * @param {String} html to set the title to.
25532      */
25533     setTitle: function(str)
25534     {
25535         if (!this.tab) {
25536             return;
25537         }
25538         this.title = str;
25539         this.tab.select('a', true).first().dom.innerHTML = str;
25540         
25541     }
25542     
25543     
25544     
25545 });
25546
25547  
25548
25549
25550  /*
25551  * - LGPL
25552  *
25553  * menu
25554  * 
25555  */
25556 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25557
25558 /**
25559  * @class Roo.bootstrap.menu.Menu
25560  * @extends Roo.bootstrap.Component
25561  * Bootstrap Menu class - container for Menu
25562  * @cfg {String} html Text of the menu
25563  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25564  * @cfg {String} icon Font awesome icon
25565  * @cfg {String} pos Menu align to (top | bottom) default bottom
25566  * 
25567  * 
25568  * @constructor
25569  * Create a new Menu
25570  * @param {Object} config The config object
25571  */
25572
25573
25574 Roo.bootstrap.menu.Menu = function(config){
25575     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25576     
25577     this.addEvents({
25578         /**
25579          * @event beforeshow
25580          * Fires before this menu is displayed
25581          * @param {Roo.bootstrap.menu.Menu} this
25582          */
25583         beforeshow : true,
25584         /**
25585          * @event beforehide
25586          * Fires before this menu is hidden
25587          * @param {Roo.bootstrap.menu.Menu} this
25588          */
25589         beforehide : true,
25590         /**
25591          * @event show
25592          * Fires after this menu is displayed
25593          * @param {Roo.bootstrap.menu.Menu} this
25594          */
25595         show : true,
25596         /**
25597          * @event hide
25598          * Fires after this menu is hidden
25599          * @param {Roo.bootstrap.menu.Menu} this
25600          */
25601         hide : true,
25602         /**
25603          * @event click
25604          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25605          * @param {Roo.bootstrap.menu.Menu} this
25606          * @param {Roo.EventObject} e
25607          */
25608         click : true
25609     });
25610     
25611 };
25612
25613 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25614     
25615     submenu : false,
25616     html : '',
25617     weight : 'default',
25618     icon : false,
25619     pos : 'bottom',
25620     
25621     
25622     getChildContainer : function() {
25623         if(this.isSubMenu){
25624             return this.el;
25625         }
25626         
25627         return this.el.select('ul.dropdown-menu', true).first();  
25628     },
25629     
25630     getAutoCreate : function()
25631     {
25632         var text = [
25633             {
25634                 tag : 'span',
25635                 cls : 'roo-menu-text',
25636                 html : this.html
25637             }
25638         ];
25639         
25640         if(this.icon){
25641             text.unshift({
25642                 tag : 'i',
25643                 cls : 'fa ' + this.icon
25644             })
25645         }
25646         
25647         
25648         var cfg = {
25649             tag : 'div',
25650             cls : 'btn-group',
25651             cn : [
25652                 {
25653                     tag : 'button',
25654                     cls : 'dropdown-button btn btn-' + this.weight,
25655                     cn : text
25656                 },
25657                 {
25658                     tag : 'button',
25659                     cls : 'dropdown-toggle btn btn-' + this.weight,
25660                     cn : [
25661                         {
25662                             tag : 'span',
25663                             cls : 'caret'
25664                         }
25665                     ]
25666                 },
25667                 {
25668                     tag : 'ul',
25669                     cls : 'dropdown-menu'
25670                 }
25671             ]
25672             
25673         };
25674         
25675         if(this.pos == 'top'){
25676             cfg.cls += ' dropup';
25677         }
25678         
25679         if(this.isSubMenu){
25680             cfg = {
25681                 tag : 'ul',
25682                 cls : 'dropdown-menu'
25683             }
25684         }
25685         
25686         return cfg;
25687     },
25688     
25689     onRender : function(ct, position)
25690     {
25691         this.isSubMenu = ct.hasClass('dropdown-submenu');
25692         
25693         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25694     },
25695     
25696     initEvents : function() 
25697     {
25698         if(this.isSubMenu){
25699             return;
25700         }
25701         
25702         this.hidden = true;
25703         
25704         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25705         this.triggerEl.on('click', this.onTriggerPress, this);
25706         
25707         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25708         this.buttonEl.on('click', this.onClick, this);
25709         
25710     },
25711     
25712     list : function()
25713     {
25714         if(this.isSubMenu){
25715             return this.el;
25716         }
25717         
25718         return this.el.select('ul.dropdown-menu', true).first();
25719     },
25720     
25721     onClick : function(e)
25722     {
25723         this.fireEvent("click", this, e);
25724     },
25725     
25726     onTriggerPress  : function(e)
25727     {   
25728         if (this.isVisible()) {
25729             this.hide();
25730         } else {
25731             this.show();
25732         }
25733     },
25734     
25735     isVisible : function(){
25736         return !this.hidden;
25737     },
25738     
25739     show : function()
25740     {
25741         this.fireEvent("beforeshow", this);
25742         
25743         this.hidden = false;
25744         this.el.addClass('open');
25745         
25746         Roo.get(document).on("mouseup", this.onMouseUp, this);
25747         
25748         this.fireEvent("show", this);
25749         
25750         
25751     },
25752     
25753     hide : function()
25754     {
25755         this.fireEvent("beforehide", this);
25756         
25757         this.hidden = true;
25758         this.el.removeClass('open');
25759         
25760         Roo.get(document).un("mouseup", this.onMouseUp);
25761         
25762         this.fireEvent("hide", this);
25763     },
25764     
25765     onMouseUp : function()
25766     {
25767         this.hide();
25768     }
25769     
25770 });
25771
25772  
25773  /*
25774  * - LGPL
25775  *
25776  * menu item
25777  * 
25778  */
25779 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25780
25781 /**
25782  * @class Roo.bootstrap.menu.Item
25783  * @extends Roo.bootstrap.Component
25784  * Bootstrap MenuItem class
25785  * @cfg {Boolean} submenu (true | false) default false
25786  * @cfg {String} html text of the item
25787  * @cfg {String} href the link
25788  * @cfg {Boolean} disable (true | false) default false
25789  * @cfg {Boolean} preventDefault (true | false) default true
25790  * @cfg {String} icon Font awesome icon
25791  * @cfg {String} pos Submenu align to (left | right) default right 
25792  * 
25793  * 
25794  * @constructor
25795  * Create a new Item
25796  * @param {Object} config The config object
25797  */
25798
25799
25800 Roo.bootstrap.menu.Item = function(config){
25801     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25802     this.addEvents({
25803         /**
25804          * @event mouseover
25805          * Fires when the mouse is hovering over this menu
25806          * @param {Roo.bootstrap.menu.Item} this
25807          * @param {Roo.EventObject} e
25808          */
25809         mouseover : true,
25810         /**
25811          * @event mouseout
25812          * Fires when the mouse exits this menu
25813          * @param {Roo.bootstrap.menu.Item} this
25814          * @param {Roo.EventObject} e
25815          */
25816         mouseout : true,
25817         // raw events
25818         /**
25819          * @event click
25820          * The raw click event for the entire grid.
25821          * @param {Roo.EventObject} e
25822          */
25823         click : true
25824     });
25825 };
25826
25827 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25828     
25829     submenu : false,
25830     href : '',
25831     html : '',
25832     preventDefault: true,
25833     disable : false,
25834     icon : false,
25835     pos : 'right',
25836     
25837     getAutoCreate : function()
25838     {
25839         var text = [
25840             {
25841                 tag : 'span',
25842                 cls : 'roo-menu-item-text',
25843                 html : this.html
25844             }
25845         ];
25846         
25847         if(this.icon){
25848             text.unshift({
25849                 tag : 'i',
25850                 cls : 'fa ' + this.icon
25851             })
25852         }
25853         
25854         var cfg = {
25855             tag : 'li',
25856             cn : [
25857                 {
25858                     tag : 'a',
25859                     href : this.href || '#',
25860                     cn : text
25861                 }
25862             ]
25863         };
25864         
25865         if(this.disable){
25866             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25867         }
25868         
25869         if(this.submenu){
25870             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25871             
25872             if(this.pos == 'left'){
25873                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25874             }
25875         }
25876         
25877         return cfg;
25878     },
25879     
25880     initEvents : function() 
25881     {
25882         this.el.on('mouseover', this.onMouseOver, this);
25883         this.el.on('mouseout', this.onMouseOut, this);
25884         
25885         this.el.select('a', true).first().on('click', this.onClick, this);
25886         
25887     },
25888     
25889     onClick : function(e)
25890     {
25891         if(this.preventDefault){
25892             e.preventDefault();
25893         }
25894         
25895         this.fireEvent("click", this, e);
25896     },
25897     
25898     onMouseOver : function(e)
25899     {
25900         if(this.submenu && this.pos == 'left'){
25901             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25902         }
25903         
25904         this.fireEvent("mouseover", this, e);
25905     },
25906     
25907     onMouseOut : function(e)
25908     {
25909         this.fireEvent("mouseout", this, e);
25910     }
25911 });
25912
25913  
25914
25915  /*
25916  * - LGPL
25917  *
25918  * menu separator
25919  * 
25920  */
25921 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25922
25923 /**
25924  * @class Roo.bootstrap.menu.Separator
25925  * @extends Roo.bootstrap.Component
25926  * Bootstrap Separator class
25927  * 
25928  * @constructor
25929  * Create a new Separator
25930  * @param {Object} config The config object
25931  */
25932
25933
25934 Roo.bootstrap.menu.Separator = function(config){
25935     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25936 };
25937
25938 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25939     
25940     getAutoCreate : function(){
25941         var cfg = {
25942             tag : 'li',
25943             cls: 'divider'
25944         };
25945         
25946         return cfg;
25947     }
25948    
25949 });
25950
25951  
25952
25953  /*
25954  * - LGPL
25955  *
25956  * Tooltip
25957  * 
25958  */
25959
25960 /**
25961  * @class Roo.bootstrap.Tooltip
25962  * Bootstrap Tooltip class
25963  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25964  * to determine which dom element triggers the tooltip.
25965  * 
25966  * It needs to add support for additional attributes like tooltip-position
25967  * 
25968  * @constructor
25969  * Create a new Toolti
25970  * @param {Object} config The config object
25971  */
25972
25973 Roo.bootstrap.Tooltip = function(config){
25974     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25975     
25976     this.alignment = Roo.bootstrap.Tooltip.alignment;
25977     
25978     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25979         this.alignment = config.alignment;
25980     }
25981     
25982 };
25983
25984 Roo.apply(Roo.bootstrap.Tooltip, {
25985     /**
25986      * @function init initialize tooltip monitoring.
25987      * @static
25988      */
25989     currentEl : false,
25990     currentTip : false,
25991     currentRegion : false,
25992     
25993     //  init : delay?
25994     
25995     init : function()
25996     {
25997         Roo.get(document).on('mouseover', this.enter ,this);
25998         Roo.get(document).on('mouseout', this.leave, this);
25999          
26000         
26001         this.currentTip = new Roo.bootstrap.Tooltip();
26002     },
26003     
26004     enter : function(ev)
26005     {
26006         var dom = ev.getTarget();
26007         
26008         //Roo.log(['enter',dom]);
26009         var el = Roo.fly(dom);
26010         if (this.currentEl) {
26011             //Roo.log(dom);
26012             //Roo.log(this.currentEl);
26013             //Roo.log(this.currentEl.contains(dom));
26014             if (this.currentEl == el) {
26015                 return;
26016             }
26017             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26018                 return;
26019             }
26020
26021         }
26022         
26023         if (this.currentTip.el) {
26024             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26025         }    
26026         //Roo.log(ev);
26027         
26028         if(!el || el.dom == document){
26029             return;
26030         }
26031         
26032         var bindEl = el;
26033         
26034         // you can not look for children, as if el is the body.. then everythign is the child..
26035         if (!el.attr('tooltip')) { //
26036             if (!el.select("[tooltip]").elements.length) {
26037                 return;
26038             }
26039             // is the mouse over this child...?
26040             bindEl = el.select("[tooltip]").first();
26041             var xy = ev.getXY();
26042             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26043                 //Roo.log("not in region.");
26044                 return;
26045             }
26046             //Roo.log("child element over..");
26047             
26048         }
26049         this.currentEl = bindEl;
26050         this.currentTip.bind(bindEl);
26051         this.currentRegion = Roo.lib.Region.getRegion(dom);
26052         this.currentTip.enter();
26053         
26054     },
26055     leave : function(ev)
26056     {
26057         var dom = ev.getTarget();
26058         //Roo.log(['leave',dom]);
26059         if (!this.currentEl) {
26060             return;
26061         }
26062         
26063         
26064         if (dom != this.currentEl.dom) {
26065             return;
26066         }
26067         var xy = ev.getXY();
26068         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26069             return;
26070         }
26071         // only activate leave if mouse cursor is outside... bounding box..
26072         
26073         
26074         
26075         
26076         if (this.currentTip) {
26077             this.currentTip.leave();
26078         }
26079         //Roo.log('clear currentEl');
26080         this.currentEl = false;
26081         
26082         
26083     },
26084     alignment : {
26085         'left' : ['r-l', [-2,0], 'right'],
26086         'right' : ['l-r', [2,0], 'left'],
26087         'bottom' : ['t-b', [0,2], 'top'],
26088         'top' : [ 'b-t', [0,-2], 'bottom']
26089     }
26090     
26091 });
26092
26093
26094 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26095     
26096     
26097     bindEl : false,
26098     
26099     delay : null, // can be { show : 300 , hide: 500}
26100     
26101     timeout : null,
26102     
26103     hoverState : null, //???
26104     
26105     placement : 'bottom', 
26106     
26107     alignment : false,
26108     
26109     getAutoCreate : function(){
26110     
26111         var cfg = {
26112            cls : 'tooltip',
26113            role : 'tooltip',
26114            cn : [
26115                 {
26116                     cls : 'tooltip-arrow'
26117                 },
26118                 {
26119                     cls : 'tooltip-inner'
26120                 }
26121            ]
26122         };
26123         
26124         return cfg;
26125     },
26126     bind : function(el)
26127     {
26128         this.bindEl = el;
26129     },
26130       
26131     
26132     enter : function () {
26133        
26134         if (this.timeout != null) {
26135             clearTimeout(this.timeout);
26136         }
26137         
26138         this.hoverState = 'in';
26139          //Roo.log("enter - show");
26140         if (!this.delay || !this.delay.show) {
26141             this.show();
26142             return;
26143         }
26144         var _t = this;
26145         this.timeout = setTimeout(function () {
26146             if (_t.hoverState == 'in') {
26147                 _t.show();
26148             }
26149         }, this.delay.show);
26150     },
26151     leave : function()
26152     {
26153         clearTimeout(this.timeout);
26154     
26155         this.hoverState = 'out';
26156          if (!this.delay || !this.delay.hide) {
26157             this.hide();
26158             return;
26159         }
26160        
26161         var _t = this;
26162         this.timeout = setTimeout(function () {
26163             //Roo.log("leave - timeout");
26164             
26165             if (_t.hoverState == 'out') {
26166                 _t.hide();
26167                 Roo.bootstrap.Tooltip.currentEl = false;
26168             }
26169         }, delay);
26170     },
26171     
26172     show : function (msg)
26173     {
26174         if (!this.el) {
26175             this.render(document.body);
26176         }
26177         // set content.
26178         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26179         
26180         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26181         
26182         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26183         
26184         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26185         
26186         var placement = typeof this.placement == 'function' ?
26187             this.placement.call(this, this.el, on_el) :
26188             this.placement;
26189             
26190         var autoToken = /\s?auto?\s?/i;
26191         var autoPlace = autoToken.test(placement);
26192         if (autoPlace) {
26193             placement = placement.replace(autoToken, '') || 'top';
26194         }
26195         
26196         //this.el.detach()
26197         //this.el.setXY([0,0]);
26198         this.el.show();
26199         //this.el.dom.style.display='block';
26200         
26201         //this.el.appendTo(on_el);
26202         
26203         var p = this.getPosition();
26204         var box = this.el.getBox();
26205         
26206         if (autoPlace) {
26207             // fixme..
26208         }
26209         
26210         var align = this.alignment[placement];
26211         
26212         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26213         
26214         if(placement == 'top' || placement == 'bottom'){
26215             if(xy[0] < 0){
26216                 placement = 'right';
26217             }
26218             
26219             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26220                 placement = 'left';
26221             }
26222             
26223             var scroll = Roo.select('body', true).first().getScroll();
26224             
26225             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26226                 placement = 'top';
26227             }
26228             
26229             align = this.alignment[placement];
26230         }
26231         
26232         this.el.alignTo(this.bindEl, align[0],align[1]);
26233         //var arrow = this.el.select('.arrow',true).first();
26234         //arrow.set(align[2], 
26235         
26236         this.el.addClass(placement);
26237         
26238         this.el.addClass('in fade');
26239         
26240         this.hoverState = null;
26241         
26242         if (this.el.hasClass('fade')) {
26243             // fade it?
26244         }
26245         
26246     },
26247     hide : function()
26248     {
26249          
26250         if (!this.el) {
26251             return;
26252         }
26253         //this.el.setXY([0,0]);
26254         this.el.removeClass('in');
26255         //this.el.hide();
26256         
26257     }
26258     
26259 });
26260  
26261
26262  /*
26263  * - LGPL
26264  *
26265  * Location Picker
26266  * 
26267  */
26268
26269 /**
26270  * @class Roo.bootstrap.LocationPicker
26271  * @extends Roo.bootstrap.Component
26272  * Bootstrap LocationPicker class
26273  * @cfg {Number} latitude Position when init default 0
26274  * @cfg {Number} longitude Position when init default 0
26275  * @cfg {Number} zoom default 15
26276  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26277  * @cfg {Boolean} mapTypeControl default false
26278  * @cfg {Boolean} disableDoubleClickZoom default false
26279  * @cfg {Boolean} scrollwheel default true
26280  * @cfg {Boolean} streetViewControl default false
26281  * @cfg {Number} radius default 0
26282  * @cfg {String} locationName
26283  * @cfg {Boolean} draggable default true
26284  * @cfg {Boolean} enableAutocomplete default false
26285  * @cfg {Boolean} enableReverseGeocode default true
26286  * @cfg {String} markerTitle
26287  * 
26288  * @constructor
26289  * Create a new LocationPicker
26290  * @param {Object} config The config object
26291  */
26292
26293
26294 Roo.bootstrap.LocationPicker = function(config){
26295     
26296     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26297     
26298     this.addEvents({
26299         /**
26300          * @event initial
26301          * Fires when the picker initialized.
26302          * @param {Roo.bootstrap.LocationPicker} this
26303          * @param {Google Location} location
26304          */
26305         initial : true,
26306         /**
26307          * @event positionchanged
26308          * Fires when the picker position changed.
26309          * @param {Roo.bootstrap.LocationPicker} this
26310          * @param {Google Location} location
26311          */
26312         positionchanged : true,
26313         /**
26314          * @event resize
26315          * Fires when the map resize.
26316          * @param {Roo.bootstrap.LocationPicker} this
26317          */
26318         resize : true,
26319         /**
26320          * @event show
26321          * Fires when the map show.
26322          * @param {Roo.bootstrap.LocationPicker} this
26323          */
26324         show : true,
26325         /**
26326          * @event hide
26327          * Fires when the map hide.
26328          * @param {Roo.bootstrap.LocationPicker} this
26329          */
26330         hide : true,
26331         /**
26332          * @event mapClick
26333          * Fires when click the map.
26334          * @param {Roo.bootstrap.LocationPicker} this
26335          * @param {Map event} e
26336          */
26337         mapClick : true,
26338         /**
26339          * @event mapRightClick
26340          * Fires when right click the map.
26341          * @param {Roo.bootstrap.LocationPicker} this
26342          * @param {Map event} e
26343          */
26344         mapRightClick : true,
26345         /**
26346          * @event markerClick
26347          * Fires when click the marker.
26348          * @param {Roo.bootstrap.LocationPicker} this
26349          * @param {Map event} e
26350          */
26351         markerClick : true,
26352         /**
26353          * @event markerRightClick
26354          * Fires when right click the marker.
26355          * @param {Roo.bootstrap.LocationPicker} this
26356          * @param {Map event} e
26357          */
26358         markerRightClick : true,
26359         /**
26360          * @event OverlayViewDraw
26361          * Fires when OverlayView Draw
26362          * @param {Roo.bootstrap.LocationPicker} this
26363          */
26364         OverlayViewDraw : true,
26365         /**
26366          * @event OverlayViewOnAdd
26367          * Fires when OverlayView Draw
26368          * @param {Roo.bootstrap.LocationPicker} this
26369          */
26370         OverlayViewOnAdd : true,
26371         /**
26372          * @event OverlayViewOnRemove
26373          * Fires when OverlayView Draw
26374          * @param {Roo.bootstrap.LocationPicker} this
26375          */
26376         OverlayViewOnRemove : true,
26377         /**
26378          * @event OverlayViewShow
26379          * Fires when OverlayView Draw
26380          * @param {Roo.bootstrap.LocationPicker} this
26381          * @param {Pixel} cpx
26382          */
26383         OverlayViewShow : true,
26384         /**
26385          * @event OverlayViewHide
26386          * Fires when OverlayView Draw
26387          * @param {Roo.bootstrap.LocationPicker} this
26388          */
26389         OverlayViewHide : true,
26390         /**
26391          * @event loadexception
26392          * Fires when load google lib failed.
26393          * @param {Roo.bootstrap.LocationPicker} this
26394          */
26395         loadexception : true
26396     });
26397         
26398 };
26399
26400 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26401     
26402     gMapContext: false,
26403     
26404     latitude: 0,
26405     longitude: 0,
26406     zoom: 15,
26407     mapTypeId: false,
26408     mapTypeControl: false,
26409     disableDoubleClickZoom: false,
26410     scrollwheel: true,
26411     streetViewControl: false,
26412     radius: 0,
26413     locationName: '',
26414     draggable: true,
26415     enableAutocomplete: false,
26416     enableReverseGeocode: true,
26417     markerTitle: '',
26418     
26419     getAutoCreate: function()
26420     {
26421
26422         var cfg = {
26423             tag: 'div',
26424             cls: 'roo-location-picker'
26425         };
26426         
26427         return cfg
26428     },
26429     
26430     initEvents: function(ct, position)
26431     {       
26432         if(!this.el.getWidth() || this.isApplied()){
26433             return;
26434         }
26435         
26436         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26437         
26438         this.initial();
26439     },
26440     
26441     initial: function()
26442     {
26443         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26444             this.fireEvent('loadexception', this);
26445             return;
26446         }
26447         
26448         if(!this.mapTypeId){
26449             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26450         }
26451         
26452         this.gMapContext = this.GMapContext();
26453         
26454         this.initOverlayView();
26455         
26456         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26457         
26458         var _this = this;
26459                 
26460         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26461             _this.setPosition(_this.gMapContext.marker.position);
26462         });
26463         
26464         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26465             _this.fireEvent('mapClick', this, event);
26466             
26467         });
26468
26469         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26470             _this.fireEvent('mapRightClick', this, event);
26471             
26472         });
26473         
26474         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26475             _this.fireEvent('markerClick', this, event);
26476             
26477         });
26478
26479         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26480             _this.fireEvent('markerRightClick', this, event);
26481             
26482         });
26483         
26484         this.setPosition(this.gMapContext.location);
26485         
26486         this.fireEvent('initial', this, this.gMapContext.location);
26487     },
26488     
26489     initOverlayView: function()
26490     {
26491         var _this = this;
26492         
26493         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26494             
26495             draw: function()
26496             {
26497                 _this.fireEvent('OverlayViewDraw', _this);
26498             },
26499             
26500             onAdd: function()
26501             {
26502                 _this.fireEvent('OverlayViewOnAdd', _this);
26503             },
26504             
26505             onRemove: function()
26506             {
26507                 _this.fireEvent('OverlayViewOnRemove', _this);
26508             },
26509             
26510             show: function(cpx)
26511             {
26512                 _this.fireEvent('OverlayViewShow', _this, cpx);
26513             },
26514             
26515             hide: function()
26516             {
26517                 _this.fireEvent('OverlayViewHide', _this);
26518             }
26519             
26520         });
26521     },
26522     
26523     fromLatLngToContainerPixel: function(event)
26524     {
26525         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26526     },
26527     
26528     isApplied: function() 
26529     {
26530         return this.getGmapContext() == false ? false : true;
26531     },
26532     
26533     getGmapContext: function() 
26534     {
26535         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26536     },
26537     
26538     GMapContext: function() 
26539     {
26540         var position = new google.maps.LatLng(this.latitude, this.longitude);
26541         
26542         var _map = new google.maps.Map(this.el.dom, {
26543             center: position,
26544             zoom: this.zoom,
26545             mapTypeId: this.mapTypeId,
26546             mapTypeControl: this.mapTypeControl,
26547             disableDoubleClickZoom: this.disableDoubleClickZoom,
26548             scrollwheel: this.scrollwheel,
26549             streetViewControl: this.streetViewControl,
26550             locationName: this.locationName,
26551             draggable: this.draggable,
26552             enableAutocomplete: this.enableAutocomplete,
26553             enableReverseGeocode: this.enableReverseGeocode
26554         });
26555         
26556         var _marker = new google.maps.Marker({
26557             position: position,
26558             map: _map,
26559             title: this.markerTitle,
26560             draggable: this.draggable
26561         });
26562         
26563         return {
26564             map: _map,
26565             marker: _marker,
26566             circle: null,
26567             location: position,
26568             radius: this.radius,
26569             locationName: this.locationName,
26570             addressComponents: {
26571                 formatted_address: null,
26572                 addressLine1: null,
26573                 addressLine2: null,
26574                 streetName: null,
26575                 streetNumber: null,
26576                 city: null,
26577                 district: null,
26578                 state: null,
26579                 stateOrProvince: null
26580             },
26581             settings: this,
26582             domContainer: this.el.dom,
26583             geodecoder: new google.maps.Geocoder()
26584         };
26585     },
26586     
26587     drawCircle: function(center, radius, options) 
26588     {
26589         if (this.gMapContext.circle != null) {
26590             this.gMapContext.circle.setMap(null);
26591         }
26592         if (radius > 0) {
26593             radius *= 1;
26594             options = Roo.apply({}, options, {
26595                 strokeColor: "#0000FF",
26596                 strokeOpacity: .35,
26597                 strokeWeight: 2,
26598                 fillColor: "#0000FF",
26599                 fillOpacity: .2
26600             });
26601             
26602             options.map = this.gMapContext.map;
26603             options.radius = radius;
26604             options.center = center;
26605             this.gMapContext.circle = new google.maps.Circle(options);
26606             return this.gMapContext.circle;
26607         }
26608         
26609         return null;
26610     },
26611     
26612     setPosition: function(location) 
26613     {
26614         this.gMapContext.location = location;
26615         this.gMapContext.marker.setPosition(location);
26616         this.gMapContext.map.panTo(location);
26617         this.drawCircle(location, this.gMapContext.radius, {});
26618         
26619         var _this = this;
26620         
26621         if (this.gMapContext.settings.enableReverseGeocode) {
26622             this.gMapContext.geodecoder.geocode({
26623                 latLng: this.gMapContext.location
26624             }, function(results, status) {
26625                 
26626                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26627                     _this.gMapContext.locationName = results[0].formatted_address;
26628                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26629                     
26630                     _this.fireEvent('positionchanged', this, location);
26631                 }
26632             });
26633             
26634             return;
26635         }
26636         
26637         this.fireEvent('positionchanged', this, location);
26638     },
26639     
26640     resize: function()
26641     {
26642         google.maps.event.trigger(this.gMapContext.map, "resize");
26643         
26644         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26645         
26646         this.fireEvent('resize', this);
26647     },
26648     
26649     setPositionByLatLng: function(latitude, longitude)
26650     {
26651         this.setPosition(new google.maps.LatLng(latitude, longitude));
26652     },
26653     
26654     getCurrentPosition: function() 
26655     {
26656         return {
26657             latitude: this.gMapContext.location.lat(),
26658             longitude: this.gMapContext.location.lng()
26659         };
26660     },
26661     
26662     getAddressName: function() 
26663     {
26664         return this.gMapContext.locationName;
26665     },
26666     
26667     getAddressComponents: function() 
26668     {
26669         return this.gMapContext.addressComponents;
26670     },
26671     
26672     address_component_from_google_geocode: function(address_components) 
26673     {
26674         var result = {};
26675         
26676         for (var i = 0; i < address_components.length; i++) {
26677             var component = address_components[i];
26678             if (component.types.indexOf("postal_code") >= 0) {
26679                 result.postalCode = component.short_name;
26680             } else if (component.types.indexOf("street_number") >= 0) {
26681                 result.streetNumber = component.short_name;
26682             } else if (component.types.indexOf("route") >= 0) {
26683                 result.streetName = component.short_name;
26684             } else if (component.types.indexOf("neighborhood") >= 0) {
26685                 result.city = component.short_name;
26686             } else if (component.types.indexOf("locality") >= 0) {
26687                 result.city = component.short_name;
26688             } else if (component.types.indexOf("sublocality") >= 0) {
26689                 result.district = component.short_name;
26690             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26691                 result.stateOrProvince = component.short_name;
26692             } else if (component.types.indexOf("country") >= 0) {
26693                 result.country = component.short_name;
26694             }
26695         }
26696         
26697         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26698         result.addressLine2 = "";
26699         return result;
26700     },
26701     
26702     setZoomLevel: function(zoom)
26703     {
26704         this.gMapContext.map.setZoom(zoom);
26705     },
26706     
26707     show: function()
26708     {
26709         if(!this.el){
26710             return;
26711         }
26712         
26713         this.el.show();
26714         
26715         this.resize();
26716         
26717         this.fireEvent('show', this);
26718     },
26719     
26720     hide: function()
26721     {
26722         if(!this.el){
26723             return;
26724         }
26725         
26726         this.el.hide();
26727         
26728         this.fireEvent('hide', this);
26729     }
26730     
26731 });
26732
26733 Roo.apply(Roo.bootstrap.LocationPicker, {
26734     
26735     OverlayView : function(map, options)
26736     {
26737         options = options || {};
26738         
26739         this.setMap(map);
26740     }
26741     
26742     
26743 });/*
26744  * - LGPL
26745  *
26746  * Alert
26747  * 
26748  */
26749
26750 /**
26751  * @class Roo.bootstrap.Alert
26752  * @extends Roo.bootstrap.Component
26753  * Bootstrap Alert class
26754  * @cfg {String} title The title of alert
26755  * @cfg {String} html The content of alert
26756  * @cfg {String} weight (  success | info | warning | danger )
26757  * @cfg {String} faicon font-awesomeicon
26758  * 
26759  * @constructor
26760  * Create a new alert
26761  * @param {Object} config The config object
26762  */
26763
26764
26765 Roo.bootstrap.Alert = function(config){
26766     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26767     
26768 };
26769
26770 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26771     
26772     title: '',
26773     html: '',
26774     weight: false,
26775     faicon: false,
26776     
26777     getAutoCreate : function()
26778     {
26779         
26780         var cfg = {
26781             tag : 'div',
26782             cls : 'alert',
26783             cn : [
26784                 {
26785                     tag : 'i',
26786                     cls : 'roo-alert-icon'
26787                     
26788                 },
26789                 {
26790                     tag : 'b',
26791                     cls : 'roo-alert-title',
26792                     html : this.title
26793                 },
26794                 {
26795                     tag : 'span',
26796                     cls : 'roo-alert-text',
26797                     html : this.html
26798                 }
26799             ]
26800         };
26801         
26802         if(this.faicon){
26803             cfg.cn[0].cls += ' fa ' + this.faicon;
26804         }
26805         
26806         if(this.weight){
26807             cfg.cls += ' alert-' + this.weight;
26808         }
26809         
26810         return cfg;
26811     },
26812     
26813     initEvents: function() 
26814     {
26815         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26816     },
26817     
26818     setTitle : function(str)
26819     {
26820         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26821     },
26822     
26823     setText : function(str)
26824     {
26825         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26826     },
26827     
26828     setWeight : function(weight)
26829     {
26830         if(this.weight){
26831             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26832         }
26833         
26834         this.weight = weight;
26835         
26836         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26837     },
26838     
26839     setIcon : function(icon)
26840     {
26841         if(this.faicon){
26842             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26843         }
26844         
26845         this.faicon = icon;
26846         
26847         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26848     },
26849     
26850     hide: function() 
26851     {
26852         this.el.hide();   
26853     },
26854     
26855     show: function() 
26856     {  
26857         this.el.show();   
26858     }
26859     
26860 });
26861
26862  
26863 /*
26864 * Licence: LGPL
26865 */
26866
26867 /**
26868  * @class Roo.bootstrap.UploadCropbox
26869  * @extends Roo.bootstrap.Component
26870  * Bootstrap UploadCropbox class
26871  * @cfg {String} emptyText show when image has been loaded
26872  * @cfg {String} rotateNotify show when image too small to rotate
26873  * @cfg {Number} errorTimeout default 3000
26874  * @cfg {Number} minWidth default 300
26875  * @cfg {Number} minHeight default 300
26876  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26877  * @cfg {Boolean} isDocument (true|false) default false
26878  * @cfg {String} url action url
26879  * @cfg {String} paramName default 'imageUpload'
26880  * @cfg {String} method default POST
26881  * @cfg {Boolean} loadMask (true|false) default true
26882  * @cfg {Boolean} loadingText default 'Loading...'
26883  * 
26884  * @constructor
26885  * Create a new UploadCropbox
26886  * @param {Object} config The config object
26887  */
26888
26889 Roo.bootstrap.UploadCropbox = function(config){
26890     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26891     
26892     this.addEvents({
26893         /**
26894          * @event beforeselectfile
26895          * Fire before select file
26896          * @param {Roo.bootstrap.UploadCropbox} this
26897          */
26898         "beforeselectfile" : true,
26899         /**
26900          * @event initial
26901          * Fire after initEvent
26902          * @param {Roo.bootstrap.UploadCropbox} this
26903          */
26904         "initial" : true,
26905         /**
26906          * @event crop
26907          * Fire after initEvent
26908          * @param {Roo.bootstrap.UploadCropbox} this
26909          * @param {String} data
26910          */
26911         "crop" : true,
26912         /**
26913          * @event prepare
26914          * Fire when preparing the file data
26915          * @param {Roo.bootstrap.UploadCropbox} this
26916          * @param {Object} file
26917          */
26918         "prepare" : true,
26919         /**
26920          * @event exception
26921          * Fire when get exception
26922          * @param {Roo.bootstrap.UploadCropbox} this
26923          * @param {XMLHttpRequest} xhr
26924          */
26925         "exception" : true,
26926         /**
26927          * @event beforeloadcanvas
26928          * Fire before load the canvas
26929          * @param {Roo.bootstrap.UploadCropbox} this
26930          * @param {String} src
26931          */
26932         "beforeloadcanvas" : true,
26933         /**
26934          * @event trash
26935          * Fire when trash image
26936          * @param {Roo.bootstrap.UploadCropbox} this
26937          */
26938         "trash" : true,
26939         /**
26940          * @event download
26941          * Fire when download the image
26942          * @param {Roo.bootstrap.UploadCropbox} this
26943          */
26944         "download" : true,
26945         /**
26946          * @event footerbuttonclick
26947          * Fire when footerbuttonclick
26948          * @param {Roo.bootstrap.UploadCropbox} this
26949          * @param {String} type
26950          */
26951         "footerbuttonclick" : true,
26952         /**
26953          * @event resize
26954          * Fire when resize
26955          * @param {Roo.bootstrap.UploadCropbox} this
26956          */
26957         "resize" : true,
26958         /**
26959          * @event rotate
26960          * Fire when rotate the image
26961          * @param {Roo.bootstrap.UploadCropbox} this
26962          * @param {String} pos
26963          */
26964         "rotate" : true,
26965         /**
26966          * @event inspect
26967          * Fire when inspect the file
26968          * @param {Roo.bootstrap.UploadCropbox} this
26969          * @param {Object} file
26970          */
26971         "inspect" : true,
26972         /**
26973          * @event upload
26974          * Fire when xhr upload the file
26975          * @param {Roo.bootstrap.UploadCropbox} this
26976          * @param {Object} data
26977          */
26978         "upload" : true,
26979         /**
26980          * @event arrange
26981          * Fire when arrange the file data
26982          * @param {Roo.bootstrap.UploadCropbox} this
26983          * @param {Object} formData
26984          */
26985         "arrange" : true
26986     });
26987     
26988     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26989 };
26990
26991 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26992     
26993     emptyText : 'Click to upload image',
26994     rotateNotify : 'Image is too small to rotate',
26995     errorTimeout : 3000,
26996     scale : 0,
26997     baseScale : 1,
26998     rotate : 0,
26999     dragable : false,
27000     pinching : false,
27001     mouseX : 0,
27002     mouseY : 0,
27003     cropData : false,
27004     minWidth : 300,
27005     minHeight : 300,
27006     file : false,
27007     exif : {},
27008     baseRotate : 1,
27009     cropType : 'image/jpeg',
27010     buttons : false,
27011     canvasLoaded : false,
27012     isDocument : false,
27013     method : 'POST',
27014     paramName : 'imageUpload',
27015     loadMask : true,
27016     loadingText : 'Loading...',
27017     maskEl : false,
27018     
27019     getAutoCreate : function()
27020     {
27021         var cfg = {
27022             tag : 'div',
27023             cls : 'roo-upload-cropbox',
27024             cn : [
27025                 {
27026                     tag : 'input',
27027                     cls : 'roo-upload-cropbox-selector',
27028                     type : 'file'
27029                 },
27030                 {
27031                     tag : 'div',
27032                     cls : 'roo-upload-cropbox-body',
27033                     style : 'cursor:pointer',
27034                     cn : [
27035                         {
27036                             tag : 'div',
27037                             cls : 'roo-upload-cropbox-preview'
27038                         },
27039                         {
27040                             tag : 'div',
27041                             cls : 'roo-upload-cropbox-thumb'
27042                         },
27043                         {
27044                             tag : 'div',
27045                             cls : 'roo-upload-cropbox-empty-notify',
27046                             html : this.emptyText
27047                         },
27048                         {
27049                             tag : 'div',
27050                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27051                             html : this.rotateNotify
27052                         }
27053                     ]
27054                 },
27055                 {
27056                     tag : 'div',
27057                     cls : 'roo-upload-cropbox-footer',
27058                     cn : {
27059                         tag : 'div',
27060                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27061                         cn : []
27062                     }
27063                 }
27064             ]
27065         };
27066         
27067         return cfg;
27068     },
27069     
27070     onRender : function(ct, position)
27071     {
27072         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27073         
27074         if (this.buttons.length) {
27075             
27076             Roo.each(this.buttons, function(bb) {
27077                 
27078                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27079                 
27080                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27081                 
27082             }, this);
27083         }
27084         
27085         if(this.loadMask){
27086             this.maskEl = this.el;
27087         }
27088     },
27089     
27090     initEvents : function()
27091     {
27092         this.urlAPI = (window.createObjectURL && window) || 
27093                                 (window.URL && URL.revokeObjectURL && URL) || 
27094                                 (window.webkitURL && webkitURL);
27095                         
27096         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27097         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27098         
27099         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27100         this.selectorEl.hide();
27101         
27102         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27103         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27104         
27105         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27106         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27107         this.thumbEl.hide();
27108         
27109         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27110         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27111         
27112         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27113         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27114         this.errorEl.hide();
27115         
27116         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27117         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27118         this.footerEl.hide();
27119         
27120         this.setThumbBoxSize();
27121         
27122         this.bind();
27123         
27124         this.resize();
27125         
27126         this.fireEvent('initial', this);
27127     },
27128
27129     bind : function()
27130     {
27131         var _this = this;
27132         
27133         window.addEventListener("resize", function() { _this.resize(); } );
27134         
27135         this.bodyEl.on('click', this.beforeSelectFile, this);
27136         
27137         if(Roo.isTouch){
27138             this.bodyEl.on('touchstart', this.onTouchStart, this);
27139             this.bodyEl.on('touchmove', this.onTouchMove, this);
27140             this.bodyEl.on('touchend', this.onTouchEnd, this);
27141         }
27142         
27143         if(!Roo.isTouch){
27144             this.bodyEl.on('mousedown', this.onMouseDown, this);
27145             this.bodyEl.on('mousemove', this.onMouseMove, this);
27146             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27147             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27148             Roo.get(document).on('mouseup', this.onMouseUp, this);
27149         }
27150         
27151         this.selectorEl.on('change', this.onFileSelected, this);
27152     },
27153     
27154     reset : function()
27155     {    
27156         this.scale = 0;
27157         this.baseScale = 1;
27158         this.rotate = 0;
27159         this.baseRotate = 1;
27160         this.dragable = false;
27161         this.pinching = false;
27162         this.mouseX = 0;
27163         this.mouseY = 0;
27164         this.cropData = false;
27165         this.notifyEl.dom.innerHTML = this.emptyText;
27166         
27167         this.selectorEl.dom.value = '';
27168         
27169     },
27170     
27171     resize : function()
27172     {
27173         if(this.fireEvent('resize', this) != false){
27174             this.setThumbBoxPosition();
27175             this.setCanvasPosition();
27176         }
27177     },
27178     
27179     onFooterButtonClick : function(e, el, o, type)
27180     {
27181         switch (type) {
27182             case 'rotate-left' :
27183                 this.onRotateLeft(e);
27184                 break;
27185             case 'rotate-right' :
27186                 this.onRotateRight(e);
27187                 break;
27188             case 'picture' :
27189                 this.beforeSelectFile(e);
27190                 break;
27191             case 'trash' :
27192                 this.trash(e);
27193                 break;
27194             case 'crop' :
27195                 this.crop(e);
27196                 break;
27197             case 'download' :
27198                 this.download(e);
27199                 break;
27200             default :
27201                 break;
27202         }
27203         
27204         this.fireEvent('footerbuttonclick', this, type);
27205     },
27206     
27207     beforeSelectFile : function(e)
27208     {
27209         e.preventDefault();
27210         
27211         if(this.fireEvent('beforeselectfile', this) != false){
27212             this.selectorEl.dom.click();
27213         }
27214     },
27215     
27216     onFileSelected : function(e)
27217     {
27218         e.preventDefault();
27219         
27220         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27221             return;
27222         }
27223         
27224         var file = this.selectorEl.dom.files[0];
27225         
27226         if(this.fireEvent('inspect', this, file) != false){
27227             this.prepare(file);
27228         }
27229         
27230     },
27231     
27232     trash : function(e)
27233     {
27234         this.fireEvent('trash', this);
27235     },
27236     
27237     download : function(e)
27238     {
27239         this.fireEvent('download', this);
27240     },
27241     
27242     loadCanvas : function(src)
27243     {   
27244         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27245             
27246             this.reset();
27247             
27248             this.imageEl = document.createElement('img');
27249             
27250             var _this = this;
27251             
27252             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27253             
27254             this.imageEl.src = src;
27255         }
27256     },
27257     
27258     onLoadCanvas : function()
27259     {   
27260         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27261         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27262         
27263         this.bodyEl.un('click', this.beforeSelectFile, this);
27264         
27265         this.notifyEl.hide();
27266         this.thumbEl.show();
27267         this.footerEl.show();
27268         
27269         this.baseRotateLevel();
27270         
27271         if(this.isDocument){
27272             this.setThumbBoxSize();
27273         }
27274         
27275         this.setThumbBoxPosition();
27276         
27277         this.baseScaleLevel();
27278         
27279         this.draw();
27280         
27281         this.resize();
27282         
27283         this.canvasLoaded = true;
27284         
27285         if(this.loadMask){
27286             this.maskEl.unmask();
27287         }
27288         
27289     },
27290     
27291     setCanvasPosition : function()
27292     {   
27293         if(!this.canvasEl){
27294             return;
27295         }
27296         
27297         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27298         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27299         
27300         this.previewEl.setLeft(pw);
27301         this.previewEl.setTop(ph);
27302         
27303     },
27304     
27305     onMouseDown : function(e)
27306     {   
27307         e.stopEvent();
27308         
27309         this.dragable = true;
27310         this.pinching = false;
27311         
27312         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27313             this.dragable = false;
27314             return;
27315         }
27316         
27317         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27318         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27319         
27320     },
27321     
27322     onMouseMove : function(e)
27323     {   
27324         e.stopEvent();
27325         
27326         if(!this.canvasLoaded){
27327             return;
27328         }
27329         
27330         if (!this.dragable){
27331             return;
27332         }
27333         
27334         var minX = Math.ceil(this.thumbEl.getLeft(true));
27335         var minY = Math.ceil(this.thumbEl.getTop(true));
27336         
27337         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27338         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27339         
27340         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27341         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27342         
27343         x = x - this.mouseX;
27344         y = y - this.mouseY;
27345         
27346         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27347         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27348         
27349         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27350         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27351         
27352         this.previewEl.setLeft(bgX);
27353         this.previewEl.setTop(bgY);
27354         
27355         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27356         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27357     },
27358     
27359     onMouseUp : function(e)
27360     {   
27361         e.stopEvent();
27362         
27363         this.dragable = false;
27364     },
27365     
27366     onMouseWheel : function(e)
27367     {   
27368         e.stopEvent();
27369         
27370         this.startScale = this.scale;
27371         
27372         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27373         
27374         if(!this.zoomable()){
27375             this.scale = this.startScale;
27376             return;
27377         }
27378         
27379         this.draw();
27380         
27381         return;
27382     },
27383     
27384     zoomable : function()
27385     {
27386         var minScale = this.thumbEl.getWidth() / this.minWidth;
27387         
27388         if(this.minWidth < this.minHeight){
27389             minScale = this.thumbEl.getHeight() / this.minHeight;
27390         }
27391         
27392         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27393         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27394         
27395         if(
27396                 this.isDocument &&
27397                 (this.rotate == 0 || this.rotate == 180) && 
27398                 (
27399                     width > this.imageEl.OriginWidth || 
27400                     height > this.imageEl.OriginHeight ||
27401                     (width < this.minWidth && height < this.minHeight)
27402                 )
27403         ){
27404             return false;
27405         }
27406         
27407         if(
27408                 this.isDocument &&
27409                 (this.rotate == 90 || this.rotate == 270) && 
27410                 (
27411                     width > this.imageEl.OriginWidth || 
27412                     height > this.imageEl.OriginHeight ||
27413                     (width < this.minHeight && height < this.minWidth)
27414                 )
27415         ){
27416             return false;
27417         }
27418         
27419         if(
27420                 !this.isDocument &&
27421                 (this.rotate == 0 || this.rotate == 180) && 
27422                 (
27423                     width < this.minWidth || 
27424                     width > this.imageEl.OriginWidth || 
27425                     height < this.minHeight || 
27426                     height > this.imageEl.OriginHeight
27427                 )
27428         ){
27429             return false;
27430         }
27431         
27432         if(
27433                 !this.isDocument &&
27434                 (this.rotate == 90 || this.rotate == 270) && 
27435                 (
27436                     width < this.minHeight || 
27437                     width > this.imageEl.OriginWidth || 
27438                     height < this.minWidth || 
27439                     height > this.imageEl.OriginHeight
27440                 )
27441         ){
27442             return false;
27443         }
27444         
27445         return true;
27446         
27447     },
27448     
27449     onRotateLeft : function(e)
27450     {   
27451         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27452             
27453             var minScale = this.thumbEl.getWidth() / this.minWidth;
27454             
27455             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27456             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27457             
27458             this.startScale = this.scale;
27459             
27460             while (this.getScaleLevel() < minScale){
27461             
27462                 this.scale = this.scale + 1;
27463                 
27464                 if(!this.zoomable()){
27465                     break;
27466                 }
27467                 
27468                 if(
27469                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27470                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27471                 ){
27472                     continue;
27473                 }
27474                 
27475                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27476
27477                 this.draw();
27478                 
27479                 return;
27480             }
27481             
27482             this.scale = this.startScale;
27483             
27484             this.onRotateFail();
27485             
27486             return false;
27487         }
27488         
27489         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27490
27491         if(this.isDocument){
27492             this.setThumbBoxSize();
27493             this.setThumbBoxPosition();
27494             this.setCanvasPosition();
27495         }
27496         
27497         this.draw();
27498         
27499         this.fireEvent('rotate', this, 'left');
27500         
27501     },
27502     
27503     onRotateRight : function(e)
27504     {
27505         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27506             
27507             var minScale = this.thumbEl.getWidth() / this.minWidth;
27508         
27509             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27510             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27511             
27512             this.startScale = this.scale;
27513             
27514             while (this.getScaleLevel() < minScale){
27515             
27516                 this.scale = this.scale + 1;
27517                 
27518                 if(!this.zoomable()){
27519                     break;
27520                 }
27521                 
27522                 if(
27523                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27524                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27525                 ){
27526                     continue;
27527                 }
27528                 
27529                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27530
27531                 this.draw();
27532                 
27533                 return;
27534             }
27535             
27536             this.scale = this.startScale;
27537             
27538             this.onRotateFail();
27539             
27540             return false;
27541         }
27542         
27543         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27544
27545         if(this.isDocument){
27546             this.setThumbBoxSize();
27547             this.setThumbBoxPosition();
27548             this.setCanvasPosition();
27549         }
27550         
27551         this.draw();
27552         
27553         this.fireEvent('rotate', this, 'right');
27554     },
27555     
27556     onRotateFail : function()
27557     {
27558         this.errorEl.show(true);
27559         
27560         var _this = this;
27561         
27562         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27563     },
27564     
27565     draw : function()
27566     {
27567         this.previewEl.dom.innerHTML = '';
27568         
27569         var canvasEl = document.createElement("canvas");
27570         
27571         var contextEl = canvasEl.getContext("2d");
27572         
27573         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27574         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27575         var center = this.imageEl.OriginWidth / 2;
27576         
27577         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27578             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27579             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27580             center = this.imageEl.OriginHeight / 2;
27581         }
27582         
27583         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27584         
27585         contextEl.translate(center, center);
27586         contextEl.rotate(this.rotate * Math.PI / 180);
27587
27588         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27589         
27590         this.canvasEl = document.createElement("canvas");
27591         
27592         this.contextEl = this.canvasEl.getContext("2d");
27593         
27594         switch (this.rotate) {
27595             case 0 :
27596                 
27597                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27598                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27599                 
27600                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27601                 
27602                 break;
27603             case 90 : 
27604                 
27605                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27606                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27607                 
27608                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27609                     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);
27610                     break;
27611                 }
27612                 
27613                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27614                 
27615                 break;
27616             case 180 :
27617                 
27618                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27619                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27620                 
27621                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27622                     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);
27623                     break;
27624                 }
27625                 
27626                 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27627                 
27628                 break;
27629             case 270 :
27630                 
27631                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27632                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27633         
27634                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27635                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27636                     break;
27637                 }
27638                 
27639                 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27640                 
27641                 break;
27642             default : 
27643                 break;
27644         }
27645         
27646         this.previewEl.appendChild(this.canvasEl);
27647         
27648         this.setCanvasPosition();
27649     },
27650     
27651     crop : function()
27652     {
27653         if(!this.canvasLoaded){
27654             return;
27655         }
27656         
27657         var imageCanvas = document.createElement("canvas");
27658         
27659         var imageContext = imageCanvas.getContext("2d");
27660         
27661         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27662         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27663         
27664         var center = imageCanvas.width / 2;
27665         
27666         imageContext.translate(center, center);
27667         
27668         imageContext.rotate(this.rotate * Math.PI / 180);
27669         
27670         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27671         
27672         var canvas = document.createElement("canvas");
27673         
27674         var context = canvas.getContext("2d");
27675                 
27676         canvas.width = this.minWidth;
27677         canvas.height = this.minHeight;
27678
27679         switch (this.rotate) {
27680             case 0 :
27681                 
27682                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27683                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27684                 
27685                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27686                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27687                 
27688                 var targetWidth = this.minWidth - 2 * x;
27689                 var targetHeight = this.minHeight - 2 * y;
27690                 
27691                 var scale = 1;
27692                 
27693                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27694                     scale = targetWidth / width;
27695                 }
27696                 
27697                 if(x > 0 && y == 0){
27698                     scale = targetHeight / height;
27699                 }
27700                 
27701                 if(x > 0 && y > 0){
27702                     scale = targetWidth / width;
27703                     
27704                     if(width < height){
27705                         scale = targetHeight / height;
27706                     }
27707                 }
27708                 
27709                 context.scale(scale, scale);
27710                 
27711                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27712                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27713
27714                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27715                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27716
27717                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27718                 
27719                 break;
27720             case 90 : 
27721                 
27722                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27723                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27724                 
27725                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27726                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27727                 
27728                 var targetWidth = this.minWidth - 2 * x;
27729                 var targetHeight = this.minHeight - 2 * y;
27730                 
27731                 var scale = 1;
27732                 
27733                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27734                     scale = targetWidth / width;
27735                 }
27736                 
27737                 if(x > 0 && y == 0){
27738                     scale = targetHeight / height;
27739                 }
27740                 
27741                 if(x > 0 && y > 0){
27742                     scale = targetWidth / width;
27743                     
27744                     if(width < height){
27745                         scale = targetHeight / height;
27746                     }
27747                 }
27748                 
27749                 context.scale(scale, scale);
27750                 
27751                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27752                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27753
27754                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27755                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27756                 
27757                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27758                 
27759                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27760                 
27761                 break;
27762             case 180 :
27763                 
27764                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27765                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27766                 
27767                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27768                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27769                 
27770                 var targetWidth = this.minWidth - 2 * x;
27771                 var targetHeight = this.minHeight - 2 * y;
27772                 
27773                 var scale = 1;
27774                 
27775                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27776                     scale = targetWidth / width;
27777                 }
27778                 
27779                 if(x > 0 && y == 0){
27780                     scale = targetHeight / height;
27781                 }
27782                 
27783                 if(x > 0 && y > 0){
27784                     scale = targetWidth / width;
27785                     
27786                     if(width < height){
27787                         scale = targetHeight / height;
27788                     }
27789                 }
27790                 
27791                 context.scale(scale, scale);
27792                 
27793                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27794                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27795
27796                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27797                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27798
27799                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27800                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27801                 
27802                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27803                 
27804                 break;
27805             case 270 :
27806                 
27807                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27808                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27809                 
27810                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27811                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27812                 
27813                 var targetWidth = this.minWidth - 2 * x;
27814                 var targetHeight = this.minHeight - 2 * y;
27815                 
27816                 var scale = 1;
27817                 
27818                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27819                     scale = targetWidth / width;
27820                 }
27821                 
27822                 if(x > 0 && y == 0){
27823                     scale = targetHeight / height;
27824                 }
27825                 
27826                 if(x > 0 && y > 0){
27827                     scale = targetWidth / width;
27828                     
27829                     if(width < height){
27830                         scale = targetHeight / height;
27831                     }
27832                 }
27833                 
27834                 context.scale(scale, scale);
27835                 
27836                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27837                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27838
27839                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27840                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27841                 
27842                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27843                 
27844                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27845                 
27846                 break;
27847             default : 
27848                 break;
27849         }
27850         
27851         this.cropData = canvas.toDataURL(this.cropType);
27852         
27853         if(this.fireEvent('crop', this, this.cropData) !== false){
27854             this.process(this.file, this.cropData);
27855         }
27856         
27857         return;
27858         
27859     },
27860     
27861     setThumbBoxSize : function()
27862     {
27863         var width, height;
27864         
27865         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27866             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27867             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27868             
27869             this.minWidth = width;
27870             this.minHeight = height;
27871             
27872             if(this.rotate == 90 || this.rotate == 270){
27873                 this.minWidth = height;
27874                 this.minHeight = width;
27875             }
27876         }
27877         
27878         height = 300;
27879         width = Math.ceil(this.minWidth * height / this.minHeight);
27880         
27881         if(this.minWidth > this.minHeight){
27882             width = 300;
27883             height = Math.ceil(this.minHeight * width / this.minWidth);
27884         }
27885         
27886         this.thumbEl.setStyle({
27887             width : width + 'px',
27888             height : height + 'px'
27889         });
27890
27891         return;
27892             
27893     },
27894     
27895     setThumbBoxPosition : function()
27896     {
27897         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27898         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27899         
27900         this.thumbEl.setLeft(x);
27901         this.thumbEl.setTop(y);
27902         
27903     },
27904     
27905     baseRotateLevel : function()
27906     {
27907         this.baseRotate = 1;
27908         
27909         if(
27910                 typeof(this.exif) != 'undefined' &&
27911                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27912                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27913         ){
27914             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27915         }
27916         
27917         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27918         
27919     },
27920     
27921     baseScaleLevel : function()
27922     {
27923         var width, height;
27924         
27925         if(this.isDocument){
27926             
27927             if(this.baseRotate == 6 || this.baseRotate == 8){
27928             
27929                 height = this.thumbEl.getHeight();
27930                 this.baseScale = height / this.imageEl.OriginWidth;
27931
27932                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27933                     width = this.thumbEl.getWidth();
27934                     this.baseScale = width / this.imageEl.OriginHeight;
27935                 }
27936
27937                 return;
27938             }
27939
27940             height = this.thumbEl.getHeight();
27941             this.baseScale = height / this.imageEl.OriginHeight;
27942
27943             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27944                 width = this.thumbEl.getWidth();
27945                 this.baseScale = width / this.imageEl.OriginWidth;
27946             }
27947
27948             return;
27949         }
27950         
27951         if(this.baseRotate == 6 || this.baseRotate == 8){
27952             
27953             width = this.thumbEl.getHeight();
27954             this.baseScale = width / this.imageEl.OriginHeight;
27955             
27956             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27957                 height = this.thumbEl.getWidth();
27958                 this.baseScale = height / this.imageEl.OriginHeight;
27959             }
27960             
27961             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27962                 height = this.thumbEl.getWidth();
27963                 this.baseScale = height / this.imageEl.OriginHeight;
27964                 
27965                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27966                     width = this.thumbEl.getHeight();
27967                     this.baseScale = width / this.imageEl.OriginWidth;
27968                 }
27969             }
27970             
27971             return;
27972         }
27973         
27974         width = this.thumbEl.getWidth();
27975         this.baseScale = width / this.imageEl.OriginWidth;
27976         
27977         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27978             height = this.thumbEl.getHeight();
27979             this.baseScale = height / this.imageEl.OriginHeight;
27980         }
27981         
27982         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27983             
27984             height = this.thumbEl.getHeight();
27985             this.baseScale = height / this.imageEl.OriginHeight;
27986             
27987             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27988                 width = this.thumbEl.getWidth();
27989                 this.baseScale = width / this.imageEl.OriginWidth;
27990             }
27991             
27992         }
27993         
27994         return;
27995     },
27996     
27997     getScaleLevel : function()
27998     {
27999         return this.baseScale * Math.pow(1.1, this.scale);
28000     },
28001     
28002     onTouchStart : function(e)
28003     {
28004         if(!this.canvasLoaded){
28005             this.beforeSelectFile(e);
28006             return;
28007         }
28008         
28009         var touches = e.browserEvent.touches;
28010         
28011         if(!touches){
28012             return;
28013         }
28014         
28015         if(touches.length == 1){
28016             this.onMouseDown(e);
28017             return;
28018         }
28019         
28020         if(touches.length != 2){
28021             return;
28022         }
28023         
28024         var coords = [];
28025         
28026         for(var i = 0, finger; finger = touches[i]; i++){
28027             coords.push(finger.pageX, finger.pageY);
28028         }
28029         
28030         var x = Math.pow(coords[0] - coords[2], 2);
28031         var y = Math.pow(coords[1] - coords[3], 2);
28032         
28033         this.startDistance = Math.sqrt(x + y);
28034         
28035         this.startScale = this.scale;
28036         
28037         this.pinching = true;
28038         this.dragable = false;
28039         
28040     },
28041     
28042     onTouchMove : function(e)
28043     {
28044         if(!this.pinching && !this.dragable){
28045             return;
28046         }
28047         
28048         var touches = e.browserEvent.touches;
28049         
28050         if(!touches){
28051             return;
28052         }
28053         
28054         if(this.dragable){
28055             this.onMouseMove(e);
28056             return;
28057         }
28058         
28059         var coords = [];
28060         
28061         for(var i = 0, finger; finger = touches[i]; i++){
28062             coords.push(finger.pageX, finger.pageY);
28063         }
28064         
28065         var x = Math.pow(coords[0] - coords[2], 2);
28066         var y = Math.pow(coords[1] - coords[3], 2);
28067         
28068         this.endDistance = Math.sqrt(x + y);
28069         
28070         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28071         
28072         if(!this.zoomable()){
28073             this.scale = this.startScale;
28074             return;
28075         }
28076         
28077         this.draw();
28078         
28079     },
28080     
28081     onTouchEnd : function(e)
28082     {
28083         this.pinching = false;
28084         this.dragable = false;
28085         
28086     },
28087     
28088     process : function(file, crop)
28089     {
28090         if(this.loadMask){
28091             this.maskEl.mask(this.loadingText);
28092         }
28093         
28094         this.xhr = new XMLHttpRequest();
28095         
28096         file.xhr = this.xhr;
28097
28098         this.xhr.open(this.method, this.url, true);
28099         
28100         var headers = {
28101             "Accept": "application/json",
28102             "Cache-Control": "no-cache",
28103             "X-Requested-With": "XMLHttpRequest"
28104         };
28105         
28106         for (var headerName in headers) {
28107             var headerValue = headers[headerName];
28108             if (headerValue) {
28109                 this.xhr.setRequestHeader(headerName, headerValue);
28110             }
28111         }
28112         
28113         var _this = this;
28114         
28115         this.xhr.onload = function()
28116         {
28117             _this.xhrOnLoad(_this.xhr);
28118         }
28119         
28120         this.xhr.onerror = function()
28121         {
28122             _this.xhrOnError(_this.xhr);
28123         }
28124         
28125         var formData = new FormData();
28126
28127         formData.append('returnHTML', 'NO');
28128         
28129         if(crop){
28130             formData.append('crop', crop);
28131         }
28132         
28133         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28134             formData.append(this.paramName, file, file.name);
28135         }
28136         
28137         if(typeof(file.filename) != 'undefined'){
28138             formData.append('filename', file.filename);
28139         }
28140         
28141         if(typeof(file.mimetype) != 'undefined'){
28142             formData.append('mimetype', file.mimetype);
28143         }
28144         
28145         if(this.fireEvent('arrange', this, formData) != false){
28146             this.xhr.send(formData);
28147         };
28148     },
28149     
28150     xhrOnLoad : function(xhr)
28151     {
28152         if(this.loadMask){
28153             this.maskEl.unmask();
28154         }
28155         
28156         if (xhr.readyState !== 4) {
28157             this.fireEvent('exception', this, xhr);
28158             return;
28159         }
28160
28161         var response = Roo.decode(xhr.responseText);
28162         
28163         if(!response.success){
28164             this.fireEvent('exception', this, xhr);
28165             return;
28166         }
28167         
28168         var response = Roo.decode(xhr.responseText);
28169         
28170         this.fireEvent('upload', this, response);
28171         
28172     },
28173     
28174     xhrOnError : function()
28175     {
28176         if(this.loadMask){
28177             this.maskEl.unmask();
28178         }
28179         
28180         Roo.log('xhr on error');
28181         
28182         var response = Roo.decode(xhr.responseText);
28183           
28184         Roo.log(response);
28185         
28186     },
28187     
28188     prepare : function(file)
28189     {   
28190         if(this.loadMask){
28191             this.maskEl.mask(this.loadingText);
28192         }
28193         
28194         this.file = false;
28195         this.exif = {};
28196         
28197         if(typeof(file) === 'string'){
28198             this.loadCanvas(file);
28199             return;
28200         }
28201         
28202         if(!file || !this.urlAPI){
28203             return;
28204         }
28205         
28206         this.file = file;
28207         this.cropType = file.type;
28208         
28209         var _this = this;
28210         
28211         if(this.fireEvent('prepare', this, this.file) != false){
28212             
28213             var reader = new FileReader();
28214             
28215             reader.onload = function (e) {
28216                 if (e.target.error) {
28217                     Roo.log(e.target.error);
28218                     return;
28219                 }
28220                 
28221                 var buffer = e.target.result,
28222                     dataView = new DataView(buffer),
28223                     offset = 2,
28224                     maxOffset = dataView.byteLength - 4,
28225                     markerBytes,
28226                     markerLength;
28227                 
28228                 if (dataView.getUint16(0) === 0xffd8) {
28229                     while (offset < maxOffset) {
28230                         markerBytes = dataView.getUint16(offset);
28231                         
28232                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28233                             markerLength = dataView.getUint16(offset + 2) + 2;
28234                             if (offset + markerLength > dataView.byteLength) {
28235                                 Roo.log('Invalid meta data: Invalid segment size.');
28236                                 break;
28237                             }
28238                             
28239                             if(markerBytes == 0xffe1){
28240                                 _this.parseExifData(
28241                                     dataView,
28242                                     offset,
28243                                     markerLength
28244                                 );
28245                             }
28246                             
28247                             offset += markerLength;
28248                             
28249                             continue;
28250                         }
28251                         
28252                         break;
28253                     }
28254                     
28255                 }
28256                 
28257                 var url = _this.urlAPI.createObjectURL(_this.file);
28258                 
28259                 _this.loadCanvas(url);
28260                 
28261                 return;
28262             }
28263             
28264             reader.readAsArrayBuffer(this.file);
28265             
28266         }
28267         
28268     },
28269     
28270     parseExifData : function(dataView, offset, length)
28271     {
28272         var tiffOffset = offset + 10,
28273             littleEndian,
28274             dirOffset;
28275     
28276         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28277             // No Exif data, might be XMP data instead
28278             return;
28279         }
28280         
28281         // Check for the ASCII code for "Exif" (0x45786966):
28282         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28283             // No Exif data, might be XMP data instead
28284             return;
28285         }
28286         if (tiffOffset + 8 > dataView.byteLength) {
28287             Roo.log('Invalid Exif data: Invalid segment size.');
28288             return;
28289         }
28290         // Check for the two null bytes:
28291         if (dataView.getUint16(offset + 8) !== 0x0000) {
28292             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28293             return;
28294         }
28295         // Check the byte alignment:
28296         switch (dataView.getUint16(tiffOffset)) {
28297         case 0x4949:
28298             littleEndian = true;
28299             break;
28300         case 0x4D4D:
28301             littleEndian = false;
28302             break;
28303         default:
28304             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28305             return;
28306         }
28307         // Check for the TIFF tag marker (0x002A):
28308         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28309             Roo.log('Invalid Exif data: Missing TIFF marker.');
28310             return;
28311         }
28312         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28313         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28314         
28315         this.parseExifTags(
28316             dataView,
28317             tiffOffset,
28318             tiffOffset + dirOffset,
28319             littleEndian
28320         );
28321     },
28322     
28323     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28324     {
28325         var tagsNumber,
28326             dirEndOffset,
28327             i;
28328         if (dirOffset + 6 > dataView.byteLength) {
28329             Roo.log('Invalid Exif data: Invalid directory offset.');
28330             return;
28331         }
28332         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28333         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28334         if (dirEndOffset + 4 > dataView.byteLength) {
28335             Roo.log('Invalid Exif data: Invalid directory size.');
28336             return;
28337         }
28338         for (i = 0; i < tagsNumber; i += 1) {
28339             this.parseExifTag(
28340                 dataView,
28341                 tiffOffset,
28342                 dirOffset + 2 + 12 * i, // tag offset
28343                 littleEndian
28344             );
28345         }
28346         // Return the offset to the next directory:
28347         return dataView.getUint32(dirEndOffset, littleEndian);
28348     },
28349     
28350     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28351     {
28352         var tag = dataView.getUint16(offset, littleEndian);
28353         
28354         this.exif[tag] = this.getExifValue(
28355             dataView,
28356             tiffOffset,
28357             offset,
28358             dataView.getUint16(offset + 2, littleEndian), // tag type
28359             dataView.getUint32(offset + 4, littleEndian), // tag length
28360             littleEndian
28361         );
28362     },
28363     
28364     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28365     {
28366         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28367             tagSize,
28368             dataOffset,
28369             values,
28370             i,
28371             str,
28372             c;
28373     
28374         if (!tagType) {
28375             Roo.log('Invalid Exif data: Invalid tag type.');
28376             return;
28377         }
28378         
28379         tagSize = tagType.size * length;
28380         // Determine if the value is contained in the dataOffset bytes,
28381         // or if the value at the dataOffset is a pointer to the actual data:
28382         dataOffset = tagSize > 4 ?
28383                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28384         if (dataOffset + tagSize > dataView.byteLength) {
28385             Roo.log('Invalid Exif data: Invalid data offset.');
28386             return;
28387         }
28388         if (length === 1) {
28389             return tagType.getValue(dataView, dataOffset, littleEndian);
28390         }
28391         values = [];
28392         for (i = 0; i < length; i += 1) {
28393             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28394         }
28395         
28396         if (tagType.ascii) {
28397             str = '';
28398             // Concatenate the chars:
28399             for (i = 0; i < values.length; i += 1) {
28400                 c = values[i];
28401                 // Ignore the terminating NULL byte(s):
28402                 if (c === '\u0000') {
28403                     break;
28404                 }
28405                 str += c;
28406             }
28407             return str;
28408         }
28409         return values;
28410     }
28411     
28412 });
28413
28414 Roo.apply(Roo.bootstrap.UploadCropbox, {
28415     tags : {
28416         'Orientation': 0x0112
28417     },
28418     
28419     Orientation: {
28420             1: 0, //'top-left',
28421 //            2: 'top-right',
28422             3: 180, //'bottom-right',
28423 //            4: 'bottom-left',
28424 //            5: 'left-top',
28425             6: 90, //'right-top',
28426 //            7: 'right-bottom',
28427             8: 270 //'left-bottom'
28428     },
28429     
28430     exifTagTypes : {
28431         // byte, 8-bit unsigned int:
28432         1: {
28433             getValue: function (dataView, dataOffset) {
28434                 return dataView.getUint8(dataOffset);
28435             },
28436             size: 1
28437         },
28438         // ascii, 8-bit byte:
28439         2: {
28440             getValue: function (dataView, dataOffset) {
28441                 return String.fromCharCode(dataView.getUint8(dataOffset));
28442             },
28443             size: 1,
28444             ascii: true
28445         },
28446         // short, 16 bit int:
28447         3: {
28448             getValue: function (dataView, dataOffset, littleEndian) {
28449                 return dataView.getUint16(dataOffset, littleEndian);
28450             },
28451             size: 2
28452         },
28453         // long, 32 bit int:
28454         4: {
28455             getValue: function (dataView, dataOffset, littleEndian) {
28456                 return dataView.getUint32(dataOffset, littleEndian);
28457             },
28458             size: 4
28459         },
28460         // rational = two long values, first is numerator, second is denominator:
28461         5: {
28462             getValue: function (dataView, dataOffset, littleEndian) {
28463                 return dataView.getUint32(dataOffset, littleEndian) /
28464                     dataView.getUint32(dataOffset + 4, littleEndian);
28465             },
28466             size: 8
28467         },
28468         // slong, 32 bit signed int:
28469         9: {
28470             getValue: function (dataView, dataOffset, littleEndian) {
28471                 return dataView.getInt32(dataOffset, littleEndian);
28472             },
28473             size: 4
28474         },
28475         // srational, two slongs, first is numerator, second is denominator:
28476         10: {
28477             getValue: function (dataView, dataOffset, littleEndian) {
28478                 return dataView.getInt32(dataOffset, littleEndian) /
28479                     dataView.getInt32(dataOffset + 4, littleEndian);
28480             },
28481             size: 8
28482         }
28483     },
28484     
28485     footer : {
28486         STANDARD : [
28487             {
28488                 tag : 'div',
28489                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28490                 action : 'rotate-left',
28491                 cn : [
28492                     {
28493                         tag : 'button',
28494                         cls : 'btn btn-default',
28495                         html : '<i class="fa fa-undo"></i>'
28496                     }
28497                 ]
28498             },
28499             {
28500                 tag : 'div',
28501                 cls : 'btn-group roo-upload-cropbox-picture',
28502                 action : 'picture',
28503                 cn : [
28504                     {
28505                         tag : 'button',
28506                         cls : 'btn btn-default',
28507                         html : '<i class="fa fa-picture-o"></i>'
28508                     }
28509                 ]
28510             },
28511             {
28512                 tag : 'div',
28513                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28514                 action : 'rotate-right',
28515                 cn : [
28516                     {
28517                         tag : 'button',
28518                         cls : 'btn btn-default',
28519                         html : '<i class="fa fa-repeat"></i>'
28520                     }
28521                 ]
28522             }
28523         ],
28524         DOCUMENT : [
28525             {
28526                 tag : 'div',
28527                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28528                 action : 'rotate-left',
28529                 cn : [
28530                     {
28531                         tag : 'button',
28532                         cls : 'btn btn-default',
28533                         html : '<i class="fa fa-undo"></i>'
28534                     }
28535                 ]
28536             },
28537             {
28538                 tag : 'div',
28539                 cls : 'btn-group roo-upload-cropbox-download',
28540                 action : 'download',
28541                 cn : [
28542                     {
28543                         tag : 'button',
28544                         cls : 'btn btn-default',
28545                         html : '<i class="fa fa-download"></i>'
28546                     }
28547                 ]
28548             },
28549             {
28550                 tag : 'div',
28551                 cls : 'btn-group roo-upload-cropbox-crop',
28552                 action : 'crop',
28553                 cn : [
28554                     {
28555                         tag : 'button',
28556                         cls : 'btn btn-default',
28557                         html : '<i class="fa fa-crop"></i>'
28558                     }
28559                 ]
28560             },
28561             {
28562                 tag : 'div',
28563                 cls : 'btn-group roo-upload-cropbox-trash',
28564                 action : 'trash',
28565                 cn : [
28566                     {
28567                         tag : 'button',
28568                         cls : 'btn btn-default',
28569                         html : '<i class="fa fa-trash"></i>'
28570                     }
28571                 ]
28572             },
28573             {
28574                 tag : 'div',
28575                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28576                 action : 'rotate-right',
28577                 cn : [
28578                     {
28579                         tag : 'button',
28580                         cls : 'btn btn-default',
28581                         html : '<i class="fa fa-repeat"></i>'
28582                     }
28583                 ]
28584             }
28585         ],
28586         ROTATOR : [
28587             {
28588                 tag : 'div',
28589                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28590                 action : 'rotate-left',
28591                 cn : [
28592                     {
28593                         tag : 'button',
28594                         cls : 'btn btn-default',
28595                         html : '<i class="fa fa-undo"></i>'
28596                     }
28597                 ]
28598             },
28599             {
28600                 tag : 'div',
28601                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28602                 action : 'rotate-right',
28603                 cn : [
28604                     {
28605                         tag : 'button',
28606                         cls : 'btn btn-default',
28607                         html : '<i class="fa fa-repeat"></i>'
28608                     }
28609                 ]
28610             }
28611         ]
28612     }
28613 });
28614
28615 /*
28616 * Licence: LGPL
28617 */
28618
28619 /**
28620  * @class Roo.bootstrap.DocumentManager
28621  * @extends Roo.bootstrap.Component
28622  * Bootstrap DocumentManager class
28623  * @cfg {String} paramName default 'imageUpload'
28624  * @cfg {String} toolTipName default 'filename'
28625  * @cfg {String} method default POST
28626  * @cfg {String} url action url
28627  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28628  * @cfg {Boolean} multiple multiple upload default true
28629  * @cfg {Number} thumbSize default 300
28630  * @cfg {String} fieldLabel
28631  * @cfg {Number} labelWidth default 4
28632  * @cfg {String} labelAlign (left|top) default left
28633  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28634 * @cfg {Number} labellg set the width of label (1-12)
28635  * @cfg {Number} labelmd set the width of label (1-12)
28636  * @cfg {Number} labelsm set the width of label (1-12)
28637  * @cfg {Number} labelxs set the width of label (1-12)
28638  * 
28639  * @constructor
28640  * Create a new DocumentManager
28641  * @param {Object} config The config object
28642  */
28643
28644 Roo.bootstrap.DocumentManager = function(config){
28645     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28646     
28647     this.files = [];
28648     this.delegates = [];
28649     
28650     this.addEvents({
28651         /**
28652          * @event initial
28653          * Fire when initial the DocumentManager
28654          * @param {Roo.bootstrap.DocumentManager} this
28655          */
28656         "initial" : true,
28657         /**
28658          * @event inspect
28659          * inspect selected file
28660          * @param {Roo.bootstrap.DocumentManager} this
28661          * @param {File} file
28662          */
28663         "inspect" : true,
28664         /**
28665          * @event exception
28666          * Fire when xhr load exception
28667          * @param {Roo.bootstrap.DocumentManager} this
28668          * @param {XMLHttpRequest} xhr
28669          */
28670         "exception" : true,
28671         /**
28672          * @event afterupload
28673          * Fire when xhr load exception
28674          * @param {Roo.bootstrap.DocumentManager} this
28675          * @param {XMLHttpRequest} xhr
28676          */
28677         "afterupload" : true,
28678         /**
28679          * @event prepare
28680          * prepare the form data
28681          * @param {Roo.bootstrap.DocumentManager} this
28682          * @param {Object} formData
28683          */
28684         "prepare" : true,
28685         /**
28686          * @event remove
28687          * Fire when remove the file
28688          * @param {Roo.bootstrap.DocumentManager} this
28689          * @param {Object} file
28690          */
28691         "remove" : true,
28692         /**
28693          * @event refresh
28694          * Fire after refresh the file
28695          * @param {Roo.bootstrap.DocumentManager} this
28696          */
28697         "refresh" : true,
28698         /**
28699          * @event click
28700          * Fire after click the image
28701          * @param {Roo.bootstrap.DocumentManager} this
28702          * @param {Object} file
28703          */
28704         "click" : true,
28705         /**
28706          * @event edit
28707          * Fire when upload a image and editable set to true
28708          * @param {Roo.bootstrap.DocumentManager} this
28709          * @param {Object} file
28710          */
28711         "edit" : true,
28712         /**
28713          * @event beforeselectfile
28714          * Fire before select file
28715          * @param {Roo.bootstrap.DocumentManager} this
28716          */
28717         "beforeselectfile" : true,
28718         /**
28719          * @event process
28720          * Fire before process file
28721          * @param {Roo.bootstrap.DocumentManager} this
28722          * @param {Object} file
28723          */
28724         "process" : true,
28725         /**
28726          * @event previewrendered
28727          * Fire when preview rendered
28728          * @param {Roo.bootstrap.DocumentManager} this
28729          * @param {Object} file
28730          */
28731         "previewrendered" : true,
28732         /**
28733          */
28734         "previewResize" : true
28735         
28736     });
28737 };
28738
28739 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28740     
28741     boxes : 0,
28742     inputName : '',
28743     thumbSize : 300,
28744     multiple : true,
28745     files : false,
28746     method : 'POST',
28747     url : '',
28748     paramName : 'imageUpload',
28749     toolTipName : 'filename',
28750     fieldLabel : '',
28751     labelWidth : 4,
28752     labelAlign : 'left',
28753     editable : true,
28754     delegates : false,
28755     xhr : false, 
28756     
28757     labellg : 0,
28758     labelmd : 0,
28759     labelsm : 0,
28760     labelxs : 0,
28761     
28762     getAutoCreate : function()
28763     {   
28764         var managerWidget = {
28765             tag : 'div',
28766             cls : 'roo-document-manager',
28767             cn : [
28768                 {
28769                     tag : 'input',
28770                     cls : 'roo-document-manager-selector',
28771                     type : 'file'
28772                 },
28773                 {
28774                     tag : 'div',
28775                     cls : 'roo-document-manager-uploader',
28776                     cn : [
28777                         {
28778                             tag : 'div',
28779                             cls : 'roo-document-manager-upload-btn',
28780                             html : '<i class="fa fa-plus"></i>'
28781                         }
28782                     ]
28783                     
28784                 }
28785             ]
28786         };
28787         
28788         var content = [
28789             {
28790                 tag : 'div',
28791                 cls : 'column col-md-12',
28792                 cn : managerWidget
28793             }
28794         ];
28795         
28796         if(this.fieldLabel.length){
28797             
28798             content = [
28799                 {
28800                     tag : 'div',
28801                     cls : 'column col-md-12',
28802                     html : this.fieldLabel
28803                 },
28804                 {
28805                     tag : 'div',
28806                     cls : 'column col-md-12',
28807                     cn : managerWidget
28808                 }
28809             ];
28810
28811             if(this.labelAlign == 'left'){
28812                 content = [
28813                     {
28814                         tag : 'div',
28815                         cls : 'column',
28816                         html : this.fieldLabel
28817                     },
28818                     {
28819                         tag : 'div',
28820                         cls : 'column',
28821                         cn : managerWidget
28822                     }
28823                 ];
28824                 
28825                 if(this.labelWidth > 12){
28826                     content[0].style = "width: " + this.labelWidth + 'px';
28827                 }
28828
28829                 if(this.labelWidth < 13 && this.labelmd == 0){
28830                     this.labelmd = this.labelWidth;
28831                 }
28832
28833                 if(this.labellg > 0){
28834                     content[0].cls += ' col-lg-' + this.labellg;
28835                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28836                 }
28837
28838                 if(this.labelmd > 0){
28839                     content[0].cls += ' col-md-' + this.labelmd;
28840                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28841                 }
28842
28843                 if(this.labelsm > 0){
28844                     content[0].cls += ' col-sm-' + this.labelsm;
28845                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28846                 }
28847
28848                 if(this.labelxs > 0){
28849                     content[0].cls += ' col-xs-' + this.labelxs;
28850                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28851                 }
28852                 
28853             }
28854         }
28855         
28856         var cfg = {
28857             tag : 'div',
28858             cls : 'row clearfix',
28859             cn : content
28860         };
28861         
28862         return cfg;
28863         
28864     },
28865     
28866     initEvents : function()
28867     {
28868         this.managerEl = this.el.select('.roo-document-manager', true).first();
28869         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28870         
28871         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28872         this.selectorEl.hide();
28873         
28874         if(this.multiple){
28875             this.selectorEl.attr('multiple', 'multiple');
28876         }
28877         
28878         this.selectorEl.on('change', this.onFileSelected, this);
28879         
28880         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28881         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28882         
28883         this.uploader.on('click', this.onUploaderClick, this);
28884         
28885         this.renderProgressDialog();
28886         
28887         var _this = this;
28888         
28889         window.addEventListener("resize", function() { _this.refresh(); } );
28890         
28891         this.fireEvent('initial', this);
28892     },
28893     
28894     renderProgressDialog : function()
28895     {
28896         var _this = this;
28897         
28898         this.progressDialog = new Roo.bootstrap.Modal({
28899             cls : 'roo-document-manager-progress-dialog',
28900             allow_close : false,
28901             title : '',
28902             buttons : [
28903                 {
28904                     name  :'cancel',
28905                     weight : 'danger',
28906                     html : 'Cancel'
28907                 }
28908             ], 
28909             listeners : { 
28910                 btnclick : function() {
28911                     _this.uploadCancel();
28912                     this.hide();
28913                 }
28914             }
28915         });
28916          
28917         this.progressDialog.render(Roo.get(document.body));
28918          
28919         this.progress = new Roo.bootstrap.Progress({
28920             cls : 'roo-document-manager-progress',
28921             active : true,
28922             striped : true
28923         });
28924         
28925         this.progress.render(this.progressDialog.getChildContainer());
28926         
28927         this.progressBar = new Roo.bootstrap.ProgressBar({
28928             cls : 'roo-document-manager-progress-bar',
28929             aria_valuenow : 0,
28930             aria_valuemin : 0,
28931             aria_valuemax : 12,
28932             panel : 'success'
28933         });
28934         
28935         this.progressBar.render(this.progress.getChildContainer());
28936     },
28937     
28938     onUploaderClick : function(e)
28939     {
28940         e.preventDefault();
28941      
28942         if(this.fireEvent('beforeselectfile', this) != false){
28943             this.selectorEl.dom.click();
28944         }
28945         
28946     },
28947     
28948     onFileSelected : function(e)
28949     {
28950         e.preventDefault();
28951         
28952         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28953             return;
28954         }
28955         
28956         Roo.each(this.selectorEl.dom.files, function(file){
28957             if(this.fireEvent('inspect', this, file) != false){
28958                 this.files.push(file);
28959             }
28960         }, this);
28961         
28962         this.queue();
28963         
28964     },
28965     
28966     queue : function()
28967     {
28968         this.selectorEl.dom.value = '';
28969         
28970         if(!this.files || !this.files.length){
28971             return;
28972         }
28973         
28974         if(this.boxes > 0 && this.files.length > this.boxes){
28975             this.files = this.files.slice(0, this.boxes);
28976         }
28977         
28978         this.uploader.show();
28979         
28980         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28981             this.uploader.hide();
28982         }
28983         
28984         var _this = this;
28985         
28986         var files = [];
28987         
28988         var docs = [];
28989         
28990         Roo.each(this.files, function(file){
28991             
28992             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28993                 var f = this.renderPreview(file);
28994                 files.push(f);
28995                 return;
28996             }
28997             
28998             if(file.type.indexOf('image') != -1){
28999                 this.delegates.push(
29000                     (function(){
29001                         _this.process(file);
29002                     }).createDelegate(this)
29003                 );
29004         
29005                 return;
29006             }
29007             
29008             docs.push(
29009                 (function(){
29010                     _this.process(file);
29011                 }).createDelegate(this)
29012             );
29013             
29014         }, this);
29015         
29016         this.files = files;
29017         
29018         this.delegates = this.delegates.concat(docs);
29019         
29020         if(!this.delegates.length){
29021             this.refresh();
29022             return;
29023         }
29024         
29025         this.progressBar.aria_valuemax = this.delegates.length;
29026         
29027         this.arrange();
29028         
29029         return;
29030     },
29031     
29032     arrange : function()
29033     {
29034         if(!this.delegates.length){
29035             this.progressDialog.hide();
29036             this.refresh();
29037             return;
29038         }
29039         
29040         var delegate = this.delegates.shift();
29041         
29042         this.progressDialog.show();
29043         
29044         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29045         
29046         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29047         
29048         delegate();
29049     },
29050     
29051     refresh : function()
29052     {
29053         this.uploader.show();
29054         
29055         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29056             this.uploader.hide();
29057         }
29058         
29059         Roo.isTouch ? this.closable(false) : this.closable(true);
29060         
29061         this.fireEvent('refresh', this);
29062     },
29063     
29064     onRemove : function(e, el, o)
29065     {
29066         e.preventDefault();
29067         
29068         this.fireEvent('remove', this, o);
29069         
29070     },
29071     
29072     remove : function(o)
29073     {
29074         var files = [];
29075         
29076         Roo.each(this.files, function(file){
29077             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29078                 files.push(file);
29079                 return;
29080             }
29081
29082             o.target.remove();
29083
29084         }, this);
29085         
29086         this.files = files;
29087         
29088         this.refresh();
29089     },
29090     
29091     clear : function()
29092     {
29093         Roo.each(this.files, function(file){
29094             if(!file.target){
29095                 return;
29096             }
29097             
29098             file.target.remove();
29099
29100         }, this);
29101         
29102         this.files = [];
29103         
29104         this.refresh();
29105     },
29106     
29107     onClick : function(e, el, o)
29108     {
29109         e.preventDefault();
29110         
29111         this.fireEvent('click', this, o);
29112         
29113     },
29114     
29115     closable : function(closable)
29116     {
29117         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29118             
29119             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29120             
29121             if(closable){
29122                 el.show();
29123                 return;
29124             }
29125             
29126             el.hide();
29127             
29128         }, this);
29129     },
29130     
29131     xhrOnLoad : function(xhr)
29132     {
29133         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29134             el.remove();
29135         }, this);
29136         
29137         if (xhr.readyState !== 4) {
29138             this.arrange();
29139             this.fireEvent('exception', this, xhr);
29140             return;
29141         }
29142
29143         var response = Roo.decode(xhr.responseText);
29144         
29145         if(!response.success){
29146             this.arrange();
29147             this.fireEvent('exception', this, xhr);
29148             return;
29149         }
29150         
29151         var file = this.renderPreview(response.data);
29152         
29153         this.files.push(file);
29154         
29155         this.arrange();
29156         
29157         this.fireEvent('afterupload', this, xhr);
29158         
29159     },
29160     
29161     xhrOnError : function(xhr)
29162     {
29163         Roo.log('xhr on error');
29164         
29165         var response = Roo.decode(xhr.responseText);
29166           
29167         Roo.log(response);
29168         
29169         this.arrange();
29170     },
29171     
29172     process : function(file)
29173     {
29174         if(this.fireEvent('process', this, file) !== false){
29175             if(this.editable && file.type.indexOf('image') != -1){
29176                 this.fireEvent('edit', this, file);
29177                 return;
29178             }
29179
29180             this.uploadStart(file, false);
29181
29182             return;
29183         }
29184         
29185     },
29186     
29187     uploadStart : function(file, crop)
29188     {
29189         this.xhr = new XMLHttpRequest();
29190         
29191         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29192             this.arrange();
29193             return;
29194         }
29195         
29196         file.xhr = this.xhr;
29197             
29198         this.managerEl.createChild({
29199             tag : 'div',
29200             cls : 'roo-document-manager-loading',
29201             cn : [
29202                 {
29203                     tag : 'div',
29204                     tooltip : file.name,
29205                     cls : 'roo-document-manager-thumb',
29206                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29207                 }
29208             ]
29209
29210         });
29211
29212         this.xhr.open(this.method, this.url, true);
29213         
29214         var headers = {
29215             "Accept": "application/json",
29216             "Cache-Control": "no-cache",
29217             "X-Requested-With": "XMLHttpRequest"
29218         };
29219         
29220         for (var headerName in headers) {
29221             var headerValue = headers[headerName];
29222             if (headerValue) {
29223                 this.xhr.setRequestHeader(headerName, headerValue);
29224             }
29225         }
29226         
29227         var _this = this;
29228         
29229         this.xhr.onload = function()
29230         {
29231             _this.xhrOnLoad(_this.xhr);
29232         }
29233         
29234         this.xhr.onerror = function()
29235         {
29236             _this.xhrOnError(_this.xhr);
29237         }
29238         
29239         var formData = new FormData();
29240
29241         formData.append('returnHTML', 'NO');
29242         
29243         if(crop){
29244             formData.append('crop', crop);
29245         }
29246         
29247         formData.append(this.paramName, file, file.name);
29248         
29249         var options = {
29250             file : file, 
29251             manually : false
29252         };
29253         
29254         if(this.fireEvent('prepare', this, formData, options) != false){
29255             
29256             if(options.manually){
29257                 return;
29258             }
29259             
29260             this.xhr.send(formData);
29261             return;
29262         };
29263         
29264         this.uploadCancel();
29265     },
29266     
29267     uploadCancel : function()
29268     {
29269         if (this.xhr) {
29270             this.xhr.abort();
29271         }
29272         
29273         this.delegates = [];
29274         
29275         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29276             el.remove();
29277         }, this);
29278         
29279         this.arrange();
29280     },
29281     
29282     renderPreview : function(file)
29283     {
29284         if(typeof(file.target) != 'undefined' && file.target){
29285             return file;
29286         }
29287         
29288         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29289         
29290         var previewEl = this.managerEl.createChild({
29291             tag : 'div',
29292             cls : 'roo-document-manager-preview',
29293             cn : [
29294                 {
29295                     tag : 'div',
29296                     tooltip : file[this.toolTipName],
29297                     cls : 'roo-document-manager-thumb',
29298                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29299                 },
29300                 {
29301                     tag : 'button',
29302                     cls : 'close',
29303                     html : '<i class="fa fa-times-circle"></i>'
29304                 }
29305             ]
29306         });
29307
29308         var close = previewEl.select('button.close', true).first();
29309
29310         close.on('click', this.onRemove, this, file);
29311
29312         file.target = previewEl;
29313
29314         var image = previewEl.select('img', true).first();
29315         
29316         var _this = this;
29317         
29318         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29319         
29320         image.on('click', this.onClick, this, file);
29321         
29322         this.fireEvent('previewrendered', this, file);
29323         
29324         return file;
29325         
29326     },
29327     
29328     onPreviewLoad : function(file, image)
29329     {
29330         if(typeof(file.target) == 'undefined' || !file.target){
29331             return;
29332         }
29333         
29334         var width = image.dom.naturalWidth || image.dom.width;
29335         var height = image.dom.naturalHeight || image.dom.height;
29336         
29337         if(!this.previewResize) {
29338             return;
29339         }
29340         
29341         if(width > height){
29342             file.target.addClass('wide');
29343             return;
29344         }
29345         
29346         file.target.addClass('tall');
29347         return;
29348         
29349     },
29350     
29351     uploadFromSource : function(file, crop)
29352     {
29353         this.xhr = new XMLHttpRequest();
29354         
29355         this.managerEl.createChild({
29356             tag : 'div',
29357             cls : 'roo-document-manager-loading',
29358             cn : [
29359                 {
29360                     tag : 'div',
29361                     tooltip : file.name,
29362                     cls : 'roo-document-manager-thumb',
29363                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29364                 }
29365             ]
29366
29367         });
29368
29369         this.xhr.open(this.method, this.url, true);
29370         
29371         var headers = {
29372             "Accept": "application/json",
29373             "Cache-Control": "no-cache",
29374             "X-Requested-With": "XMLHttpRequest"
29375         };
29376         
29377         for (var headerName in headers) {
29378             var headerValue = headers[headerName];
29379             if (headerValue) {
29380                 this.xhr.setRequestHeader(headerName, headerValue);
29381             }
29382         }
29383         
29384         var _this = this;
29385         
29386         this.xhr.onload = function()
29387         {
29388             _this.xhrOnLoad(_this.xhr);
29389         }
29390         
29391         this.xhr.onerror = function()
29392         {
29393             _this.xhrOnError(_this.xhr);
29394         }
29395         
29396         var formData = new FormData();
29397
29398         formData.append('returnHTML', 'NO');
29399         
29400         formData.append('crop', crop);
29401         
29402         if(typeof(file.filename) != 'undefined'){
29403             formData.append('filename', file.filename);
29404         }
29405         
29406         if(typeof(file.mimetype) != 'undefined'){
29407             formData.append('mimetype', file.mimetype);
29408         }
29409         
29410         Roo.log(formData);
29411         
29412         if(this.fireEvent('prepare', this, formData) != false){
29413             this.xhr.send(formData);
29414         };
29415     }
29416 });
29417
29418 /*
29419 * Licence: LGPL
29420 */
29421
29422 /**
29423  * @class Roo.bootstrap.DocumentViewer
29424  * @extends Roo.bootstrap.Component
29425  * Bootstrap DocumentViewer class
29426  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29427  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29428  * 
29429  * @constructor
29430  * Create a new DocumentViewer
29431  * @param {Object} config The config object
29432  */
29433
29434 Roo.bootstrap.DocumentViewer = function(config){
29435     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29436     
29437     this.addEvents({
29438         /**
29439          * @event initial
29440          * Fire after initEvent
29441          * @param {Roo.bootstrap.DocumentViewer} this
29442          */
29443         "initial" : true,
29444         /**
29445          * @event click
29446          * Fire after click
29447          * @param {Roo.bootstrap.DocumentViewer} this
29448          */
29449         "click" : true,
29450         /**
29451          * @event download
29452          * Fire after download button
29453          * @param {Roo.bootstrap.DocumentViewer} this
29454          */
29455         "download" : true,
29456         /**
29457          * @event trash
29458          * Fire after trash button
29459          * @param {Roo.bootstrap.DocumentViewer} this
29460          */
29461         "trash" : true
29462         
29463     });
29464 };
29465
29466 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29467     
29468     showDownload : true,
29469     
29470     showTrash : true,
29471     
29472     getAutoCreate : function()
29473     {
29474         var cfg = {
29475             tag : 'div',
29476             cls : 'roo-document-viewer',
29477             cn : [
29478                 {
29479                     tag : 'div',
29480                     cls : 'roo-document-viewer-body',
29481                     cn : [
29482                         {
29483                             tag : 'div',
29484                             cls : 'roo-document-viewer-thumb',
29485                             cn : [
29486                                 {
29487                                     tag : 'img',
29488                                     cls : 'roo-document-viewer-image'
29489                                 }
29490                             ]
29491                         }
29492                     ]
29493                 },
29494                 {
29495                     tag : 'div',
29496                     cls : 'roo-document-viewer-footer',
29497                     cn : {
29498                         tag : 'div',
29499                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29500                         cn : [
29501                             {
29502                                 tag : 'div',
29503                                 cls : 'btn-group roo-document-viewer-download',
29504                                 cn : [
29505                                     {
29506                                         tag : 'button',
29507                                         cls : 'btn btn-default',
29508                                         html : '<i class="fa fa-download"></i>'
29509                                     }
29510                                 ]
29511                             },
29512                             {
29513                                 tag : 'div',
29514                                 cls : 'btn-group roo-document-viewer-trash',
29515                                 cn : [
29516                                     {
29517                                         tag : 'button',
29518                                         cls : 'btn btn-default',
29519                                         html : '<i class="fa fa-trash"></i>'
29520                                     }
29521                                 ]
29522                             }
29523                         ]
29524                     }
29525                 }
29526             ]
29527         };
29528         
29529         return cfg;
29530     },
29531     
29532     initEvents : function()
29533     {
29534         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29535         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29536         
29537         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29538         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29539         
29540         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29541         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29542         
29543         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29544         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29545         
29546         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29547         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29548         
29549         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29550         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29551         
29552         this.bodyEl.on('click', this.onClick, this);
29553         this.downloadBtn.on('click', this.onDownload, this);
29554         this.trashBtn.on('click', this.onTrash, this);
29555         
29556         this.downloadBtn.hide();
29557         this.trashBtn.hide();
29558         
29559         if(this.showDownload){
29560             this.downloadBtn.show();
29561         }
29562         
29563         if(this.showTrash){
29564             this.trashBtn.show();
29565         }
29566         
29567         if(!this.showDownload && !this.showTrash) {
29568             this.footerEl.hide();
29569         }
29570         
29571     },
29572     
29573     initial : function()
29574     {
29575         this.fireEvent('initial', this);
29576         
29577     },
29578     
29579     onClick : function(e)
29580     {
29581         e.preventDefault();
29582         
29583         this.fireEvent('click', this);
29584     },
29585     
29586     onDownload : function(e)
29587     {
29588         e.preventDefault();
29589         
29590         this.fireEvent('download', this);
29591     },
29592     
29593     onTrash : function(e)
29594     {
29595         e.preventDefault();
29596         
29597         this.fireEvent('trash', this);
29598     }
29599     
29600 });
29601 /*
29602  * - LGPL
29603  *
29604  * nav progress bar
29605  * 
29606  */
29607
29608 /**
29609  * @class Roo.bootstrap.NavProgressBar
29610  * @extends Roo.bootstrap.Component
29611  * Bootstrap NavProgressBar class
29612  * 
29613  * @constructor
29614  * Create a new nav progress bar
29615  * @param {Object} config The config object
29616  */
29617
29618 Roo.bootstrap.NavProgressBar = function(config){
29619     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29620
29621     this.bullets = this.bullets || [];
29622    
29623 //    Roo.bootstrap.NavProgressBar.register(this);
29624      this.addEvents({
29625         /**
29626              * @event changed
29627              * Fires when the active item changes
29628              * @param {Roo.bootstrap.NavProgressBar} this
29629              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29630              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29631          */
29632         'changed': true
29633      });
29634     
29635 };
29636
29637 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29638     
29639     bullets : [],
29640     barItems : [],
29641     
29642     getAutoCreate : function()
29643     {
29644         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29645         
29646         cfg = {
29647             tag : 'div',
29648             cls : 'roo-navigation-bar-group',
29649             cn : [
29650                 {
29651                     tag : 'div',
29652                     cls : 'roo-navigation-top-bar'
29653                 },
29654                 {
29655                     tag : 'div',
29656                     cls : 'roo-navigation-bullets-bar',
29657                     cn : [
29658                         {
29659                             tag : 'ul',
29660                             cls : 'roo-navigation-bar'
29661                         }
29662                     ]
29663                 },
29664                 
29665                 {
29666                     tag : 'div',
29667                     cls : 'roo-navigation-bottom-bar'
29668                 }
29669             ]
29670             
29671         };
29672         
29673         return cfg;
29674         
29675     },
29676     
29677     initEvents: function() 
29678     {
29679         
29680     },
29681     
29682     onRender : function(ct, position) 
29683     {
29684         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29685         
29686         if(this.bullets.length){
29687             Roo.each(this.bullets, function(b){
29688                this.addItem(b);
29689             }, this);
29690         }
29691         
29692         this.format();
29693         
29694     },
29695     
29696     addItem : function(cfg)
29697     {
29698         var item = new Roo.bootstrap.NavProgressItem(cfg);
29699         
29700         item.parentId = this.id;
29701         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29702         
29703         if(cfg.html){
29704             var top = new Roo.bootstrap.Element({
29705                 tag : 'div',
29706                 cls : 'roo-navigation-bar-text'
29707             });
29708             
29709             var bottom = new Roo.bootstrap.Element({
29710                 tag : 'div',
29711                 cls : 'roo-navigation-bar-text'
29712             });
29713             
29714             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29715             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29716             
29717             var topText = new Roo.bootstrap.Element({
29718                 tag : 'span',
29719                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29720             });
29721             
29722             var bottomText = new Roo.bootstrap.Element({
29723                 tag : 'span',
29724                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29725             });
29726             
29727             topText.onRender(top.el, null);
29728             bottomText.onRender(bottom.el, null);
29729             
29730             item.topEl = top;
29731             item.bottomEl = bottom;
29732         }
29733         
29734         this.barItems.push(item);
29735         
29736         return item;
29737     },
29738     
29739     getActive : function()
29740     {
29741         var active = false;
29742         
29743         Roo.each(this.barItems, function(v){
29744             
29745             if (!v.isActive()) {
29746                 return;
29747             }
29748             
29749             active = v;
29750             return false;
29751             
29752         });
29753         
29754         return active;
29755     },
29756     
29757     setActiveItem : function(item)
29758     {
29759         var prev = false;
29760         
29761         Roo.each(this.barItems, function(v){
29762             if (v.rid == item.rid) {
29763                 return ;
29764             }
29765             
29766             if (v.isActive()) {
29767                 v.setActive(false);
29768                 prev = v;
29769             }
29770         });
29771
29772         item.setActive(true);
29773         
29774         this.fireEvent('changed', this, item, prev);
29775     },
29776     
29777     getBarItem: function(rid)
29778     {
29779         var ret = false;
29780         
29781         Roo.each(this.barItems, function(e) {
29782             if (e.rid != rid) {
29783                 return;
29784             }
29785             
29786             ret =  e;
29787             return false;
29788         });
29789         
29790         return ret;
29791     },
29792     
29793     indexOfItem : function(item)
29794     {
29795         var index = false;
29796         
29797         Roo.each(this.barItems, function(v, i){
29798             
29799             if (v.rid != item.rid) {
29800                 return;
29801             }
29802             
29803             index = i;
29804             return false
29805         });
29806         
29807         return index;
29808     },
29809     
29810     setActiveNext : function()
29811     {
29812         var i = this.indexOfItem(this.getActive());
29813         
29814         if (i > this.barItems.length) {
29815             return;
29816         }
29817         
29818         this.setActiveItem(this.barItems[i+1]);
29819     },
29820     
29821     setActivePrev : function()
29822     {
29823         var i = this.indexOfItem(this.getActive());
29824         
29825         if (i  < 1) {
29826             return;
29827         }
29828         
29829         this.setActiveItem(this.barItems[i-1]);
29830     },
29831     
29832     format : function()
29833     {
29834         if(!this.barItems.length){
29835             return;
29836         }
29837      
29838         var width = 100 / this.barItems.length;
29839         
29840         Roo.each(this.barItems, function(i){
29841             i.el.setStyle('width', width + '%');
29842             i.topEl.el.setStyle('width', width + '%');
29843             i.bottomEl.el.setStyle('width', width + '%');
29844         }, this);
29845         
29846     }
29847     
29848 });
29849 /*
29850  * - LGPL
29851  *
29852  * Nav Progress Item
29853  * 
29854  */
29855
29856 /**
29857  * @class Roo.bootstrap.NavProgressItem
29858  * @extends Roo.bootstrap.Component
29859  * Bootstrap NavProgressItem class
29860  * @cfg {String} rid the reference id
29861  * @cfg {Boolean} active (true|false) Is item active default false
29862  * @cfg {Boolean} disabled (true|false) Is item active default false
29863  * @cfg {String} html
29864  * @cfg {String} position (top|bottom) text position default bottom
29865  * @cfg {String} icon show icon instead of number
29866  * 
29867  * @constructor
29868  * Create a new NavProgressItem
29869  * @param {Object} config The config object
29870  */
29871 Roo.bootstrap.NavProgressItem = function(config){
29872     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29873     this.addEvents({
29874         // raw events
29875         /**
29876          * @event click
29877          * The raw click event for the entire grid.
29878          * @param {Roo.bootstrap.NavProgressItem} this
29879          * @param {Roo.EventObject} e
29880          */
29881         "click" : true
29882     });
29883    
29884 };
29885
29886 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29887     
29888     rid : '',
29889     active : false,
29890     disabled : false,
29891     html : '',
29892     position : 'bottom',
29893     icon : false,
29894     
29895     getAutoCreate : function()
29896     {
29897         var iconCls = 'roo-navigation-bar-item-icon';
29898         
29899         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29900         
29901         var cfg = {
29902             tag: 'li',
29903             cls: 'roo-navigation-bar-item',
29904             cn : [
29905                 {
29906                     tag : 'i',
29907                     cls : iconCls
29908                 }
29909             ]
29910         };
29911         
29912         if(this.active){
29913             cfg.cls += ' active';
29914         }
29915         if(this.disabled){
29916             cfg.cls += ' disabled';
29917         }
29918         
29919         return cfg;
29920     },
29921     
29922     disable : function()
29923     {
29924         this.setDisabled(true);
29925     },
29926     
29927     enable : function()
29928     {
29929         this.setDisabled(false);
29930     },
29931     
29932     initEvents: function() 
29933     {
29934         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29935         
29936         this.iconEl.on('click', this.onClick, this);
29937     },
29938     
29939     onClick : function(e)
29940     {
29941         e.preventDefault();
29942         
29943         if(this.disabled){
29944             return;
29945         }
29946         
29947         if(this.fireEvent('click', this, e) === false){
29948             return;
29949         };
29950         
29951         this.parent().setActiveItem(this);
29952     },
29953     
29954     isActive: function () 
29955     {
29956         return this.active;
29957     },
29958     
29959     setActive : function(state)
29960     {
29961         if(this.active == state){
29962             return;
29963         }
29964         
29965         this.active = state;
29966         
29967         if (state) {
29968             this.el.addClass('active');
29969             return;
29970         }
29971         
29972         this.el.removeClass('active');
29973         
29974         return;
29975     },
29976     
29977     setDisabled : function(state)
29978     {
29979         if(this.disabled == state){
29980             return;
29981         }
29982         
29983         this.disabled = state;
29984         
29985         if (state) {
29986             this.el.addClass('disabled');
29987             return;
29988         }
29989         
29990         this.el.removeClass('disabled');
29991     },
29992     
29993     tooltipEl : function()
29994     {
29995         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29996     }
29997 });
29998  
29999
30000  /*
30001  * - LGPL
30002  *
30003  * FieldLabel
30004  * 
30005  */
30006
30007 /**
30008  * @class Roo.bootstrap.FieldLabel
30009  * @extends Roo.bootstrap.Component
30010  * Bootstrap FieldLabel class
30011  * @cfg {String} html contents of the element
30012  * @cfg {String} tag tag of the element default label
30013  * @cfg {String} cls class of the element
30014  * @cfg {String} target label target 
30015  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30016  * @cfg {String} invalidClass default "text-warning"
30017  * @cfg {String} validClass default "text-success"
30018  * @cfg {String} iconTooltip default "This field is required"
30019  * @cfg {String} indicatorpos (left|right) default left
30020  * 
30021  * @constructor
30022  * Create a new FieldLabel
30023  * @param {Object} config The config object
30024  */
30025
30026 Roo.bootstrap.FieldLabel = function(config){
30027     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30028     
30029     this.addEvents({
30030             /**
30031              * @event invalid
30032              * Fires after the field has been marked as invalid.
30033              * @param {Roo.form.FieldLabel} this
30034              * @param {String} msg The validation message
30035              */
30036             invalid : true,
30037             /**
30038              * @event valid
30039              * Fires after the field has been validated with no errors.
30040              * @param {Roo.form.FieldLabel} this
30041              */
30042             valid : true
30043         });
30044 };
30045
30046 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30047     
30048     tag: 'label',
30049     cls: '',
30050     html: '',
30051     target: '',
30052     allowBlank : true,
30053     invalidClass : 'has-warning',
30054     validClass : 'has-success',
30055     iconTooltip : 'This field is required',
30056     indicatorpos : 'left',
30057     
30058     getAutoCreate : function(){
30059         
30060         var cls = "";
30061         if (!this.allowBlank) {
30062             cls  = "visible";
30063         }
30064         
30065         var cfg = {
30066             tag : this.tag,
30067             cls : 'roo-bootstrap-field-label ' + this.cls,
30068             for : this.target,
30069             cn : [
30070                 {
30071                     tag : 'i',
30072                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30073                     tooltip : this.iconTooltip
30074                 },
30075                 {
30076                     tag : 'span',
30077                     html : this.html
30078                 }
30079             ] 
30080         };
30081         
30082         if(this.indicatorpos == 'right'){
30083             var cfg = {
30084                 tag : this.tag,
30085                 cls : 'roo-bootstrap-field-label ' + this.cls,
30086                 for : this.target,
30087                 cn : [
30088                     {
30089                         tag : 'span',
30090                         html : this.html
30091                     },
30092                     {
30093                         tag : 'i',
30094                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30095                         tooltip : this.iconTooltip
30096                     }
30097                 ] 
30098             };
30099         }
30100         
30101         return cfg;
30102     },
30103     
30104     initEvents: function() 
30105     {
30106         Roo.bootstrap.Element.superclass.initEvents.call(this);
30107         
30108         this.indicator = this.indicatorEl();
30109         
30110         if(this.indicator){
30111             this.indicator.removeClass('visible');
30112             this.indicator.addClass('invisible');
30113         }
30114         
30115         Roo.bootstrap.FieldLabel.register(this);
30116     },
30117     
30118     indicatorEl : function()
30119     {
30120         var indicator = this.el.select('i.roo-required-indicator',true).first();
30121         
30122         if(!indicator){
30123             return false;
30124         }
30125         
30126         return indicator;
30127         
30128     },
30129     
30130     /**
30131      * Mark this field as valid
30132      */
30133     markValid : function()
30134     {
30135         if(this.indicator){
30136             this.indicator.removeClass('visible');
30137             this.indicator.addClass('invisible');
30138         }
30139         
30140         this.el.removeClass(this.invalidClass);
30141         
30142         this.el.addClass(this.validClass);
30143         
30144         this.fireEvent('valid', this);
30145     },
30146     
30147     /**
30148      * Mark this field as invalid
30149      * @param {String} msg The validation message
30150      */
30151     markInvalid : function(msg)
30152     {
30153         if(this.indicator){
30154             this.indicator.removeClass('invisible');
30155             this.indicator.addClass('visible');
30156         }
30157         
30158         this.el.removeClass(this.validClass);
30159         
30160         this.el.addClass(this.invalidClass);
30161         
30162         this.fireEvent('invalid', this, msg);
30163     }
30164     
30165    
30166 });
30167
30168 Roo.apply(Roo.bootstrap.FieldLabel, {
30169     
30170     groups: {},
30171     
30172      /**
30173     * register a FieldLabel Group
30174     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30175     */
30176     register : function(label)
30177     {
30178         if(this.groups.hasOwnProperty(label.target)){
30179             return;
30180         }
30181      
30182         this.groups[label.target] = label;
30183         
30184     },
30185     /**
30186     * fetch a FieldLabel Group based on the target
30187     * @param {string} target
30188     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30189     */
30190     get: function(target) {
30191         if (typeof(this.groups[target]) == 'undefined') {
30192             return false;
30193         }
30194         
30195         return this.groups[target] ;
30196     }
30197 });
30198
30199  
30200
30201  /*
30202  * - LGPL
30203  *
30204  * page DateSplitField.
30205  * 
30206  */
30207
30208
30209 /**
30210  * @class Roo.bootstrap.DateSplitField
30211  * @extends Roo.bootstrap.Component
30212  * Bootstrap DateSplitField class
30213  * @cfg {string} fieldLabel - the label associated
30214  * @cfg {Number} labelWidth set the width of label (0-12)
30215  * @cfg {String} labelAlign (top|left)
30216  * @cfg {Boolean} dayAllowBlank (true|false) default false
30217  * @cfg {Boolean} monthAllowBlank (true|false) default false
30218  * @cfg {Boolean} yearAllowBlank (true|false) default false
30219  * @cfg {string} dayPlaceholder 
30220  * @cfg {string} monthPlaceholder
30221  * @cfg {string} yearPlaceholder
30222  * @cfg {string} dayFormat default 'd'
30223  * @cfg {string} monthFormat default 'm'
30224  * @cfg {string} yearFormat default 'Y'
30225  * @cfg {Number} labellg set the width of label (1-12)
30226  * @cfg {Number} labelmd set the width of label (1-12)
30227  * @cfg {Number} labelsm set the width of label (1-12)
30228  * @cfg {Number} labelxs set the width of label (1-12)
30229
30230  *     
30231  * @constructor
30232  * Create a new DateSplitField
30233  * @param {Object} config The config object
30234  */
30235
30236 Roo.bootstrap.DateSplitField = function(config){
30237     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30238     
30239     this.addEvents({
30240         // raw events
30241          /**
30242          * @event years
30243          * getting the data of years
30244          * @param {Roo.bootstrap.DateSplitField} this
30245          * @param {Object} years
30246          */
30247         "years" : true,
30248         /**
30249          * @event days
30250          * getting the data of days
30251          * @param {Roo.bootstrap.DateSplitField} this
30252          * @param {Object} days
30253          */
30254         "days" : true,
30255         /**
30256          * @event invalid
30257          * Fires after the field has been marked as invalid.
30258          * @param {Roo.form.Field} this
30259          * @param {String} msg The validation message
30260          */
30261         invalid : true,
30262        /**
30263          * @event valid
30264          * Fires after the field has been validated with no errors.
30265          * @param {Roo.form.Field} this
30266          */
30267         valid : true
30268     });
30269 };
30270
30271 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30272     
30273     fieldLabel : '',
30274     labelAlign : 'top',
30275     labelWidth : 3,
30276     dayAllowBlank : false,
30277     monthAllowBlank : false,
30278     yearAllowBlank : false,
30279     dayPlaceholder : '',
30280     monthPlaceholder : '',
30281     yearPlaceholder : '',
30282     dayFormat : 'd',
30283     monthFormat : 'm',
30284     yearFormat : 'Y',
30285     isFormField : true,
30286     labellg : 0,
30287     labelmd : 0,
30288     labelsm : 0,
30289     labelxs : 0,
30290     
30291     getAutoCreate : function()
30292     {
30293         var cfg = {
30294             tag : 'div',
30295             cls : 'row roo-date-split-field-group',
30296             cn : [
30297                 {
30298                     tag : 'input',
30299                     type : 'hidden',
30300                     cls : 'form-hidden-field roo-date-split-field-group-value',
30301                     name : this.name
30302                 }
30303             ]
30304         };
30305         
30306         var labelCls = 'col-md-12';
30307         var contentCls = 'col-md-4';
30308         
30309         if(this.fieldLabel){
30310             
30311             var label = {
30312                 tag : 'div',
30313                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30314                 cn : [
30315                     {
30316                         tag : 'label',
30317                         html : this.fieldLabel
30318                     }
30319                 ]
30320             };
30321             
30322             if(this.labelAlign == 'left'){
30323             
30324                 if(this.labelWidth > 12){
30325                     label.style = "width: " + this.labelWidth + 'px';
30326                 }
30327
30328                 if(this.labelWidth < 13 && this.labelmd == 0){
30329                     this.labelmd = this.labelWidth;
30330                 }
30331
30332                 if(this.labellg > 0){
30333                     labelCls = ' col-lg-' + this.labellg;
30334                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30335                 }
30336
30337                 if(this.labelmd > 0){
30338                     labelCls = ' col-md-' + this.labelmd;
30339                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30340                 }
30341
30342                 if(this.labelsm > 0){
30343                     labelCls = ' col-sm-' + this.labelsm;
30344                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30345                 }
30346
30347                 if(this.labelxs > 0){
30348                     labelCls = ' col-xs-' + this.labelxs;
30349                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30350                 }
30351             }
30352             
30353             label.cls += ' ' + labelCls;
30354             
30355             cfg.cn.push(label);
30356         }
30357         
30358         Roo.each(['day', 'month', 'year'], function(t){
30359             cfg.cn.push({
30360                 tag : 'div',
30361                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30362             });
30363         }, this);
30364         
30365         return cfg;
30366     },
30367     
30368     inputEl: function ()
30369     {
30370         return this.el.select('.roo-date-split-field-group-value', true).first();
30371     },
30372     
30373     onRender : function(ct, position) 
30374     {
30375         var _this = this;
30376         
30377         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30378         
30379         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30380         
30381         this.dayField = new Roo.bootstrap.ComboBox({
30382             allowBlank : this.dayAllowBlank,
30383             alwaysQuery : true,
30384             displayField : 'value',
30385             editable : false,
30386             fieldLabel : '',
30387             forceSelection : true,
30388             mode : 'local',
30389             placeholder : this.dayPlaceholder,
30390             selectOnFocus : true,
30391             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30392             triggerAction : 'all',
30393             typeAhead : true,
30394             valueField : 'value',
30395             store : new Roo.data.SimpleStore({
30396                 data : (function() {    
30397                     var days = [];
30398                     _this.fireEvent('days', _this, days);
30399                     return days;
30400                 })(),
30401                 fields : [ 'value' ]
30402             }),
30403             listeners : {
30404                 select : function (_self, record, index)
30405                 {
30406                     _this.setValue(_this.getValue());
30407                 }
30408             }
30409         });
30410
30411         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30412         
30413         this.monthField = new Roo.bootstrap.MonthField({
30414             after : '<i class=\"fa fa-calendar\"></i>',
30415             allowBlank : this.monthAllowBlank,
30416             placeholder : this.monthPlaceholder,
30417             readOnly : true,
30418             listeners : {
30419                 render : function (_self)
30420                 {
30421                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30422                         e.preventDefault();
30423                         _self.focus();
30424                     });
30425                 },
30426                 select : function (_self, oldvalue, newvalue)
30427                 {
30428                     _this.setValue(_this.getValue());
30429                 }
30430             }
30431         });
30432         
30433         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30434         
30435         this.yearField = new Roo.bootstrap.ComboBox({
30436             allowBlank : this.yearAllowBlank,
30437             alwaysQuery : true,
30438             displayField : 'value',
30439             editable : false,
30440             fieldLabel : '',
30441             forceSelection : true,
30442             mode : 'local',
30443             placeholder : this.yearPlaceholder,
30444             selectOnFocus : true,
30445             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30446             triggerAction : 'all',
30447             typeAhead : true,
30448             valueField : 'value',
30449             store : new Roo.data.SimpleStore({
30450                 data : (function() {
30451                     var years = [];
30452                     _this.fireEvent('years', _this, years);
30453                     return years;
30454                 })(),
30455                 fields : [ 'value' ]
30456             }),
30457             listeners : {
30458                 select : function (_self, record, index)
30459                 {
30460                     _this.setValue(_this.getValue());
30461                 }
30462             }
30463         });
30464
30465         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30466     },
30467     
30468     setValue : function(v, format)
30469     {
30470         this.inputEl.dom.value = v;
30471         
30472         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30473         
30474         var d = Date.parseDate(v, f);
30475         
30476         if(!d){
30477             this.validate();
30478             return;
30479         }
30480         
30481         this.setDay(d.format(this.dayFormat));
30482         this.setMonth(d.format(this.monthFormat));
30483         this.setYear(d.format(this.yearFormat));
30484         
30485         this.validate();
30486         
30487         return;
30488     },
30489     
30490     setDay : function(v)
30491     {
30492         this.dayField.setValue(v);
30493         this.inputEl.dom.value = this.getValue();
30494         this.validate();
30495         return;
30496     },
30497     
30498     setMonth : function(v)
30499     {
30500         this.monthField.setValue(v, true);
30501         this.inputEl.dom.value = this.getValue();
30502         this.validate();
30503         return;
30504     },
30505     
30506     setYear : function(v)
30507     {
30508         this.yearField.setValue(v);
30509         this.inputEl.dom.value = this.getValue();
30510         this.validate();
30511         return;
30512     },
30513     
30514     getDay : function()
30515     {
30516         return this.dayField.getValue();
30517     },
30518     
30519     getMonth : function()
30520     {
30521         return this.monthField.getValue();
30522     },
30523     
30524     getYear : function()
30525     {
30526         return this.yearField.getValue();
30527     },
30528     
30529     getValue : function()
30530     {
30531         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30532         
30533         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30534         
30535         return date;
30536     },
30537     
30538     reset : function()
30539     {
30540         this.setDay('');
30541         this.setMonth('');
30542         this.setYear('');
30543         this.inputEl.dom.value = '';
30544         this.validate();
30545         return;
30546     },
30547     
30548     validate : function()
30549     {
30550         var d = this.dayField.validate();
30551         var m = this.monthField.validate();
30552         var y = this.yearField.validate();
30553         
30554         var valid = true;
30555         
30556         if(
30557                 (!this.dayAllowBlank && !d) ||
30558                 (!this.monthAllowBlank && !m) ||
30559                 (!this.yearAllowBlank && !y)
30560         ){
30561             valid = false;
30562         }
30563         
30564         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30565             return valid;
30566         }
30567         
30568         if(valid){
30569             this.markValid();
30570             return valid;
30571         }
30572         
30573         this.markInvalid();
30574         
30575         return valid;
30576     },
30577     
30578     markValid : function()
30579     {
30580         
30581         var label = this.el.select('label', true).first();
30582         var icon = this.el.select('i.fa-star', true).first();
30583
30584         if(label && icon){
30585             icon.remove();
30586         }
30587         
30588         this.fireEvent('valid', this);
30589     },
30590     
30591      /**
30592      * Mark this field as invalid
30593      * @param {String} msg The validation message
30594      */
30595     markInvalid : function(msg)
30596     {
30597         
30598         var label = this.el.select('label', true).first();
30599         var icon = this.el.select('i.fa-star', true).first();
30600
30601         if(label && !icon){
30602             this.el.select('.roo-date-split-field-label', true).createChild({
30603                 tag : 'i',
30604                 cls : 'text-danger fa fa-lg fa-star',
30605                 tooltip : 'This field is required',
30606                 style : 'margin-right:5px;'
30607             }, label, true);
30608         }
30609         
30610         this.fireEvent('invalid', this, msg);
30611     },
30612     
30613     clearInvalid : function()
30614     {
30615         var label = this.el.select('label', true).first();
30616         var icon = this.el.select('i.fa-star', true).first();
30617
30618         if(label && icon){
30619             icon.remove();
30620         }
30621         
30622         this.fireEvent('valid', this);
30623     },
30624     
30625     getName: function()
30626     {
30627         return this.name;
30628     }
30629     
30630 });
30631
30632  /**
30633  *
30634  * This is based on 
30635  * http://masonry.desandro.com
30636  *
30637  * The idea is to render all the bricks based on vertical width...
30638  *
30639  * The original code extends 'outlayer' - we might need to use that....
30640  * 
30641  */
30642
30643
30644 /**
30645  * @class Roo.bootstrap.LayoutMasonry
30646  * @extends Roo.bootstrap.Component
30647  * Bootstrap Layout Masonry class
30648  * 
30649  * @constructor
30650  * Create a new Element
30651  * @param {Object} config The config object
30652  */
30653
30654 Roo.bootstrap.LayoutMasonry = function(config){
30655     
30656     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30657     
30658     this.bricks = [];
30659     
30660     Roo.bootstrap.LayoutMasonry.register(this);
30661     
30662     this.addEvents({
30663         // raw events
30664         /**
30665          * @event layout
30666          * Fire after layout the items
30667          * @param {Roo.bootstrap.LayoutMasonry} this
30668          * @param {Roo.EventObject} e
30669          */
30670         "layout" : true
30671     });
30672     
30673 };
30674
30675 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30676     
30677     /**
30678      * @cfg {Boolean} isLayoutInstant = no animation?
30679      */   
30680     isLayoutInstant : false, // needed?
30681    
30682     /**
30683      * @cfg {Number} boxWidth  width of the columns
30684      */   
30685     boxWidth : 450,
30686     
30687       /**
30688      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30689      */   
30690     boxHeight : 0,
30691     
30692     /**
30693      * @cfg {Number} padWidth padding below box..
30694      */   
30695     padWidth : 10, 
30696     
30697     /**
30698      * @cfg {Number} gutter gutter width..
30699      */   
30700     gutter : 10,
30701     
30702      /**
30703      * @cfg {Number} maxCols maximum number of columns
30704      */   
30705     
30706     maxCols: 0,
30707     
30708     /**
30709      * @cfg {Boolean} isAutoInitial defalut true
30710      */   
30711     isAutoInitial : true, 
30712     
30713     containerWidth: 0,
30714     
30715     /**
30716      * @cfg {Boolean} isHorizontal defalut false
30717      */   
30718     isHorizontal : false, 
30719
30720     currentSize : null,
30721     
30722     tag: 'div',
30723     
30724     cls: '',
30725     
30726     bricks: null, //CompositeElement
30727     
30728     cols : 1,
30729     
30730     _isLayoutInited : false,
30731     
30732 //    isAlternative : false, // only use for vertical layout...
30733     
30734     /**
30735      * @cfg {Number} alternativePadWidth padding below box..
30736      */   
30737     alternativePadWidth : 50,
30738     
30739     selectedBrick : [],
30740     
30741     getAutoCreate : function(){
30742         
30743         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30744         
30745         var cfg = {
30746             tag: this.tag,
30747             cls: 'blog-masonary-wrapper ' + this.cls,
30748             cn : {
30749                 cls : 'mas-boxes masonary'
30750             }
30751         };
30752         
30753         return cfg;
30754     },
30755     
30756     getChildContainer: function( )
30757     {
30758         if (this.boxesEl) {
30759             return this.boxesEl;
30760         }
30761         
30762         this.boxesEl = this.el.select('.mas-boxes').first();
30763         
30764         return this.boxesEl;
30765     },
30766     
30767     
30768     initEvents : function()
30769     {
30770         var _this = this;
30771         
30772         if(this.isAutoInitial){
30773             Roo.log('hook children rendered');
30774             this.on('childrenrendered', function() {
30775                 Roo.log('children rendered');
30776                 _this.initial();
30777             } ,this);
30778         }
30779     },
30780     
30781     initial : function()
30782     {
30783         this.selectedBrick = [];
30784         
30785         this.currentSize = this.el.getBox(true);
30786         
30787         Roo.EventManager.onWindowResize(this.resize, this); 
30788
30789         if(!this.isAutoInitial){
30790             this.layout();
30791             return;
30792         }
30793         
30794         this.layout();
30795         
30796         return;
30797         //this.layout.defer(500,this);
30798         
30799     },
30800     
30801     resize : function()
30802     {
30803         var cs = this.el.getBox(true);
30804         
30805         if (
30806                 this.currentSize.width == cs.width && 
30807                 this.currentSize.x == cs.x && 
30808                 this.currentSize.height == cs.height && 
30809                 this.currentSize.y == cs.y 
30810         ) {
30811             Roo.log("no change in with or X or Y");
30812             return;
30813         }
30814         
30815         this.currentSize = cs;
30816         
30817         this.layout();
30818         
30819     },
30820     
30821     layout : function()
30822     {   
30823         this._resetLayout();
30824         
30825         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30826         
30827         this.layoutItems( isInstant );
30828       
30829         this._isLayoutInited = true;
30830         
30831         this.fireEvent('layout', this);
30832         
30833     },
30834     
30835     _resetLayout : function()
30836     {
30837         if(this.isHorizontal){
30838             this.horizontalMeasureColumns();
30839             return;
30840         }
30841         
30842         this.verticalMeasureColumns();
30843         
30844     },
30845     
30846     verticalMeasureColumns : function()
30847     {
30848         this.getContainerWidth();
30849         
30850 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30851 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30852 //            return;
30853 //        }
30854         
30855         var boxWidth = this.boxWidth + this.padWidth;
30856         
30857         if(this.containerWidth < this.boxWidth){
30858             boxWidth = this.containerWidth
30859         }
30860         
30861         var containerWidth = this.containerWidth;
30862         
30863         var cols = Math.floor(containerWidth / boxWidth);
30864         
30865         this.cols = Math.max( cols, 1 );
30866         
30867         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30868         
30869         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30870         
30871         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30872         
30873         this.colWidth = boxWidth + avail - this.padWidth;
30874         
30875         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30876         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30877     },
30878     
30879     horizontalMeasureColumns : function()
30880     {
30881         this.getContainerWidth();
30882         
30883         var boxWidth = this.boxWidth;
30884         
30885         if(this.containerWidth < boxWidth){
30886             boxWidth = this.containerWidth;
30887         }
30888         
30889         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30890         
30891         this.el.setHeight(boxWidth);
30892         
30893     },
30894     
30895     getContainerWidth : function()
30896     {
30897         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30898     },
30899     
30900     layoutItems : function( isInstant )
30901     {
30902         Roo.log(this.bricks);
30903         
30904         var items = Roo.apply([], this.bricks);
30905         
30906         if(this.isHorizontal){
30907             this._horizontalLayoutItems( items , isInstant );
30908             return;
30909         }
30910         
30911 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30912 //            this._verticalAlternativeLayoutItems( items , isInstant );
30913 //            return;
30914 //        }
30915         
30916         this._verticalLayoutItems( items , isInstant );
30917         
30918     },
30919     
30920     _verticalLayoutItems : function ( items , isInstant)
30921     {
30922         if ( !items || !items.length ) {
30923             return;
30924         }
30925         
30926         var standard = [
30927             ['xs', 'xs', 'xs', 'tall'],
30928             ['xs', 'xs', 'tall'],
30929             ['xs', 'xs', 'sm'],
30930             ['xs', 'xs', 'xs'],
30931             ['xs', 'tall'],
30932             ['xs', 'sm'],
30933             ['xs', 'xs'],
30934             ['xs'],
30935             
30936             ['sm', 'xs', 'xs'],
30937             ['sm', 'xs'],
30938             ['sm'],
30939             
30940             ['tall', 'xs', 'xs', 'xs'],
30941             ['tall', 'xs', 'xs'],
30942             ['tall', 'xs'],
30943             ['tall']
30944             
30945         ];
30946         
30947         var queue = [];
30948         
30949         var boxes = [];
30950         
30951         var box = [];
30952         
30953         Roo.each(items, function(item, k){
30954             
30955             switch (item.size) {
30956                 // these layouts take up a full box,
30957                 case 'md' :
30958                 case 'md-left' :
30959                 case 'md-right' :
30960                 case 'wide' :
30961                     
30962                     if(box.length){
30963                         boxes.push(box);
30964                         box = [];
30965                     }
30966                     
30967                     boxes.push([item]);
30968                     
30969                     break;
30970                     
30971                 case 'xs' :
30972                 case 'sm' :
30973                 case 'tall' :
30974                     
30975                     box.push(item);
30976                     
30977                     break;
30978                 default :
30979                     break;
30980                     
30981             }
30982             
30983         }, this);
30984         
30985         if(box.length){
30986             boxes.push(box);
30987             box = [];
30988         }
30989         
30990         var filterPattern = function(box, length)
30991         {
30992             if(!box.length){
30993                 return;
30994             }
30995             
30996             var match = false;
30997             
30998             var pattern = box.slice(0, length);
30999             
31000             var format = [];
31001             
31002             Roo.each(pattern, function(i){
31003                 format.push(i.size);
31004             }, this);
31005             
31006             Roo.each(standard, function(s){
31007                 
31008                 if(String(s) != String(format)){
31009                     return;
31010                 }
31011                 
31012                 match = true;
31013                 return false;
31014                 
31015             }, this);
31016             
31017             if(!match && length == 1){
31018                 return;
31019             }
31020             
31021             if(!match){
31022                 filterPattern(box, length - 1);
31023                 return;
31024             }
31025                 
31026             queue.push(pattern);
31027
31028             box = box.slice(length, box.length);
31029
31030             filterPattern(box, 4);
31031
31032             return;
31033             
31034         }
31035         
31036         Roo.each(boxes, function(box, k){
31037             
31038             if(!box.length){
31039                 return;
31040             }
31041             
31042             if(box.length == 1){
31043                 queue.push(box);
31044                 return;
31045             }
31046             
31047             filterPattern(box, 4);
31048             
31049         }, this);
31050         
31051         this._processVerticalLayoutQueue( queue, isInstant );
31052         
31053     },
31054     
31055 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31056 //    {
31057 //        if ( !items || !items.length ) {
31058 //            return;
31059 //        }
31060 //
31061 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31062 //        
31063 //    },
31064     
31065     _horizontalLayoutItems : function ( items , isInstant)
31066     {
31067         if ( !items || !items.length || items.length < 3) {
31068             return;
31069         }
31070         
31071         items.reverse();
31072         
31073         var eItems = items.slice(0, 3);
31074         
31075         items = items.slice(3, items.length);
31076         
31077         var standard = [
31078             ['xs', 'xs', 'xs', 'wide'],
31079             ['xs', 'xs', 'wide'],
31080             ['xs', 'xs', 'sm'],
31081             ['xs', 'xs', 'xs'],
31082             ['xs', 'wide'],
31083             ['xs', 'sm'],
31084             ['xs', 'xs'],
31085             ['xs'],
31086             
31087             ['sm', 'xs', 'xs'],
31088             ['sm', 'xs'],
31089             ['sm'],
31090             
31091             ['wide', 'xs', 'xs', 'xs'],
31092             ['wide', 'xs', 'xs'],
31093             ['wide', 'xs'],
31094             ['wide'],
31095             
31096             ['wide-thin']
31097         ];
31098         
31099         var queue = [];
31100         
31101         var boxes = [];
31102         
31103         var box = [];
31104         
31105         Roo.each(items, function(item, k){
31106             
31107             switch (item.size) {
31108                 case 'md' :
31109                 case 'md-left' :
31110                 case 'md-right' :
31111                 case 'tall' :
31112                     
31113                     if(box.length){
31114                         boxes.push(box);
31115                         box = [];
31116                     }
31117                     
31118                     boxes.push([item]);
31119                     
31120                     break;
31121                     
31122                 case 'xs' :
31123                 case 'sm' :
31124                 case 'wide' :
31125                 case 'wide-thin' :
31126                     
31127                     box.push(item);
31128                     
31129                     break;
31130                 default :
31131                     break;
31132                     
31133             }
31134             
31135         }, this);
31136         
31137         if(box.length){
31138             boxes.push(box);
31139             box = [];
31140         }
31141         
31142         var filterPattern = function(box, length)
31143         {
31144             if(!box.length){
31145                 return;
31146             }
31147             
31148             var match = false;
31149             
31150             var pattern = box.slice(0, length);
31151             
31152             var format = [];
31153             
31154             Roo.each(pattern, function(i){
31155                 format.push(i.size);
31156             }, this);
31157             
31158             Roo.each(standard, function(s){
31159                 
31160                 if(String(s) != String(format)){
31161                     return;
31162                 }
31163                 
31164                 match = true;
31165                 return false;
31166                 
31167             }, this);
31168             
31169             if(!match && length == 1){
31170                 return;
31171             }
31172             
31173             if(!match){
31174                 filterPattern(box, length - 1);
31175                 return;
31176             }
31177                 
31178             queue.push(pattern);
31179
31180             box = box.slice(length, box.length);
31181
31182             filterPattern(box, 4);
31183
31184             return;
31185             
31186         }
31187         
31188         Roo.each(boxes, function(box, k){
31189             
31190             if(!box.length){
31191                 return;
31192             }
31193             
31194             if(box.length == 1){
31195                 queue.push(box);
31196                 return;
31197             }
31198             
31199             filterPattern(box, 4);
31200             
31201         }, this);
31202         
31203         
31204         var prune = [];
31205         
31206         var pos = this.el.getBox(true);
31207         
31208         var minX = pos.x;
31209         
31210         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31211         
31212         var hit_end = false;
31213         
31214         Roo.each(queue, function(box){
31215             
31216             if(hit_end){
31217                 
31218                 Roo.each(box, function(b){
31219                 
31220                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31221                     b.el.hide();
31222
31223                 }, this);
31224
31225                 return;
31226             }
31227             
31228             var mx = 0;
31229             
31230             Roo.each(box, function(b){
31231                 
31232                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31233                 b.el.show();
31234
31235                 mx = Math.max(mx, b.x);
31236                 
31237             }, this);
31238             
31239             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31240             
31241             if(maxX < minX){
31242                 
31243                 Roo.each(box, function(b){
31244                 
31245                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31246                     b.el.hide();
31247                     
31248                 }, this);
31249                 
31250                 hit_end = true;
31251                 
31252                 return;
31253             }
31254             
31255             prune.push(box);
31256             
31257         }, this);
31258         
31259         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31260     },
31261     
31262     /** Sets position of item in DOM
31263     * @param {Element} item
31264     * @param {Number} x - horizontal position
31265     * @param {Number} y - vertical position
31266     * @param {Boolean} isInstant - disables transitions
31267     */
31268     _processVerticalLayoutQueue : function( queue, isInstant )
31269     {
31270         var pos = this.el.getBox(true);
31271         var x = pos.x;
31272         var y = pos.y;
31273         var maxY = [];
31274         
31275         for (var i = 0; i < this.cols; i++){
31276             maxY[i] = pos.y;
31277         }
31278         
31279         Roo.each(queue, function(box, k){
31280             
31281             var col = k % this.cols;
31282             
31283             Roo.each(box, function(b,kk){
31284                 
31285                 b.el.position('absolute');
31286                 
31287                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31288                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31289                 
31290                 if(b.size == 'md-left' || b.size == 'md-right'){
31291                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31292                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31293                 }
31294                 
31295                 b.el.setWidth(width);
31296                 b.el.setHeight(height);
31297                 // iframe?
31298                 b.el.select('iframe',true).setSize(width,height);
31299                 
31300             }, this);
31301             
31302             for (var i = 0; i < this.cols; i++){
31303                 
31304                 if(maxY[i] < maxY[col]){
31305                     col = i;
31306                     continue;
31307                 }
31308                 
31309                 col = Math.min(col, i);
31310                 
31311             }
31312             
31313             x = pos.x + col * (this.colWidth + this.padWidth);
31314             
31315             y = maxY[col];
31316             
31317             var positions = [];
31318             
31319             switch (box.length){
31320                 case 1 :
31321                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31322                     break;
31323                 case 2 :
31324                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31325                     break;
31326                 case 3 :
31327                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31328                     break;
31329                 case 4 :
31330                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31331                     break;
31332                 default :
31333                     break;
31334             }
31335             
31336             Roo.each(box, function(b,kk){
31337                 
31338                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31339                 
31340                 var sz = b.el.getSize();
31341                 
31342                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31343                 
31344             }, this);
31345             
31346         }, this);
31347         
31348         var mY = 0;
31349         
31350         for (var i = 0; i < this.cols; i++){
31351             mY = Math.max(mY, maxY[i]);
31352         }
31353         
31354         this.el.setHeight(mY - pos.y);
31355         
31356     },
31357     
31358 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31359 //    {
31360 //        var pos = this.el.getBox(true);
31361 //        var x = pos.x;
31362 //        var y = pos.y;
31363 //        var maxX = pos.right;
31364 //        
31365 //        var maxHeight = 0;
31366 //        
31367 //        Roo.each(items, function(item, k){
31368 //            
31369 //            var c = k % 2;
31370 //            
31371 //            item.el.position('absolute');
31372 //                
31373 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31374 //
31375 //            item.el.setWidth(width);
31376 //
31377 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31378 //
31379 //            item.el.setHeight(height);
31380 //            
31381 //            if(c == 0){
31382 //                item.el.setXY([x, y], isInstant ? false : true);
31383 //            } else {
31384 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31385 //            }
31386 //            
31387 //            y = y + height + this.alternativePadWidth;
31388 //            
31389 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31390 //            
31391 //        }, this);
31392 //        
31393 //        this.el.setHeight(maxHeight);
31394 //        
31395 //    },
31396     
31397     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31398     {
31399         var pos = this.el.getBox(true);
31400         
31401         var minX = pos.x;
31402         var minY = pos.y;
31403         
31404         var maxX = pos.right;
31405         
31406         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31407         
31408         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31409         
31410         Roo.each(queue, function(box, k){
31411             
31412             Roo.each(box, function(b, kk){
31413                 
31414                 b.el.position('absolute');
31415                 
31416                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31417                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31418                 
31419                 if(b.size == 'md-left' || b.size == 'md-right'){
31420                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31421                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31422                 }
31423                 
31424                 b.el.setWidth(width);
31425                 b.el.setHeight(height);
31426                 
31427             }, this);
31428             
31429             if(!box.length){
31430                 return;
31431             }
31432             
31433             var positions = [];
31434             
31435             switch (box.length){
31436                 case 1 :
31437                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31438                     break;
31439                 case 2 :
31440                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31441                     break;
31442                 case 3 :
31443                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31444                     break;
31445                 case 4 :
31446                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31447                     break;
31448                 default :
31449                     break;
31450             }
31451             
31452             Roo.each(box, function(b,kk){
31453                 
31454                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31455                 
31456                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31457                 
31458             }, this);
31459             
31460         }, this);
31461         
31462     },
31463     
31464     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31465     {
31466         Roo.each(eItems, function(b,k){
31467             
31468             b.size = (k == 0) ? 'sm' : 'xs';
31469             b.x = (k == 0) ? 2 : 1;
31470             b.y = (k == 0) ? 2 : 1;
31471             
31472             b.el.position('absolute');
31473             
31474             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31475                 
31476             b.el.setWidth(width);
31477             
31478             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31479             
31480             b.el.setHeight(height);
31481             
31482         }, this);
31483
31484         var positions = [];
31485         
31486         positions.push({
31487             x : maxX - this.unitWidth * 2 - this.gutter,
31488             y : minY
31489         });
31490         
31491         positions.push({
31492             x : maxX - this.unitWidth,
31493             y : minY + (this.unitWidth + this.gutter) * 2
31494         });
31495         
31496         positions.push({
31497             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31498             y : minY
31499         });
31500         
31501         Roo.each(eItems, function(b,k){
31502             
31503             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31504
31505         }, this);
31506         
31507     },
31508     
31509     getVerticalOneBoxColPositions : function(x, y, box)
31510     {
31511         var pos = [];
31512         
31513         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31514         
31515         if(box[0].size == 'md-left'){
31516             rand = 0;
31517         }
31518         
31519         if(box[0].size == 'md-right'){
31520             rand = 1;
31521         }
31522         
31523         pos.push({
31524             x : x + (this.unitWidth + this.gutter) * rand,
31525             y : y
31526         });
31527         
31528         return pos;
31529     },
31530     
31531     getVerticalTwoBoxColPositions : function(x, y, box)
31532     {
31533         var pos = [];
31534         
31535         if(box[0].size == 'xs'){
31536             
31537             pos.push({
31538                 x : x,
31539                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31540             });
31541
31542             pos.push({
31543                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31544                 y : y
31545             });
31546             
31547             return pos;
31548             
31549         }
31550         
31551         pos.push({
31552             x : x,
31553             y : y
31554         });
31555
31556         pos.push({
31557             x : x + (this.unitWidth + this.gutter) * 2,
31558             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31559         });
31560         
31561         return pos;
31562         
31563     },
31564     
31565     getVerticalThreeBoxColPositions : function(x, y, box)
31566     {
31567         var pos = [];
31568         
31569         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31570             
31571             pos.push({
31572                 x : x,
31573                 y : y
31574             });
31575
31576             pos.push({
31577                 x : x + (this.unitWidth + this.gutter) * 1,
31578                 y : y
31579             });
31580             
31581             pos.push({
31582                 x : x + (this.unitWidth + this.gutter) * 2,
31583                 y : y
31584             });
31585             
31586             return pos;
31587             
31588         }
31589         
31590         if(box[0].size == 'xs' && box[1].size == 'xs'){
31591             
31592             pos.push({
31593                 x : x,
31594                 y : y
31595             });
31596
31597             pos.push({
31598                 x : x,
31599                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31600             });
31601             
31602             pos.push({
31603                 x : x + (this.unitWidth + this.gutter) * 1,
31604                 y : y
31605             });
31606             
31607             return pos;
31608             
31609         }
31610         
31611         pos.push({
31612             x : x,
31613             y : y
31614         });
31615
31616         pos.push({
31617             x : x + (this.unitWidth + this.gutter) * 2,
31618             y : y
31619         });
31620
31621         pos.push({
31622             x : x + (this.unitWidth + this.gutter) * 2,
31623             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31624         });
31625             
31626         return pos;
31627         
31628     },
31629     
31630     getVerticalFourBoxColPositions : function(x, y, box)
31631     {
31632         var pos = [];
31633         
31634         if(box[0].size == 'xs'){
31635             
31636             pos.push({
31637                 x : x,
31638                 y : y
31639             });
31640
31641             pos.push({
31642                 x : x,
31643                 y : y + (this.unitHeight + this.gutter) * 1
31644             });
31645             
31646             pos.push({
31647                 x : x,
31648                 y : y + (this.unitHeight + this.gutter) * 2
31649             });
31650             
31651             pos.push({
31652                 x : x + (this.unitWidth + this.gutter) * 1,
31653                 y : y
31654             });
31655             
31656             return pos;
31657             
31658         }
31659         
31660         pos.push({
31661             x : x,
31662             y : y
31663         });
31664
31665         pos.push({
31666             x : x + (this.unitWidth + this.gutter) * 2,
31667             y : y
31668         });
31669
31670         pos.push({
31671             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31672             y : y + (this.unitHeight + this.gutter) * 1
31673         });
31674
31675         pos.push({
31676             x : x + (this.unitWidth + this.gutter) * 2,
31677             y : y + (this.unitWidth + this.gutter) * 2
31678         });
31679
31680         return pos;
31681         
31682     },
31683     
31684     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31685     {
31686         var pos = [];
31687         
31688         if(box[0].size == 'md-left'){
31689             pos.push({
31690                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31691                 y : minY
31692             });
31693             
31694             return pos;
31695         }
31696         
31697         if(box[0].size == 'md-right'){
31698             pos.push({
31699                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31700                 y : minY + (this.unitWidth + this.gutter) * 1
31701             });
31702             
31703             return pos;
31704         }
31705         
31706         var rand = Math.floor(Math.random() * (4 - box[0].y));
31707         
31708         pos.push({
31709             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31710             y : minY + (this.unitWidth + this.gutter) * rand
31711         });
31712         
31713         return pos;
31714         
31715     },
31716     
31717     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31718     {
31719         var pos = [];
31720         
31721         if(box[0].size == 'xs'){
31722             
31723             pos.push({
31724                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31725                 y : minY
31726             });
31727
31728             pos.push({
31729                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31730                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31731             });
31732             
31733             return pos;
31734             
31735         }
31736         
31737         pos.push({
31738             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31739             y : minY
31740         });
31741
31742         pos.push({
31743             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31744             y : minY + (this.unitWidth + this.gutter) * 2
31745         });
31746         
31747         return pos;
31748         
31749     },
31750     
31751     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31752     {
31753         var pos = [];
31754         
31755         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31756             
31757             pos.push({
31758                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31759                 y : minY
31760             });
31761
31762             pos.push({
31763                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31764                 y : minY + (this.unitWidth + this.gutter) * 1
31765             });
31766             
31767             pos.push({
31768                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31769                 y : minY + (this.unitWidth + this.gutter) * 2
31770             });
31771             
31772             return pos;
31773             
31774         }
31775         
31776         if(box[0].size == 'xs' && box[1].size == 'xs'){
31777             
31778             pos.push({
31779                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31780                 y : minY
31781             });
31782
31783             pos.push({
31784                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31785                 y : minY
31786             });
31787             
31788             pos.push({
31789                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31790                 y : minY + (this.unitWidth + this.gutter) * 1
31791             });
31792             
31793             return pos;
31794             
31795         }
31796         
31797         pos.push({
31798             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31799             y : minY
31800         });
31801
31802         pos.push({
31803             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31804             y : minY + (this.unitWidth + this.gutter) * 2
31805         });
31806
31807         pos.push({
31808             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31809             y : minY + (this.unitWidth + this.gutter) * 2
31810         });
31811             
31812         return pos;
31813         
31814     },
31815     
31816     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31817     {
31818         var pos = [];
31819         
31820         if(box[0].size == 'xs'){
31821             
31822             pos.push({
31823                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31824                 y : minY
31825             });
31826
31827             pos.push({
31828                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31829                 y : minY
31830             });
31831             
31832             pos.push({
31833                 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),
31834                 y : minY
31835             });
31836             
31837             pos.push({
31838                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31839                 y : minY + (this.unitWidth + this.gutter) * 1
31840             });
31841             
31842             return pos;
31843             
31844         }
31845         
31846         pos.push({
31847             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31848             y : minY
31849         });
31850         
31851         pos.push({
31852             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31853             y : minY + (this.unitWidth + this.gutter) * 2
31854         });
31855         
31856         pos.push({
31857             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31858             y : minY + (this.unitWidth + this.gutter) * 2
31859         });
31860         
31861         pos.push({
31862             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),
31863             y : minY + (this.unitWidth + this.gutter) * 2
31864         });
31865
31866         return pos;
31867         
31868     },
31869     
31870     /**
31871     * remove a Masonry Brick
31872     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31873     */
31874     removeBrick : function(brick_id)
31875     {
31876         if (!brick_id) {
31877             return;
31878         }
31879         
31880         for (var i = 0; i<this.bricks.length; i++) {
31881             if (this.bricks[i].id == brick_id) {
31882                 this.bricks.splice(i,1);
31883                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31884                 this.initial();
31885             }
31886         }
31887     },
31888     
31889     /**
31890     * adds a Masonry Brick
31891     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31892     */
31893     addBrick : function(cfg)
31894     {
31895         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31896         //this.register(cn);
31897         cn.parentId = this.id;
31898         cn.onRender(this.el, null);
31899         return cn;
31900     },
31901     
31902     /**
31903     * register a Masonry Brick
31904     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31905     */
31906     
31907     register : function(brick)
31908     {
31909         this.bricks.push(brick);
31910         brick.masonryId = this.id;
31911     },
31912     
31913     /**
31914     * clear all the Masonry Brick
31915     */
31916     clearAll : function()
31917     {
31918         this.bricks = [];
31919         //this.getChildContainer().dom.innerHTML = "";
31920         this.el.dom.innerHTML = '';
31921     },
31922     
31923     getSelected : function()
31924     {
31925         if (!this.selectedBrick) {
31926             return false;
31927         }
31928         
31929         return this.selectedBrick;
31930     }
31931 });
31932
31933 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31934     
31935     groups: {},
31936      /**
31937     * register a Masonry Layout
31938     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31939     */
31940     
31941     register : function(layout)
31942     {
31943         this.groups[layout.id] = layout;
31944     },
31945     /**
31946     * fetch a  Masonry Layout based on the masonry layout ID
31947     * @param {string} the masonry layout to add
31948     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31949     */
31950     
31951     get: function(layout_id) {
31952         if (typeof(this.groups[layout_id]) == 'undefined') {
31953             return false;
31954         }
31955         return this.groups[layout_id] ;
31956     }
31957     
31958     
31959     
31960 });
31961
31962  
31963
31964  /**
31965  *
31966  * This is based on 
31967  * http://masonry.desandro.com
31968  *
31969  * The idea is to render all the bricks based on vertical width...
31970  *
31971  * The original code extends 'outlayer' - we might need to use that....
31972  * 
31973  */
31974
31975
31976 /**
31977  * @class Roo.bootstrap.LayoutMasonryAuto
31978  * @extends Roo.bootstrap.Component
31979  * Bootstrap Layout Masonry class
31980  * 
31981  * @constructor
31982  * Create a new Element
31983  * @param {Object} config The config object
31984  */
31985
31986 Roo.bootstrap.LayoutMasonryAuto = function(config){
31987     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31988 };
31989
31990 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31991     
31992       /**
31993      * @cfg {Boolean} isFitWidth  - resize the width..
31994      */   
31995     isFitWidth : false,  // options..
31996     /**
31997      * @cfg {Boolean} isOriginLeft = left align?
31998      */   
31999     isOriginLeft : true,
32000     /**
32001      * @cfg {Boolean} isOriginTop = top align?
32002      */   
32003     isOriginTop : false,
32004     /**
32005      * @cfg {Boolean} isLayoutInstant = no animation?
32006      */   
32007     isLayoutInstant : false, // needed?
32008     /**
32009      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32010      */   
32011     isResizingContainer : true,
32012     /**
32013      * @cfg {Number} columnWidth  width of the columns 
32014      */   
32015     
32016     columnWidth : 0,
32017     
32018     /**
32019      * @cfg {Number} maxCols maximum number of columns
32020      */   
32021     
32022     maxCols: 0,
32023     /**
32024      * @cfg {Number} padHeight padding below box..
32025      */   
32026     
32027     padHeight : 10, 
32028     
32029     /**
32030      * @cfg {Boolean} isAutoInitial defalut true
32031      */   
32032     
32033     isAutoInitial : true, 
32034     
32035     // private?
32036     gutter : 0,
32037     
32038     containerWidth: 0,
32039     initialColumnWidth : 0,
32040     currentSize : null,
32041     
32042     colYs : null, // array.
32043     maxY : 0,
32044     padWidth: 10,
32045     
32046     
32047     tag: 'div',
32048     cls: '',
32049     bricks: null, //CompositeElement
32050     cols : 0, // array?
32051     // element : null, // wrapped now this.el
32052     _isLayoutInited : null, 
32053     
32054     
32055     getAutoCreate : function(){
32056         
32057         var cfg = {
32058             tag: this.tag,
32059             cls: 'blog-masonary-wrapper ' + this.cls,
32060             cn : {
32061                 cls : 'mas-boxes masonary'
32062             }
32063         };
32064         
32065         return cfg;
32066     },
32067     
32068     getChildContainer: function( )
32069     {
32070         if (this.boxesEl) {
32071             return this.boxesEl;
32072         }
32073         
32074         this.boxesEl = this.el.select('.mas-boxes').first();
32075         
32076         return this.boxesEl;
32077     },
32078     
32079     
32080     initEvents : function()
32081     {
32082         var _this = this;
32083         
32084         if(this.isAutoInitial){
32085             Roo.log('hook children rendered');
32086             this.on('childrenrendered', function() {
32087                 Roo.log('children rendered');
32088                 _this.initial();
32089             } ,this);
32090         }
32091         
32092     },
32093     
32094     initial : function()
32095     {
32096         this.reloadItems();
32097
32098         this.currentSize = this.el.getBox(true);
32099
32100         /// was window resize... - let's see if this works..
32101         Roo.EventManager.onWindowResize(this.resize, this); 
32102
32103         if(!this.isAutoInitial){
32104             this.layout();
32105             return;
32106         }
32107         
32108         this.layout.defer(500,this);
32109     },
32110     
32111     reloadItems: function()
32112     {
32113         this.bricks = this.el.select('.masonry-brick', true);
32114         
32115         this.bricks.each(function(b) {
32116             //Roo.log(b.getSize());
32117             if (!b.attr('originalwidth')) {
32118                 b.attr('originalwidth',  b.getSize().width);
32119             }
32120             
32121         });
32122         
32123         Roo.log(this.bricks.elements.length);
32124     },
32125     
32126     resize : function()
32127     {
32128         Roo.log('resize');
32129         var cs = this.el.getBox(true);
32130         
32131         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32132             Roo.log("no change in with or X");
32133             return;
32134         }
32135         this.currentSize = cs;
32136         this.layout();
32137     },
32138     
32139     layout : function()
32140     {
32141          Roo.log('layout');
32142         this._resetLayout();
32143         //this._manageStamps();
32144       
32145         // don't animate first layout
32146         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32147         this.layoutItems( isInstant );
32148       
32149         // flag for initalized
32150         this._isLayoutInited = true;
32151     },
32152     
32153     layoutItems : function( isInstant )
32154     {
32155         //var items = this._getItemsForLayout( this.items );
32156         // original code supports filtering layout items.. we just ignore it..
32157         
32158         this._layoutItems( this.bricks , isInstant );
32159       
32160         this._postLayout();
32161     },
32162     _layoutItems : function ( items , isInstant)
32163     {
32164        //this.fireEvent( 'layout', this, items );
32165     
32166
32167         if ( !items || !items.elements.length ) {
32168           // no items, emit event with empty array
32169             return;
32170         }
32171
32172         var queue = [];
32173         items.each(function(item) {
32174             Roo.log("layout item");
32175             Roo.log(item);
32176             // get x/y object from method
32177             var position = this._getItemLayoutPosition( item );
32178             // enqueue
32179             position.item = item;
32180             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32181             queue.push( position );
32182         }, this);
32183       
32184         this._processLayoutQueue( queue );
32185     },
32186     /** Sets position of item in DOM
32187     * @param {Element} item
32188     * @param {Number} x - horizontal position
32189     * @param {Number} y - vertical position
32190     * @param {Boolean} isInstant - disables transitions
32191     */
32192     _processLayoutQueue : function( queue )
32193     {
32194         for ( var i=0, len = queue.length; i < len; i++ ) {
32195             var obj = queue[i];
32196             obj.item.position('absolute');
32197             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32198         }
32199     },
32200       
32201     
32202     /**
32203     * Any logic you want to do after each layout,
32204     * i.e. size the container
32205     */
32206     _postLayout : function()
32207     {
32208         this.resizeContainer();
32209     },
32210     
32211     resizeContainer : function()
32212     {
32213         if ( !this.isResizingContainer ) {
32214             return;
32215         }
32216         var size = this._getContainerSize();
32217         if ( size ) {
32218             this.el.setSize(size.width,size.height);
32219             this.boxesEl.setSize(size.width,size.height);
32220         }
32221     },
32222     
32223     
32224     
32225     _resetLayout : function()
32226     {
32227         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32228         this.colWidth = this.el.getWidth();
32229         //this.gutter = this.el.getWidth(); 
32230         
32231         this.measureColumns();
32232
32233         // reset column Y
32234         var i = this.cols;
32235         this.colYs = [];
32236         while (i--) {
32237             this.colYs.push( 0 );
32238         }
32239     
32240         this.maxY = 0;
32241     },
32242
32243     measureColumns : function()
32244     {
32245         this.getContainerWidth();
32246       // if columnWidth is 0, default to outerWidth of first item
32247         if ( !this.columnWidth ) {
32248             var firstItem = this.bricks.first();
32249             Roo.log(firstItem);
32250             this.columnWidth  = this.containerWidth;
32251             if (firstItem && firstItem.attr('originalwidth') ) {
32252                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32253             }
32254             // columnWidth fall back to item of first element
32255             Roo.log("set column width?");
32256                         this.initialColumnWidth = this.columnWidth  ;
32257
32258             // if first elem has no width, default to size of container
32259             
32260         }
32261         
32262         
32263         if (this.initialColumnWidth) {
32264             this.columnWidth = this.initialColumnWidth;
32265         }
32266         
32267         
32268             
32269         // column width is fixed at the top - however if container width get's smaller we should
32270         // reduce it...
32271         
32272         // this bit calcs how man columns..
32273             
32274         var columnWidth = this.columnWidth += this.gutter;
32275       
32276         // calculate columns
32277         var containerWidth = this.containerWidth + this.gutter;
32278         
32279         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32280         // fix rounding errors, typically with gutters
32281         var excess = columnWidth - containerWidth % columnWidth;
32282         
32283         
32284         // if overshoot is less than a pixel, round up, otherwise floor it
32285         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32286         cols = Math[ mathMethod ]( cols );
32287         this.cols = Math.max( cols, 1 );
32288         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32289         
32290          // padding positioning..
32291         var totalColWidth = this.cols * this.columnWidth;
32292         var padavail = this.containerWidth - totalColWidth;
32293         // so for 2 columns - we need 3 'pads'
32294         
32295         var padNeeded = (1+this.cols) * this.padWidth;
32296         
32297         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32298         
32299         this.columnWidth += padExtra
32300         //this.padWidth = Math.floor(padavail /  ( this.cols));
32301         
32302         // adjust colum width so that padding is fixed??
32303         
32304         // we have 3 columns ... total = width * 3
32305         // we have X left over... that should be used by 
32306         
32307         //if (this.expandC) {
32308             
32309         //}
32310         
32311         
32312         
32313     },
32314     
32315     getContainerWidth : function()
32316     {
32317        /* // container is parent if fit width
32318         var container = this.isFitWidth ? this.element.parentNode : this.element;
32319         // check that this.size and size are there
32320         // IE8 triggers resize on body size change, so they might not be
32321         
32322         var size = getSize( container );  //FIXME
32323         this.containerWidth = size && size.innerWidth; //FIXME
32324         */
32325          
32326         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32327         
32328     },
32329     
32330     _getItemLayoutPosition : function( item )  // what is item?
32331     {
32332         // we resize the item to our columnWidth..
32333       
32334         item.setWidth(this.columnWidth);
32335         item.autoBoxAdjust  = false;
32336         
32337         var sz = item.getSize();
32338  
32339         // how many columns does this brick span
32340         var remainder = this.containerWidth % this.columnWidth;
32341         
32342         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32343         // round if off by 1 pixel, otherwise use ceil
32344         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32345         colSpan = Math.min( colSpan, this.cols );
32346         
32347         // normally this should be '1' as we dont' currently allow multi width columns..
32348         
32349         var colGroup = this._getColGroup( colSpan );
32350         // get the minimum Y value from the columns
32351         var minimumY = Math.min.apply( Math, colGroup );
32352         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32353         
32354         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32355          
32356         // position the brick
32357         var position = {
32358             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32359             y: this.currentSize.y + minimumY + this.padHeight
32360         };
32361         
32362         Roo.log(position);
32363         // apply setHeight to necessary columns
32364         var setHeight = minimumY + sz.height + this.padHeight;
32365         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32366         
32367         var setSpan = this.cols + 1 - colGroup.length;
32368         for ( var i = 0; i < setSpan; i++ ) {
32369           this.colYs[ shortColIndex + i ] = setHeight ;
32370         }
32371       
32372         return position;
32373     },
32374     
32375     /**
32376      * @param {Number} colSpan - number of columns the element spans
32377      * @returns {Array} colGroup
32378      */
32379     _getColGroup : function( colSpan )
32380     {
32381         if ( colSpan < 2 ) {
32382           // if brick spans only one column, use all the column Ys
32383           return this.colYs;
32384         }
32385       
32386         var colGroup = [];
32387         // how many different places could this brick fit horizontally
32388         var groupCount = this.cols + 1 - colSpan;
32389         // for each group potential horizontal position
32390         for ( var i = 0; i < groupCount; i++ ) {
32391           // make an array of colY values for that one group
32392           var groupColYs = this.colYs.slice( i, i + colSpan );
32393           // and get the max value of the array
32394           colGroup[i] = Math.max.apply( Math, groupColYs );
32395         }
32396         return colGroup;
32397     },
32398     /*
32399     _manageStamp : function( stamp )
32400     {
32401         var stampSize =  stamp.getSize();
32402         var offset = stamp.getBox();
32403         // get the columns that this stamp affects
32404         var firstX = this.isOriginLeft ? offset.x : offset.right;
32405         var lastX = firstX + stampSize.width;
32406         var firstCol = Math.floor( firstX / this.columnWidth );
32407         firstCol = Math.max( 0, firstCol );
32408         
32409         var lastCol = Math.floor( lastX / this.columnWidth );
32410         // lastCol should not go over if multiple of columnWidth #425
32411         lastCol -= lastX % this.columnWidth ? 0 : 1;
32412         lastCol = Math.min( this.cols - 1, lastCol );
32413         
32414         // set colYs to bottom of the stamp
32415         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32416             stampSize.height;
32417             
32418         for ( var i = firstCol; i <= lastCol; i++ ) {
32419           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32420         }
32421     },
32422     */
32423     
32424     _getContainerSize : function()
32425     {
32426         this.maxY = Math.max.apply( Math, this.colYs );
32427         var size = {
32428             height: this.maxY
32429         };
32430       
32431         if ( this.isFitWidth ) {
32432             size.width = this._getContainerFitWidth();
32433         }
32434       
32435         return size;
32436     },
32437     
32438     _getContainerFitWidth : function()
32439     {
32440         var unusedCols = 0;
32441         // count unused columns
32442         var i = this.cols;
32443         while ( --i ) {
32444           if ( this.colYs[i] !== 0 ) {
32445             break;
32446           }
32447           unusedCols++;
32448         }
32449         // fit container to columns that have been used
32450         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32451     },
32452     
32453     needsResizeLayout : function()
32454     {
32455         var previousWidth = this.containerWidth;
32456         this.getContainerWidth();
32457         return previousWidth !== this.containerWidth;
32458     }
32459  
32460 });
32461
32462  
32463
32464  /*
32465  * - LGPL
32466  *
32467  * element
32468  * 
32469  */
32470
32471 /**
32472  * @class Roo.bootstrap.MasonryBrick
32473  * @extends Roo.bootstrap.Component
32474  * Bootstrap MasonryBrick class
32475  * 
32476  * @constructor
32477  * Create a new MasonryBrick
32478  * @param {Object} config The config object
32479  */
32480
32481 Roo.bootstrap.MasonryBrick = function(config){
32482     
32483     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32484     
32485     Roo.bootstrap.MasonryBrick.register(this);
32486     
32487     this.addEvents({
32488         // raw events
32489         /**
32490          * @event click
32491          * When a MasonryBrick is clcik
32492          * @param {Roo.bootstrap.MasonryBrick} this
32493          * @param {Roo.EventObject} e
32494          */
32495         "click" : true
32496     });
32497 };
32498
32499 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32500     
32501     /**
32502      * @cfg {String} title
32503      */   
32504     title : '',
32505     /**
32506      * @cfg {String} html
32507      */   
32508     html : '',
32509     /**
32510      * @cfg {String} bgimage
32511      */   
32512     bgimage : '',
32513     /**
32514      * @cfg {String} videourl
32515      */   
32516     videourl : '',
32517     /**
32518      * @cfg {String} cls
32519      */   
32520     cls : '',
32521     /**
32522      * @cfg {String} href
32523      */   
32524     href : '',
32525     /**
32526      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32527      */   
32528     size : 'xs',
32529     
32530     /**
32531      * @cfg {String} placetitle (center|bottom)
32532      */   
32533     placetitle : '',
32534     
32535     /**
32536      * @cfg {Boolean} isFitContainer defalut true
32537      */   
32538     isFitContainer : true, 
32539     
32540     /**
32541      * @cfg {Boolean} preventDefault defalut false
32542      */   
32543     preventDefault : false, 
32544     
32545     /**
32546      * @cfg {Boolean} inverse defalut false
32547      */   
32548     maskInverse : false, 
32549     
32550     getAutoCreate : function()
32551     {
32552         if(!this.isFitContainer){
32553             return this.getSplitAutoCreate();
32554         }
32555         
32556         var cls = 'masonry-brick masonry-brick-full';
32557         
32558         if(this.href.length){
32559             cls += ' masonry-brick-link';
32560         }
32561         
32562         if(this.bgimage.length){
32563             cls += ' masonry-brick-image';
32564         }
32565         
32566         if(this.maskInverse){
32567             cls += ' mask-inverse';
32568         }
32569         
32570         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32571             cls += ' enable-mask';
32572         }
32573         
32574         if(this.size){
32575             cls += ' masonry-' + this.size + '-brick';
32576         }
32577         
32578         if(this.placetitle.length){
32579             
32580             switch (this.placetitle) {
32581                 case 'center' :
32582                     cls += ' masonry-center-title';
32583                     break;
32584                 case 'bottom' :
32585                     cls += ' masonry-bottom-title';
32586                     break;
32587                 default:
32588                     break;
32589             }
32590             
32591         } else {
32592             if(!this.html.length && !this.bgimage.length){
32593                 cls += ' masonry-center-title';
32594             }
32595
32596             if(!this.html.length && this.bgimage.length){
32597                 cls += ' masonry-bottom-title';
32598             }
32599         }
32600         
32601         if(this.cls){
32602             cls += ' ' + this.cls;
32603         }
32604         
32605         var cfg = {
32606             tag: (this.href.length) ? 'a' : 'div',
32607             cls: cls,
32608             cn: [
32609                 {
32610                     tag: 'div',
32611                     cls: 'masonry-brick-mask'
32612                 },
32613                 {
32614                     tag: 'div',
32615                     cls: 'masonry-brick-paragraph',
32616                     cn: []
32617                 }
32618             ]
32619         };
32620         
32621         if(this.href.length){
32622             cfg.href = this.href;
32623         }
32624         
32625         var cn = cfg.cn[1].cn;
32626         
32627         if(this.title.length){
32628             cn.push({
32629                 tag: 'h4',
32630                 cls: 'masonry-brick-title',
32631                 html: this.title
32632             });
32633         }
32634         
32635         if(this.html.length){
32636             cn.push({
32637                 tag: 'p',
32638                 cls: 'masonry-brick-text',
32639                 html: this.html
32640             });
32641         }
32642         
32643         if (!this.title.length && !this.html.length) {
32644             cfg.cn[1].cls += ' hide';
32645         }
32646         
32647         if(this.bgimage.length){
32648             cfg.cn.push({
32649                 tag: 'img',
32650                 cls: 'masonry-brick-image-view',
32651                 src: this.bgimage
32652             });
32653         }
32654         
32655         if(this.videourl.length){
32656             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32657             // youtube support only?
32658             cfg.cn.push({
32659                 tag: 'iframe',
32660                 cls: 'masonry-brick-image-view',
32661                 src: vurl,
32662                 frameborder : 0,
32663                 allowfullscreen : true
32664             });
32665         }
32666         
32667         return cfg;
32668         
32669     },
32670     
32671     getSplitAutoCreate : function()
32672     {
32673         var cls = 'masonry-brick masonry-brick-split';
32674         
32675         if(this.href.length){
32676             cls += ' masonry-brick-link';
32677         }
32678         
32679         if(this.bgimage.length){
32680             cls += ' masonry-brick-image';
32681         }
32682         
32683         if(this.size){
32684             cls += ' masonry-' + this.size + '-brick';
32685         }
32686         
32687         switch (this.placetitle) {
32688             case 'center' :
32689                 cls += ' masonry-center-title';
32690                 break;
32691             case 'bottom' :
32692                 cls += ' masonry-bottom-title';
32693                 break;
32694             default:
32695                 if(!this.bgimage.length){
32696                     cls += ' masonry-center-title';
32697                 }
32698
32699                 if(this.bgimage.length){
32700                     cls += ' masonry-bottom-title';
32701                 }
32702                 break;
32703         }
32704         
32705         if(this.cls){
32706             cls += ' ' + this.cls;
32707         }
32708         
32709         var cfg = {
32710             tag: (this.href.length) ? 'a' : 'div',
32711             cls: cls,
32712             cn: [
32713                 {
32714                     tag: 'div',
32715                     cls: 'masonry-brick-split-head',
32716                     cn: [
32717                         {
32718                             tag: 'div',
32719                             cls: 'masonry-brick-paragraph',
32720                             cn: []
32721                         }
32722                     ]
32723                 },
32724                 {
32725                     tag: 'div',
32726                     cls: 'masonry-brick-split-body',
32727                     cn: []
32728                 }
32729             ]
32730         };
32731         
32732         if(this.href.length){
32733             cfg.href = this.href;
32734         }
32735         
32736         if(this.title.length){
32737             cfg.cn[0].cn[0].cn.push({
32738                 tag: 'h4',
32739                 cls: 'masonry-brick-title',
32740                 html: this.title
32741             });
32742         }
32743         
32744         if(this.html.length){
32745             cfg.cn[1].cn.push({
32746                 tag: 'p',
32747                 cls: 'masonry-brick-text',
32748                 html: this.html
32749             });
32750         }
32751
32752         if(this.bgimage.length){
32753             cfg.cn[0].cn.push({
32754                 tag: 'img',
32755                 cls: 'masonry-brick-image-view',
32756                 src: this.bgimage
32757             });
32758         }
32759         
32760         if(this.videourl.length){
32761             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32762             // youtube support only?
32763             cfg.cn[0].cn.cn.push({
32764                 tag: 'iframe',
32765                 cls: 'masonry-brick-image-view',
32766                 src: vurl,
32767                 frameborder : 0,
32768                 allowfullscreen : true
32769             });
32770         }
32771         
32772         return cfg;
32773     },
32774     
32775     initEvents: function() 
32776     {
32777         switch (this.size) {
32778             case 'xs' :
32779                 this.x = 1;
32780                 this.y = 1;
32781                 break;
32782             case 'sm' :
32783                 this.x = 2;
32784                 this.y = 2;
32785                 break;
32786             case 'md' :
32787             case 'md-left' :
32788             case 'md-right' :
32789                 this.x = 3;
32790                 this.y = 3;
32791                 break;
32792             case 'tall' :
32793                 this.x = 2;
32794                 this.y = 3;
32795                 break;
32796             case 'wide' :
32797                 this.x = 3;
32798                 this.y = 2;
32799                 break;
32800             case 'wide-thin' :
32801                 this.x = 3;
32802                 this.y = 1;
32803                 break;
32804                         
32805             default :
32806                 break;
32807         }
32808         
32809         if(Roo.isTouch){
32810             this.el.on('touchstart', this.onTouchStart, this);
32811             this.el.on('touchmove', this.onTouchMove, this);
32812             this.el.on('touchend', this.onTouchEnd, this);
32813             this.el.on('contextmenu', this.onContextMenu, this);
32814         } else {
32815             this.el.on('mouseenter'  ,this.enter, this);
32816             this.el.on('mouseleave', this.leave, this);
32817             this.el.on('click', this.onClick, this);
32818         }
32819         
32820         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32821             this.parent().bricks.push(this);   
32822         }
32823         
32824     },
32825     
32826     onClick: function(e, el)
32827     {
32828         var time = this.endTimer - this.startTimer;
32829         // Roo.log(e.preventDefault());
32830         if(Roo.isTouch){
32831             if(time > 1000){
32832                 e.preventDefault();
32833                 return;
32834             }
32835         }
32836         
32837         if(!this.preventDefault){
32838             return;
32839         }
32840         
32841         e.preventDefault();
32842         
32843         if (this.activeClass != '') {
32844             this.selectBrick();
32845         }
32846         
32847         this.fireEvent('click', this, e);
32848     },
32849     
32850     enter: function(e, el)
32851     {
32852         e.preventDefault();
32853         
32854         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32855             return;
32856         }
32857         
32858         if(this.bgimage.length && this.html.length){
32859             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32860         }
32861     },
32862     
32863     leave: function(e, el)
32864     {
32865         e.preventDefault();
32866         
32867         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32868             return;
32869         }
32870         
32871         if(this.bgimage.length && this.html.length){
32872             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32873         }
32874     },
32875     
32876     onTouchStart: function(e, el)
32877     {
32878 //        e.preventDefault();
32879         
32880         this.touchmoved = false;
32881         
32882         if(!this.isFitContainer){
32883             return;
32884         }
32885         
32886         if(!this.bgimage.length || !this.html.length){
32887             return;
32888         }
32889         
32890         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32891         
32892         this.timer = new Date().getTime();
32893         
32894     },
32895     
32896     onTouchMove: function(e, el)
32897     {
32898         this.touchmoved = true;
32899     },
32900     
32901     onContextMenu : function(e,el)
32902     {
32903         e.preventDefault();
32904         e.stopPropagation();
32905         return false;
32906     },
32907     
32908     onTouchEnd: function(e, el)
32909     {
32910 //        e.preventDefault();
32911         
32912         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32913         
32914             this.leave(e,el);
32915             
32916             return;
32917         }
32918         
32919         if(!this.bgimage.length || !this.html.length){
32920             
32921             if(this.href.length){
32922                 window.location.href = this.href;
32923             }
32924             
32925             return;
32926         }
32927         
32928         if(!this.isFitContainer){
32929             return;
32930         }
32931         
32932         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32933         
32934         window.location.href = this.href;
32935     },
32936     
32937     //selection on single brick only
32938     selectBrick : function() {
32939         
32940         if (!this.parentId) {
32941             return;
32942         }
32943         
32944         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32945         var index = m.selectedBrick.indexOf(this.id);
32946         
32947         if ( index > -1) {
32948             m.selectedBrick.splice(index,1);
32949             this.el.removeClass(this.activeClass);
32950             return;
32951         }
32952         
32953         for(var i = 0; i < m.selectedBrick.length; i++) {
32954             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32955             b.el.removeClass(b.activeClass);
32956         }
32957         
32958         m.selectedBrick = [];
32959         
32960         m.selectedBrick.push(this.id);
32961         this.el.addClass(this.activeClass);
32962         return;
32963     },
32964     
32965     isSelected : function(){
32966         return this.el.hasClass(this.activeClass);
32967         
32968     }
32969 });
32970
32971 Roo.apply(Roo.bootstrap.MasonryBrick, {
32972     
32973     //groups: {},
32974     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32975      /**
32976     * register a Masonry Brick
32977     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32978     */
32979     
32980     register : function(brick)
32981     {
32982         //this.groups[brick.id] = brick;
32983         this.groups.add(brick.id, brick);
32984     },
32985     /**
32986     * fetch a  masonry brick based on the masonry brick ID
32987     * @param {string} the masonry brick to add
32988     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32989     */
32990     
32991     get: function(brick_id) 
32992     {
32993         // if (typeof(this.groups[brick_id]) == 'undefined') {
32994         //     return false;
32995         // }
32996         // return this.groups[brick_id] ;
32997         
32998         if(this.groups.key(brick_id)) {
32999             return this.groups.key(brick_id);
33000         }
33001         
33002         return false;
33003     }
33004     
33005     
33006     
33007 });
33008
33009  /*
33010  * - LGPL
33011  *
33012  * element
33013  * 
33014  */
33015
33016 /**
33017  * @class Roo.bootstrap.Brick
33018  * @extends Roo.bootstrap.Component
33019  * Bootstrap Brick class
33020  * 
33021  * @constructor
33022  * Create a new Brick
33023  * @param {Object} config The config object
33024  */
33025
33026 Roo.bootstrap.Brick = function(config){
33027     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33028     
33029     this.addEvents({
33030         // raw events
33031         /**
33032          * @event click
33033          * When a Brick is click
33034          * @param {Roo.bootstrap.Brick} this
33035          * @param {Roo.EventObject} e
33036          */
33037         "click" : true
33038     });
33039 };
33040
33041 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33042     
33043     /**
33044      * @cfg {String} title
33045      */   
33046     title : '',
33047     /**
33048      * @cfg {String} html
33049      */   
33050     html : '',
33051     /**
33052      * @cfg {String} bgimage
33053      */   
33054     bgimage : '',
33055     /**
33056      * @cfg {String} cls
33057      */   
33058     cls : '',
33059     /**
33060      * @cfg {String} href
33061      */   
33062     href : '',
33063     /**
33064      * @cfg {String} video
33065      */   
33066     video : '',
33067     /**
33068      * @cfg {Boolean} square
33069      */   
33070     square : true,
33071     
33072     getAutoCreate : function()
33073     {
33074         var cls = 'roo-brick';
33075         
33076         if(this.href.length){
33077             cls += ' roo-brick-link';
33078         }
33079         
33080         if(this.bgimage.length){
33081             cls += ' roo-brick-image';
33082         }
33083         
33084         if(!this.html.length && !this.bgimage.length){
33085             cls += ' roo-brick-center-title';
33086         }
33087         
33088         if(!this.html.length && this.bgimage.length){
33089             cls += ' roo-brick-bottom-title';
33090         }
33091         
33092         if(this.cls){
33093             cls += ' ' + this.cls;
33094         }
33095         
33096         var cfg = {
33097             tag: (this.href.length) ? 'a' : 'div',
33098             cls: cls,
33099             cn: [
33100                 {
33101                     tag: 'div',
33102                     cls: 'roo-brick-paragraph',
33103                     cn: []
33104                 }
33105             ]
33106         };
33107         
33108         if(this.href.length){
33109             cfg.href = this.href;
33110         }
33111         
33112         var cn = cfg.cn[0].cn;
33113         
33114         if(this.title.length){
33115             cn.push({
33116                 tag: 'h4',
33117                 cls: 'roo-brick-title',
33118                 html: this.title
33119             });
33120         }
33121         
33122         if(this.html.length){
33123             cn.push({
33124                 tag: 'p',
33125                 cls: 'roo-brick-text',
33126                 html: this.html
33127             });
33128         } else {
33129             cn.cls += ' hide';
33130         }
33131         
33132         if(this.bgimage.length){
33133             cfg.cn.push({
33134                 tag: 'img',
33135                 cls: 'roo-brick-image-view',
33136                 src: this.bgimage
33137             });
33138         }
33139         
33140         return cfg;
33141     },
33142     
33143     initEvents: function() 
33144     {
33145         if(this.title.length || this.html.length){
33146             this.el.on('mouseenter'  ,this.enter, this);
33147             this.el.on('mouseleave', this.leave, this);
33148         }
33149         
33150         Roo.EventManager.onWindowResize(this.resize, this); 
33151         
33152         if(this.bgimage.length){
33153             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33154             this.imageEl.on('load', this.onImageLoad, this);
33155             return;
33156         }
33157         
33158         this.resize();
33159     },
33160     
33161     onImageLoad : function()
33162     {
33163         this.resize();
33164     },
33165     
33166     resize : function()
33167     {
33168         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33169         
33170         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33171         
33172         if(this.bgimage.length){
33173             var image = this.el.select('.roo-brick-image-view', true).first();
33174             
33175             image.setWidth(paragraph.getWidth());
33176             
33177             if(this.square){
33178                 image.setHeight(paragraph.getWidth());
33179             }
33180             
33181             this.el.setHeight(image.getHeight());
33182             paragraph.setHeight(image.getHeight());
33183             
33184         }
33185         
33186     },
33187     
33188     enter: function(e, el)
33189     {
33190         e.preventDefault();
33191         
33192         if(this.bgimage.length){
33193             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33194             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33195         }
33196     },
33197     
33198     leave: function(e, el)
33199     {
33200         e.preventDefault();
33201         
33202         if(this.bgimage.length){
33203             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33204             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33205         }
33206     }
33207     
33208 });
33209
33210  
33211
33212  /*
33213  * - LGPL
33214  *
33215  * Number field 
33216  */
33217
33218 /**
33219  * @class Roo.bootstrap.NumberField
33220  * @extends Roo.bootstrap.Input
33221  * Bootstrap NumberField class
33222  * 
33223  * 
33224  * 
33225  * 
33226  * @constructor
33227  * Create a new NumberField
33228  * @param {Object} config The config object
33229  */
33230
33231 Roo.bootstrap.NumberField = function(config){
33232     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33233 };
33234
33235 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33236     
33237     /**
33238      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33239      */
33240     allowDecimals : true,
33241     /**
33242      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33243      */
33244     decimalSeparator : ".",
33245     /**
33246      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33247      */
33248     decimalPrecision : 2,
33249     /**
33250      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33251      */
33252     allowNegative : true,
33253     
33254     /**
33255      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33256      */
33257     allowZero: true,
33258     /**
33259      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33260      */
33261     minValue : Number.NEGATIVE_INFINITY,
33262     /**
33263      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33264      */
33265     maxValue : Number.MAX_VALUE,
33266     /**
33267      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33268      */
33269     minText : "The minimum value for this field is {0}",
33270     /**
33271      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33272      */
33273     maxText : "The maximum value for this field is {0}",
33274     /**
33275      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33276      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33277      */
33278     nanText : "{0} is not a valid number",
33279     /**
33280      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33281      */
33282     thousandsDelimiter : false,
33283     /**
33284      * @cfg {String} valueAlign alignment of value
33285      */
33286     valueAlign : "left",
33287
33288     getAutoCreate : function()
33289     {
33290         var hiddenInput = {
33291             tag: 'input',
33292             type: 'hidden',
33293             id: Roo.id(),
33294             cls: 'hidden-number-input'
33295         };
33296         
33297         if (this.name) {
33298             hiddenInput.name = this.name;
33299         }
33300         
33301         this.name = '';
33302         
33303         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33304         
33305         this.name = hiddenInput.name;
33306         
33307         if(cfg.cn.length > 0) {
33308             cfg.cn.push(hiddenInput);
33309         }
33310         
33311         return cfg;
33312     },
33313
33314     // private
33315     initEvents : function()
33316     {   
33317         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33318         
33319         var allowed = "0123456789";
33320         
33321         if(this.allowDecimals){
33322             allowed += this.decimalSeparator;
33323         }
33324         
33325         if(this.allowNegative){
33326             allowed += "-";
33327         }
33328         
33329         if(this.thousandsDelimiter) {
33330             allowed += ",";
33331         }
33332         
33333         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33334         
33335         var keyPress = function(e){
33336             
33337             var k = e.getKey();
33338             
33339             var c = e.getCharCode();
33340             
33341             if(
33342                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33343                     allowed.indexOf(String.fromCharCode(c)) === -1
33344             ){
33345                 e.stopEvent();
33346                 return;
33347             }
33348             
33349             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33350                 return;
33351             }
33352             
33353             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33354                 e.stopEvent();
33355             }
33356         };
33357         
33358         this.el.on("keypress", keyPress, this);
33359     },
33360     
33361     validateValue : function(value)
33362     {
33363         
33364         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33365             return false;
33366         }
33367         
33368         var num = this.parseValue(value);
33369         
33370         if(isNaN(num)){
33371             this.markInvalid(String.format(this.nanText, value));
33372             return false;
33373         }
33374         
33375         if(num < this.minValue){
33376             this.markInvalid(String.format(this.minText, this.minValue));
33377             return false;
33378         }
33379         
33380         if(num > this.maxValue){
33381             this.markInvalid(String.format(this.maxText, this.maxValue));
33382             return false;
33383         }
33384         
33385         return true;
33386     },
33387
33388     getValue : function()
33389     {
33390         var v = this.hiddenEl().getValue();
33391         
33392         return this.fixPrecision(this.parseValue(v));
33393     },
33394
33395     parseValue : function(value)
33396     {
33397         if(this.thousandsDelimiter) {
33398             value += "";
33399             r = new RegExp(",", "g");
33400             value = value.replace(r, "");
33401         }
33402         
33403         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33404         return isNaN(value) ? '' : value;
33405     },
33406
33407     fixPrecision : function(value)
33408     {
33409         if(this.thousandsDelimiter) {
33410             value += "";
33411             r = new RegExp(",", "g");
33412             value = value.replace(r, "");
33413         }
33414         
33415         var nan = isNaN(value);
33416         
33417         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33418             return nan ? '' : value;
33419         }
33420         return parseFloat(value).toFixed(this.decimalPrecision);
33421     },
33422
33423     setValue : function(v)
33424     {
33425         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33426         
33427         this.value = v;
33428         
33429         if(this.rendered){
33430             
33431             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33432             
33433             this.inputEl().dom.value = (v == '') ? '' :
33434                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33435             
33436             if(!this.allowZero && v === '0') {
33437                 this.hiddenEl().dom.value = '';
33438                 this.inputEl().dom.value = '';
33439             }
33440             
33441             this.validate();
33442         }
33443     },
33444
33445     decimalPrecisionFcn : function(v)
33446     {
33447         return Math.floor(v);
33448     },
33449
33450     beforeBlur : function()
33451     {
33452         var v = this.parseValue(this.getRawValue());
33453         
33454         if(v || v === 0 || v === ''){
33455             this.setValue(v);
33456         }
33457     },
33458     
33459     hiddenEl : function()
33460     {
33461         return this.el.select('input.hidden-number-input',true).first();
33462     }
33463     
33464 });
33465
33466  
33467
33468 /*
33469 * Licence: LGPL
33470 */
33471
33472 /**
33473  * @class Roo.bootstrap.DocumentSlider
33474  * @extends Roo.bootstrap.Component
33475  * Bootstrap DocumentSlider class
33476  * 
33477  * @constructor
33478  * Create a new DocumentViewer
33479  * @param {Object} config The config object
33480  */
33481
33482 Roo.bootstrap.DocumentSlider = function(config){
33483     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33484     
33485     this.files = [];
33486     
33487     this.addEvents({
33488         /**
33489          * @event initial
33490          * Fire after initEvent
33491          * @param {Roo.bootstrap.DocumentSlider} this
33492          */
33493         "initial" : true,
33494         /**
33495          * @event update
33496          * Fire after update
33497          * @param {Roo.bootstrap.DocumentSlider} this
33498          */
33499         "update" : true,
33500         /**
33501          * @event click
33502          * Fire after click
33503          * @param {Roo.bootstrap.DocumentSlider} this
33504          */
33505         "click" : true
33506     });
33507 };
33508
33509 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33510     
33511     files : false,
33512     
33513     indicator : 0,
33514     
33515     getAutoCreate : function()
33516     {
33517         var cfg = {
33518             tag : 'div',
33519             cls : 'roo-document-slider',
33520             cn : [
33521                 {
33522                     tag : 'div',
33523                     cls : 'roo-document-slider-header',
33524                     cn : [
33525                         {
33526                             tag : 'div',
33527                             cls : 'roo-document-slider-header-title'
33528                         }
33529                     ]
33530                 },
33531                 {
33532                     tag : 'div',
33533                     cls : 'roo-document-slider-body',
33534                     cn : [
33535                         {
33536                             tag : 'div',
33537                             cls : 'roo-document-slider-prev',
33538                             cn : [
33539                                 {
33540                                     tag : 'i',
33541                                     cls : 'fa fa-chevron-left'
33542                                 }
33543                             ]
33544                         },
33545                         {
33546                             tag : 'div',
33547                             cls : 'roo-document-slider-thumb',
33548                             cn : [
33549                                 {
33550                                     tag : 'img',
33551                                     cls : 'roo-document-slider-image'
33552                                 }
33553                             ]
33554                         },
33555                         {
33556                             tag : 'div',
33557                             cls : 'roo-document-slider-next',
33558                             cn : [
33559                                 {
33560                                     tag : 'i',
33561                                     cls : 'fa fa-chevron-right'
33562                                 }
33563                             ]
33564                         }
33565                     ]
33566                 }
33567             ]
33568         };
33569         
33570         return cfg;
33571     },
33572     
33573     initEvents : function()
33574     {
33575         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33576         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33577         
33578         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33579         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33580         
33581         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33582         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33583         
33584         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33585         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33586         
33587         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33588         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33589         
33590         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33591         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33592         
33593         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33594         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33595         
33596         this.thumbEl.on('click', this.onClick, this);
33597         
33598         this.prevIndicator.on('click', this.prev, this);
33599         
33600         this.nextIndicator.on('click', this.next, this);
33601         
33602     },
33603     
33604     initial : function()
33605     {
33606         if(this.files.length){
33607             this.indicator = 1;
33608             this.update()
33609         }
33610         
33611         this.fireEvent('initial', this);
33612     },
33613     
33614     update : function()
33615     {
33616         this.imageEl.attr('src', this.files[this.indicator - 1]);
33617         
33618         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33619         
33620         this.prevIndicator.show();
33621         
33622         if(this.indicator == 1){
33623             this.prevIndicator.hide();
33624         }
33625         
33626         this.nextIndicator.show();
33627         
33628         if(this.indicator == this.files.length){
33629             this.nextIndicator.hide();
33630         }
33631         
33632         this.thumbEl.scrollTo('top');
33633         
33634         this.fireEvent('update', this);
33635     },
33636     
33637     onClick : function(e)
33638     {
33639         e.preventDefault();
33640         
33641         this.fireEvent('click', this);
33642     },
33643     
33644     prev : function(e)
33645     {
33646         e.preventDefault();
33647         
33648         this.indicator = Math.max(1, this.indicator - 1);
33649         
33650         this.update();
33651     },
33652     
33653     next : function(e)
33654     {
33655         e.preventDefault();
33656         
33657         this.indicator = Math.min(this.files.length, this.indicator + 1);
33658         
33659         this.update();
33660     }
33661 });
33662 /*
33663  * - LGPL
33664  *
33665  * RadioSet
33666  *
33667  *
33668  */
33669
33670 /**
33671  * @class Roo.bootstrap.RadioSet
33672  * @extends Roo.bootstrap.Input
33673  * Bootstrap RadioSet class
33674  * @cfg {String} indicatorpos (left|right) default left
33675  * @cfg {Boolean} inline (true|false) inline the element (default true)
33676  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33677  * @constructor
33678  * Create a new RadioSet
33679  * @param {Object} config The config object
33680  */
33681
33682 Roo.bootstrap.RadioSet = function(config){
33683     
33684     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33685     
33686     this.radioes = [];
33687     
33688     Roo.bootstrap.RadioSet.register(this);
33689     
33690     this.addEvents({
33691         /**
33692         * @event check
33693         * Fires when the element is checked or unchecked.
33694         * @param {Roo.bootstrap.RadioSet} this This radio
33695         * @param {Roo.bootstrap.Radio} item The checked item
33696         */
33697        check : true,
33698        /**
33699         * @event click
33700         * Fires when the element is click.
33701         * @param {Roo.bootstrap.RadioSet} this This radio set
33702         * @param {Roo.bootstrap.Radio} item The checked item
33703         * @param {Roo.EventObject} e The event object
33704         */
33705        click : true
33706     });
33707     
33708 };
33709
33710 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33711
33712     radioes : false,
33713     
33714     inline : true,
33715     
33716     weight : '',
33717     
33718     indicatorpos : 'left',
33719     
33720     getAutoCreate : function()
33721     {
33722         var label = {
33723             tag : 'label',
33724             cls : 'roo-radio-set-label',
33725             cn : [
33726                 {
33727                     tag : 'span',
33728                     html : this.fieldLabel
33729                 }
33730             ]
33731         };
33732         
33733         if(this.indicatorpos == 'left'){
33734             label.cn.unshift({
33735                 tag : 'i',
33736                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33737                 tooltip : 'This field is required'
33738             });
33739         } else {
33740             label.cn.push({
33741                 tag : 'i',
33742                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33743                 tooltip : 'This field is required'
33744             });
33745         }
33746         
33747         var items = {
33748             tag : 'div',
33749             cls : 'roo-radio-set-items'
33750         };
33751         
33752         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33753         
33754         if (align === 'left' && this.fieldLabel.length) {
33755             
33756             items = {
33757                 cls : "roo-radio-set-right", 
33758                 cn: [
33759                     items
33760                 ]
33761             };
33762             
33763             if(this.labelWidth > 12){
33764                 label.style = "width: " + this.labelWidth + 'px';
33765             }
33766             
33767             if(this.labelWidth < 13 && this.labelmd == 0){
33768                 this.labelmd = this.labelWidth;
33769             }
33770             
33771             if(this.labellg > 0){
33772                 label.cls += ' col-lg-' + this.labellg;
33773                 items.cls += ' col-lg-' + (12 - this.labellg);
33774             }
33775             
33776             if(this.labelmd > 0){
33777                 label.cls += ' col-md-' + this.labelmd;
33778                 items.cls += ' col-md-' + (12 - this.labelmd);
33779             }
33780             
33781             if(this.labelsm > 0){
33782                 label.cls += ' col-sm-' + this.labelsm;
33783                 items.cls += ' col-sm-' + (12 - this.labelsm);
33784             }
33785             
33786             if(this.labelxs > 0){
33787                 label.cls += ' col-xs-' + this.labelxs;
33788                 items.cls += ' col-xs-' + (12 - this.labelxs);
33789             }
33790         }
33791         
33792         var cfg = {
33793             tag : 'div',
33794             cls : 'roo-radio-set',
33795             cn : [
33796                 {
33797                     tag : 'input',
33798                     cls : 'roo-radio-set-input',
33799                     type : 'hidden',
33800                     name : this.name,
33801                     value : this.value ? this.value :  ''
33802                 },
33803                 label,
33804                 items
33805             ]
33806         };
33807         
33808         if(this.weight.length){
33809             cfg.cls += ' roo-radio-' + this.weight;
33810         }
33811         
33812         if(this.inline) {
33813             cfg.cls += ' roo-radio-set-inline';
33814         }
33815         
33816         var settings=this;
33817         ['xs','sm','md','lg'].map(function(size){
33818             if (settings[size]) {
33819                 cfg.cls += ' col-' + size + '-' + settings[size];
33820             }
33821         });
33822         
33823         return cfg;
33824         
33825     },
33826
33827     initEvents : function()
33828     {
33829         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33830         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33831         
33832         if(!this.fieldLabel.length){
33833             this.labelEl.hide();
33834         }
33835         
33836         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33837         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33838         
33839         this.indicator = this.indicatorEl();
33840         
33841         if(this.indicator){
33842             this.indicator.addClass('invisible');
33843         }
33844         
33845         this.originalValue = this.getValue();
33846         
33847     },
33848     
33849     inputEl: function ()
33850     {
33851         return this.el.select('.roo-radio-set-input', true).first();
33852     },
33853     
33854     getChildContainer : function()
33855     {
33856         return this.itemsEl;
33857     },
33858     
33859     register : function(item)
33860     {
33861         this.radioes.push(item);
33862         
33863     },
33864     
33865     validate : function()
33866     {   
33867         if(this.getVisibilityEl().hasClass('hidden')){
33868             return true;
33869         }
33870         
33871         var valid = false;
33872         
33873         Roo.each(this.radioes, function(i){
33874             if(!i.checked){
33875                 return;
33876             }
33877             
33878             valid = true;
33879             return false;
33880         });
33881         
33882         if(this.allowBlank) {
33883             return true;
33884         }
33885         
33886         if(this.disabled || valid){
33887             this.markValid();
33888             return true;
33889         }
33890         
33891         this.markInvalid();
33892         return false;
33893         
33894     },
33895     
33896     markValid : function()
33897     {
33898         if(this.labelEl.isVisible(true)){
33899             this.indicatorEl().removeClass('visible');
33900             this.indicatorEl().addClass('invisible');
33901         }
33902         
33903         this.el.removeClass([this.invalidClass, this.validClass]);
33904         this.el.addClass(this.validClass);
33905         
33906         this.fireEvent('valid', this);
33907     },
33908     
33909     markInvalid : function(msg)
33910     {
33911         if(this.allowBlank || this.disabled){
33912             return;
33913         }
33914         
33915         if(this.labelEl.isVisible(true)){
33916             this.indicatorEl().removeClass('invisible');
33917             this.indicatorEl().addClass('visible');
33918         }
33919         
33920         this.el.removeClass([this.invalidClass, this.validClass]);
33921         this.el.addClass(this.invalidClass);
33922         
33923         this.fireEvent('invalid', this, msg);
33924         
33925     },
33926     
33927     setValue : function(v, suppressEvent)
33928     {   
33929         if(this.value === v){
33930             return;
33931         }
33932         
33933         this.value = v;
33934         
33935         if(this.rendered){
33936             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33937         }
33938         
33939         Roo.each(this.radioes, function(i){
33940             i.checked = false;
33941             i.el.removeClass('checked');
33942         });
33943         
33944         Roo.each(this.radioes, function(i){
33945             
33946             if(i.value === v || i.value.toString() === v.toString()){
33947                 i.checked = true;
33948                 i.el.addClass('checked');
33949                 
33950                 if(suppressEvent !== true){
33951                     this.fireEvent('check', this, i);
33952                 }
33953                 
33954                 return false;
33955             }
33956             
33957         }, this);
33958         
33959         this.validate();
33960     },
33961     
33962     clearInvalid : function(){
33963         
33964         if(!this.el || this.preventMark){
33965             return;
33966         }
33967         
33968         this.el.removeClass([this.invalidClass]);
33969         
33970         this.fireEvent('valid', this);
33971     }
33972     
33973 });
33974
33975 Roo.apply(Roo.bootstrap.RadioSet, {
33976     
33977     groups: {},
33978     
33979     register : function(set)
33980     {
33981         this.groups[set.name] = set;
33982     },
33983     
33984     get: function(name) 
33985     {
33986         if (typeof(this.groups[name]) == 'undefined') {
33987             return false;
33988         }
33989         
33990         return this.groups[name] ;
33991     }
33992     
33993 });
33994 /*
33995  * Based on:
33996  * Ext JS Library 1.1.1
33997  * Copyright(c) 2006-2007, Ext JS, LLC.
33998  *
33999  * Originally Released Under LGPL - original licence link has changed is not relivant.
34000  *
34001  * Fork - LGPL
34002  * <script type="text/javascript">
34003  */
34004
34005
34006 /**
34007  * @class Roo.bootstrap.SplitBar
34008  * @extends Roo.util.Observable
34009  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34010  * <br><br>
34011  * Usage:
34012  * <pre><code>
34013 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34014                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34015 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34016 split.minSize = 100;
34017 split.maxSize = 600;
34018 split.animate = true;
34019 split.on('moved', splitterMoved);
34020 </code></pre>
34021  * @constructor
34022  * Create a new SplitBar
34023  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34024  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34025  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34026  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34027                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34028                         position of the SplitBar).
34029  */
34030 Roo.bootstrap.SplitBar = function(cfg){
34031     
34032     /** @private */
34033     
34034     //{
34035     //  dragElement : elm
34036     //  resizingElement: el,
34037         // optional..
34038     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34039     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34040         // existingProxy ???
34041     //}
34042     
34043     this.el = Roo.get(cfg.dragElement, true);
34044     this.el.dom.unselectable = "on";
34045     /** @private */
34046     this.resizingEl = Roo.get(cfg.resizingElement, true);
34047
34048     /**
34049      * @private
34050      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34051      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34052      * @type Number
34053      */
34054     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34055     
34056     /**
34057      * The minimum size of the resizing element. (Defaults to 0)
34058      * @type Number
34059      */
34060     this.minSize = 0;
34061     
34062     /**
34063      * The maximum size of the resizing element. (Defaults to 2000)
34064      * @type Number
34065      */
34066     this.maxSize = 2000;
34067     
34068     /**
34069      * Whether to animate the transition to the new size
34070      * @type Boolean
34071      */
34072     this.animate = false;
34073     
34074     /**
34075      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34076      * @type Boolean
34077      */
34078     this.useShim = false;
34079     
34080     /** @private */
34081     this.shim = null;
34082     
34083     if(!cfg.existingProxy){
34084         /** @private */
34085         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34086     }else{
34087         this.proxy = Roo.get(cfg.existingProxy).dom;
34088     }
34089     /** @private */
34090     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34091     
34092     /** @private */
34093     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34094     
34095     /** @private */
34096     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34097     
34098     /** @private */
34099     this.dragSpecs = {};
34100     
34101     /**
34102      * @private The adapter to use to positon and resize elements
34103      */
34104     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34105     this.adapter.init(this);
34106     
34107     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34108         /** @private */
34109         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34110         this.el.addClass("roo-splitbar-h");
34111     }else{
34112         /** @private */
34113         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34114         this.el.addClass("roo-splitbar-v");
34115     }
34116     
34117     this.addEvents({
34118         /**
34119          * @event resize
34120          * Fires when the splitter is moved (alias for {@link #event-moved})
34121          * @param {Roo.bootstrap.SplitBar} this
34122          * @param {Number} newSize the new width or height
34123          */
34124         "resize" : true,
34125         /**
34126          * @event moved
34127          * Fires when the splitter is moved
34128          * @param {Roo.bootstrap.SplitBar} this
34129          * @param {Number} newSize the new width or height
34130          */
34131         "moved" : true,
34132         /**
34133          * @event beforeresize
34134          * Fires before the splitter is dragged
34135          * @param {Roo.bootstrap.SplitBar} this
34136          */
34137         "beforeresize" : true,
34138
34139         "beforeapply" : true
34140     });
34141
34142     Roo.util.Observable.call(this);
34143 };
34144
34145 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34146     onStartProxyDrag : function(x, y){
34147         this.fireEvent("beforeresize", this);
34148         if(!this.overlay){
34149             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34150             o.unselectable();
34151             o.enableDisplayMode("block");
34152             // all splitbars share the same overlay
34153             Roo.bootstrap.SplitBar.prototype.overlay = o;
34154         }
34155         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34156         this.overlay.show();
34157         Roo.get(this.proxy).setDisplayed("block");
34158         var size = this.adapter.getElementSize(this);
34159         this.activeMinSize = this.getMinimumSize();;
34160         this.activeMaxSize = this.getMaximumSize();;
34161         var c1 = size - this.activeMinSize;
34162         var c2 = Math.max(this.activeMaxSize - size, 0);
34163         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34164             this.dd.resetConstraints();
34165             this.dd.setXConstraint(
34166                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34167                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34168             );
34169             this.dd.setYConstraint(0, 0);
34170         }else{
34171             this.dd.resetConstraints();
34172             this.dd.setXConstraint(0, 0);
34173             this.dd.setYConstraint(
34174                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34175                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34176             );
34177          }
34178         this.dragSpecs.startSize = size;
34179         this.dragSpecs.startPoint = [x, y];
34180         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34181     },
34182     
34183     /** 
34184      * @private Called after the drag operation by the DDProxy
34185      */
34186     onEndProxyDrag : function(e){
34187         Roo.get(this.proxy).setDisplayed(false);
34188         var endPoint = Roo.lib.Event.getXY(e);
34189         if(this.overlay){
34190             this.overlay.hide();
34191         }
34192         var newSize;
34193         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34194             newSize = this.dragSpecs.startSize + 
34195                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34196                     endPoint[0] - this.dragSpecs.startPoint[0] :
34197                     this.dragSpecs.startPoint[0] - endPoint[0]
34198                 );
34199         }else{
34200             newSize = this.dragSpecs.startSize + 
34201                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34202                     endPoint[1] - this.dragSpecs.startPoint[1] :
34203                     this.dragSpecs.startPoint[1] - endPoint[1]
34204                 );
34205         }
34206         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34207         if(newSize != this.dragSpecs.startSize){
34208             if(this.fireEvent('beforeapply', this, newSize) !== false){
34209                 this.adapter.setElementSize(this, newSize);
34210                 this.fireEvent("moved", this, newSize);
34211                 this.fireEvent("resize", this, newSize);
34212             }
34213         }
34214     },
34215     
34216     /**
34217      * Get the adapter this SplitBar uses
34218      * @return The adapter object
34219      */
34220     getAdapter : function(){
34221         return this.adapter;
34222     },
34223     
34224     /**
34225      * Set the adapter this SplitBar uses
34226      * @param {Object} adapter A SplitBar adapter object
34227      */
34228     setAdapter : function(adapter){
34229         this.adapter = adapter;
34230         this.adapter.init(this);
34231     },
34232     
34233     /**
34234      * Gets the minimum size for the resizing element
34235      * @return {Number} The minimum size
34236      */
34237     getMinimumSize : function(){
34238         return this.minSize;
34239     },
34240     
34241     /**
34242      * Sets the minimum size for the resizing element
34243      * @param {Number} minSize The minimum size
34244      */
34245     setMinimumSize : function(minSize){
34246         this.minSize = minSize;
34247     },
34248     
34249     /**
34250      * Gets the maximum size for the resizing element
34251      * @return {Number} The maximum size
34252      */
34253     getMaximumSize : function(){
34254         return this.maxSize;
34255     },
34256     
34257     /**
34258      * Sets the maximum size for the resizing element
34259      * @param {Number} maxSize The maximum size
34260      */
34261     setMaximumSize : function(maxSize){
34262         this.maxSize = maxSize;
34263     },
34264     
34265     /**
34266      * Sets the initialize size for the resizing element
34267      * @param {Number} size The initial size
34268      */
34269     setCurrentSize : function(size){
34270         var oldAnimate = this.animate;
34271         this.animate = false;
34272         this.adapter.setElementSize(this, size);
34273         this.animate = oldAnimate;
34274     },
34275     
34276     /**
34277      * Destroy this splitbar. 
34278      * @param {Boolean} removeEl True to remove the element
34279      */
34280     destroy : function(removeEl){
34281         if(this.shim){
34282             this.shim.remove();
34283         }
34284         this.dd.unreg();
34285         this.proxy.parentNode.removeChild(this.proxy);
34286         if(removeEl){
34287             this.el.remove();
34288         }
34289     }
34290 });
34291
34292 /**
34293  * @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.
34294  */
34295 Roo.bootstrap.SplitBar.createProxy = function(dir){
34296     var proxy = new Roo.Element(document.createElement("div"));
34297     proxy.unselectable();
34298     var cls = 'roo-splitbar-proxy';
34299     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34300     document.body.appendChild(proxy.dom);
34301     return proxy.dom;
34302 };
34303
34304 /** 
34305  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34306  * Default Adapter. It assumes the splitter and resizing element are not positioned
34307  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34308  */
34309 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34310 };
34311
34312 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34313     // do nothing for now
34314     init : function(s){
34315     
34316     },
34317     /**
34318      * Called before drag operations to get the current size of the resizing element. 
34319      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34320      */
34321      getElementSize : function(s){
34322         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34323             return s.resizingEl.getWidth();
34324         }else{
34325             return s.resizingEl.getHeight();
34326         }
34327     },
34328     
34329     /**
34330      * Called after drag operations to set the size of the resizing element.
34331      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34332      * @param {Number} newSize The new size to set
34333      * @param {Function} onComplete A function to be invoked when resizing is complete
34334      */
34335     setElementSize : function(s, newSize, onComplete){
34336         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34337             if(!s.animate){
34338                 s.resizingEl.setWidth(newSize);
34339                 if(onComplete){
34340                     onComplete(s, newSize);
34341                 }
34342             }else{
34343                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34344             }
34345         }else{
34346             
34347             if(!s.animate){
34348                 s.resizingEl.setHeight(newSize);
34349                 if(onComplete){
34350                     onComplete(s, newSize);
34351                 }
34352             }else{
34353                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34354             }
34355         }
34356     }
34357 };
34358
34359 /** 
34360  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34361  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34362  * Adapter that  moves the splitter element to align with the resized sizing element. 
34363  * Used with an absolute positioned SplitBar.
34364  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34365  * document.body, make sure you assign an id to the body element.
34366  */
34367 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34368     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34369     this.container = Roo.get(container);
34370 };
34371
34372 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34373     init : function(s){
34374         this.basic.init(s);
34375     },
34376     
34377     getElementSize : function(s){
34378         return this.basic.getElementSize(s);
34379     },
34380     
34381     setElementSize : function(s, newSize, onComplete){
34382         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34383     },
34384     
34385     moveSplitter : function(s){
34386         var yes = Roo.bootstrap.SplitBar;
34387         switch(s.placement){
34388             case yes.LEFT:
34389                 s.el.setX(s.resizingEl.getRight());
34390                 break;
34391             case yes.RIGHT:
34392                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34393                 break;
34394             case yes.TOP:
34395                 s.el.setY(s.resizingEl.getBottom());
34396                 break;
34397             case yes.BOTTOM:
34398                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34399                 break;
34400         }
34401     }
34402 };
34403
34404 /**
34405  * Orientation constant - Create a vertical SplitBar
34406  * @static
34407  * @type Number
34408  */
34409 Roo.bootstrap.SplitBar.VERTICAL = 1;
34410
34411 /**
34412  * Orientation constant - Create a horizontal SplitBar
34413  * @static
34414  * @type Number
34415  */
34416 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34417
34418 /**
34419  * Placement constant - The resizing element is to the left of the splitter element
34420  * @static
34421  * @type Number
34422  */
34423 Roo.bootstrap.SplitBar.LEFT = 1;
34424
34425 /**
34426  * Placement constant - The resizing element is to the right of the splitter element
34427  * @static
34428  * @type Number
34429  */
34430 Roo.bootstrap.SplitBar.RIGHT = 2;
34431
34432 /**
34433  * Placement constant - The resizing element is positioned above the splitter element
34434  * @static
34435  * @type Number
34436  */
34437 Roo.bootstrap.SplitBar.TOP = 3;
34438
34439 /**
34440  * Placement constant - The resizing element is positioned under splitter element
34441  * @static
34442  * @type Number
34443  */
34444 Roo.bootstrap.SplitBar.BOTTOM = 4;
34445 Roo.namespace("Roo.bootstrap.layout");/*
34446  * Based on:
34447  * Ext JS Library 1.1.1
34448  * Copyright(c) 2006-2007, Ext JS, LLC.
34449  *
34450  * Originally Released Under LGPL - original licence link has changed is not relivant.
34451  *
34452  * Fork - LGPL
34453  * <script type="text/javascript">
34454  */
34455
34456 /**
34457  * @class Roo.bootstrap.layout.Manager
34458  * @extends Roo.bootstrap.Component
34459  * Base class for layout managers.
34460  */
34461 Roo.bootstrap.layout.Manager = function(config)
34462 {
34463     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34464
34465
34466
34467
34468
34469     /** false to disable window resize monitoring @type Boolean */
34470     this.monitorWindowResize = true;
34471     this.regions = {};
34472     this.addEvents({
34473         /**
34474          * @event layout
34475          * Fires when a layout is performed.
34476          * @param {Roo.LayoutManager} this
34477          */
34478         "layout" : true,
34479         /**
34480          * @event regionresized
34481          * Fires when the user resizes a region.
34482          * @param {Roo.LayoutRegion} region The resized region
34483          * @param {Number} newSize The new size (width for east/west, height for north/south)
34484          */
34485         "regionresized" : true,
34486         /**
34487          * @event regioncollapsed
34488          * Fires when a region is collapsed.
34489          * @param {Roo.LayoutRegion} region The collapsed region
34490          */
34491         "regioncollapsed" : true,
34492         /**
34493          * @event regionexpanded
34494          * Fires when a region is expanded.
34495          * @param {Roo.LayoutRegion} region The expanded region
34496          */
34497         "regionexpanded" : true
34498     });
34499     this.updating = false;
34500
34501     if (config.el) {
34502         this.el = Roo.get(config.el);
34503         this.initEvents();
34504     }
34505
34506 };
34507
34508 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34509
34510
34511     regions : null,
34512
34513     monitorWindowResize : true,
34514
34515
34516     updating : false,
34517
34518
34519     onRender : function(ct, position)
34520     {
34521         if(!this.el){
34522             this.el = Roo.get(ct);
34523             this.initEvents();
34524         }
34525         //this.fireEvent('render',this);
34526     },
34527
34528
34529     initEvents: function()
34530     {
34531
34532
34533         // ie scrollbar fix
34534         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34535             document.body.scroll = "no";
34536         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34537             this.el.position('relative');
34538         }
34539         this.id = this.el.id;
34540         this.el.addClass("roo-layout-container");
34541         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34542         if(this.el.dom != document.body ) {
34543             this.el.on('resize', this.layout,this);
34544             this.el.on('show', this.layout,this);
34545         }
34546
34547     },
34548
34549     /**
34550      * Returns true if this layout is currently being updated
34551      * @return {Boolean}
34552      */
34553     isUpdating : function(){
34554         return this.updating;
34555     },
34556
34557     /**
34558      * Suspend the LayoutManager from doing auto-layouts while
34559      * making multiple add or remove calls
34560      */
34561     beginUpdate : function(){
34562         this.updating = true;
34563     },
34564
34565     /**
34566      * Restore auto-layouts and optionally disable the manager from performing a layout
34567      * @param {Boolean} noLayout true to disable a layout update
34568      */
34569     endUpdate : function(noLayout){
34570         this.updating = false;
34571         if(!noLayout){
34572             this.layout();
34573         }
34574     },
34575
34576     layout: function(){
34577         // abstract...
34578     },
34579
34580     onRegionResized : function(region, newSize){
34581         this.fireEvent("regionresized", region, newSize);
34582         this.layout();
34583     },
34584
34585     onRegionCollapsed : function(region){
34586         this.fireEvent("regioncollapsed", region);
34587     },
34588
34589     onRegionExpanded : function(region){
34590         this.fireEvent("regionexpanded", region);
34591     },
34592
34593     /**
34594      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34595      * performs box-model adjustments.
34596      * @return {Object} The size as an object {width: (the width), height: (the height)}
34597      */
34598     getViewSize : function()
34599     {
34600         var size;
34601         if(this.el.dom != document.body){
34602             size = this.el.getSize();
34603         }else{
34604             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34605         }
34606         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34607         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34608         return size;
34609     },
34610
34611     /**
34612      * Returns the Element this layout is bound to.
34613      * @return {Roo.Element}
34614      */
34615     getEl : function(){
34616         return this.el;
34617     },
34618
34619     /**
34620      * Returns the specified region.
34621      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34622      * @return {Roo.LayoutRegion}
34623      */
34624     getRegion : function(target){
34625         return this.regions[target.toLowerCase()];
34626     },
34627
34628     onWindowResize : function(){
34629         if(this.monitorWindowResize){
34630             this.layout();
34631         }
34632     }
34633 });
34634 /*
34635  * Based on:
34636  * Ext JS Library 1.1.1
34637  * Copyright(c) 2006-2007, Ext JS, LLC.
34638  *
34639  * Originally Released Under LGPL - original licence link has changed is not relivant.
34640  *
34641  * Fork - LGPL
34642  * <script type="text/javascript">
34643  */
34644 /**
34645  * @class Roo.bootstrap.layout.Border
34646  * @extends Roo.bootstrap.layout.Manager
34647  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34648  * please see: examples/bootstrap/nested.html<br><br>
34649  
34650 <b>The container the layout is rendered into can be either the body element or any other element.
34651 If it is not the body element, the container needs to either be an absolute positioned element,
34652 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34653 the container size if it is not the body element.</b>
34654
34655 * @constructor
34656 * Create a new Border
34657 * @param {Object} config Configuration options
34658  */
34659 Roo.bootstrap.layout.Border = function(config){
34660     config = config || {};
34661     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34662     
34663     
34664     
34665     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34666         if(config[region]){
34667             config[region].region = region;
34668             this.addRegion(config[region]);
34669         }
34670     },this);
34671     
34672 };
34673
34674 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34675
34676 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34677     /**
34678      * Creates and adds a new region if it doesn't already exist.
34679      * @param {String} target The target region key (north, south, east, west or center).
34680      * @param {Object} config The regions config object
34681      * @return {BorderLayoutRegion} The new region
34682      */
34683     addRegion : function(config)
34684     {
34685         if(!this.regions[config.region]){
34686             var r = this.factory(config);
34687             this.bindRegion(r);
34688         }
34689         return this.regions[config.region];
34690     },
34691
34692     // private (kinda)
34693     bindRegion : function(r){
34694         this.regions[r.config.region] = r;
34695         
34696         r.on("visibilitychange",    this.layout, this);
34697         r.on("paneladded",          this.layout, this);
34698         r.on("panelremoved",        this.layout, this);
34699         r.on("invalidated",         this.layout, this);
34700         r.on("resized",             this.onRegionResized, this);
34701         r.on("collapsed",           this.onRegionCollapsed, this);
34702         r.on("expanded",            this.onRegionExpanded, this);
34703     },
34704
34705     /**
34706      * Performs a layout update.
34707      */
34708     layout : function()
34709     {
34710         if(this.updating) {
34711             return;
34712         }
34713         
34714         // render all the rebions if they have not been done alreayd?
34715         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34716             if(this.regions[region] && !this.regions[region].bodyEl){
34717                 this.regions[region].onRender(this.el)
34718             }
34719         },this);
34720         
34721         var size = this.getViewSize();
34722         var w = size.width;
34723         var h = size.height;
34724         var centerW = w;
34725         var centerH = h;
34726         var centerY = 0;
34727         var centerX = 0;
34728         //var x = 0, y = 0;
34729
34730         var rs = this.regions;
34731         var north = rs["north"];
34732         var south = rs["south"]; 
34733         var west = rs["west"];
34734         var east = rs["east"];
34735         var center = rs["center"];
34736         //if(this.hideOnLayout){ // not supported anymore
34737             //c.el.setStyle("display", "none");
34738         //}
34739         if(north && north.isVisible()){
34740             var b = north.getBox();
34741             var m = north.getMargins();
34742             b.width = w - (m.left+m.right);
34743             b.x = m.left;
34744             b.y = m.top;
34745             centerY = b.height + b.y + m.bottom;
34746             centerH -= centerY;
34747             north.updateBox(this.safeBox(b));
34748         }
34749         if(south && south.isVisible()){
34750             var b = south.getBox();
34751             var m = south.getMargins();
34752             b.width = w - (m.left+m.right);
34753             b.x = m.left;
34754             var totalHeight = (b.height + m.top + m.bottom);
34755             b.y = h - totalHeight + m.top;
34756             centerH -= totalHeight;
34757             south.updateBox(this.safeBox(b));
34758         }
34759         if(west && west.isVisible()){
34760             var b = west.getBox();
34761             var m = west.getMargins();
34762             b.height = centerH - (m.top+m.bottom);
34763             b.x = m.left;
34764             b.y = centerY + m.top;
34765             var totalWidth = (b.width + m.left + m.right);
34766             centerX += totalWidth;
34767             centerW -= totalWidth;
34768             west.updateBox(this.safeBox(b));
34769         }
34770         if(east && east.isVisible()){
34771             var b = east.getBox();
34772             var m = east.getMargins();
34773             b.height = centerH - (m.top+m.bottom);
34774             var totalWidth = (b.width + m.left + m.right);
34775             b.x = w - totalWidth + m.left;
34776             b.y = centerY + m.top;
34777             centerW -= totalWidth;
34778             east.updateBox(this.safeBox(b));
34779         }
34780         if(center){
34781             var m = center.getMargins();
34782             var centerBox = {
34783                 x: centerX + m.left,
34784                 y: centerY + m.top,
34785                 width: centerW - (m.left+m.right),
34786                 height: centerH - (m.top+m.bottom)
34787             };
34788             //if(this.hideOnLayout){
34789                 //center.el.setStyle("display", "block");
34790             //}
34791             center.updateBox(this.safeBox(centerBox));
34792         }
34793         this.el.repaint();
34794         this.fireEvent("layout", this);
34795     },
34796
34797     // private
34798     safeBox : function(box){
34799         box.width = Math.max(0, box.width);
34800         box.height = Math.max(0, box.height);
34801         return box;
34802     },
34803
34804     /**
34805      * Adds a ContentPanel (or subclass) to this layout.
34806      * @param {String} target The target region key (north, south, east, west or center).
34807      * @param {Roo.ContentPanel} panel The panel to add
34808      * @return {Roo.ContentPanel} The added panel
34809      */
34810     add : function(target, panel){
34811          
34812         target = target.toLowerCase();
34813         return this.regions[target].add(panel);
34814     },
34815
34816     /**
34817      * Remove a ContentPanel (or subclass) to this layout.
34818      * @param {String} target The target region key (north, south, east, west or center).
34819      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34820      * @return {Roo.ContentPanel} The removed panel
34821      */
34822     remove : function(target, panel){
34823         target = target.toLowerCase();
34824         return this.regions[target].remove(panel);
34825     },
34826
34827     /**
34828      * Searches all regions for a panel with the specified id
34829      * @param {String} panelId
34830      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34831      */
34832     findPanel : function(panelId){
34833         var rs = this.regions;
34834         for(var target in rs){
34835             if(typeof rs[target] != "function"){
34836                 var p = rs[target].getPanel(panelId);
34837                 if(p){
34838                     return p;
34839                 }
34840             }
34841         }
34842         return null;
34843     },
34844
34845     /**
34846      * Searches all regions for a panel with the specified id and activates (shows) it.
34847      * @param {String/ContentPanel} panelId The panels id or the panel itself
34848      * @return {Roo.ContentPanel} The shown panel or null
34849      */
34850     showPanel : function(panelId) {
34851       var rs = this.regions;
34852       for(var target in rs){
34853          var r = rs[target];
34854          if(typeof r != "function"){
34855             if(r.hasPanel(panelId)){
34856                return r.showPanel(panelId);
34857             }
34858          }
34859       }
34860       return null;
34861    },
34862
34863    /**
34864      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34865      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34866      */
34867    /*
34868     restoreState : function(provider){
34869         if(!provider){
34870             provider = Roo.state.Manager;
34871         }
34872         var sm = new Roo.LayoutStateManager();
34873         sm.init(this, provider);
34874     },
34875 */
34876  
34877  
34878     /**
34879      * Adds a xtype elements to the layout.
34880      * <pre><code>
34881
34882 layout.addxtype({
34883        xtype : 'ContentPanel',
34884        region: 'west',
34885        items: [ .... ]
34886    }
34887 );
34888
34889 layout.addxtype({
34890         xtype : 'NestedLayoutPanel',
34891         region: 'west',
34892         layout: {
34893            center: { },
34894            west: { }   
34895         },
34896         items : [ ... list of content panels or nested layout panels.. ]
34897    }
34898 );
34899 </code></pre>
34900      * @param {Object} cfg Xtype definition of item to add.
34901      */
34902     addxtype : function(cfg)
34903     {
34904         // basically accepts a pannel...
34905         // can accept a layout region..!?!?
34906         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34907         
34908         
34909         // theory?  children can only be panels??
34910         
34911         //if (!cfg.xtype.match(/Panel$/)) {
34912         //    return false;
34913         //}
34914         var ret = false;
34915         
34916         if (typeof(cfg.region) == 'undefined') {
34917             Roo.log("Failed to add Panel, region was not set");
34918             Roo.log(cfg);
34919             return false;
34920         }
34921         var region = cfg.region;
34922         delete cfg.region;
34923         
34924           
34925         var xitems = [];
34926         if (cfg.items) {
34927             xitems = cfg.items;
34928             delete cfg.items;
34929         }
34930         var nb = false;
34931         
34932         switch(cfg.xtype) 
34933         {
34934             case 'Content':  // ContentPanel (el, cfg)
34935             case 'Scroll':  // ContentPanel (el, cfg)
34936             case 'View': 
34937                 cfg.autoCreate = true;
34938                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34939                 //} else {
34940                 //    var el = this.el.createChild();
34941                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34942                 //}
34943                 
34944                 this.add(region, ret);
34945                 break;
34946             
34947             /*
34948             case 'TreePanel': // our new panel!
34949                 cfg.el = this.el.createChild();
34950                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34951                 this.add(region, ret);
34952                 break;
34953             */
34954             
34955             case 'Nest': 
34956                 // create a new Layout (which is  a Border Layout...
34957                 
34958                 var clayout = cfg.layout;
34959                 clayout.el  = this.el.createChild();
34960                 clayout.items   = clayout.items  || [];
34961                 
34962                 delete cfg.layout;
34963                 
34964                 // replace this exitems with the clayout ones..
34965                 xitems = clayout.items;
34966                  
34967                 // force background off if it's in center...
34968                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34969                     cfg.background = false;
34970                 }
34971                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34972                 
34973                 
34974                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34975                 //console.log('adding nested layout panel '  + cfg.toSource());
34976                 this.add(region, ret);
34977                 nb = {}; /// find first...
34978                 break;
34979             
34980             case 'Grid':
34981                 
34982                 // needs grid and region
34983                 
34984                 //var el = this.getRegion(region).el.createChild();
34985                 /*
34986                  *var el = this.el.createChild();
34987                 // create the grid first...
34988                 cfg.grid.container = el;
34989                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34990                 */
34991                 
34992                 if (region == 'center' && this.active ) {
34993                     cfg.background = false;
34994                 }
34995                 
34996                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34997                 
34998                 this.add(region, ret);
34999                 /*
35000                 if (cfg.background) {
35001                     // render grid on panel activation (if panel background)
35002                     ret.on('activate', function(gp) {
35003                         if (!gp.grid.rendered) {
35004                     //        gp.grid.render(el);
35005                         }
35006                     });
35007                 } else {
35008                   //  cfg.grid.render(el);
35009                 }
35010                 */
35011                 break;
35012            
35013            
35014             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35015                 // it was the old xcomponent building that caused this before.
35016                 // espeically if border is the top element in the tree.
35017                 ret = this;
35018                 break; 
35019                 
35020                     
35021                 
35022                 
35023                 
35024             default:
35025                 /*
35026                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35027                     
35028                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35029                     this.add(region, ret);
35030                 } else {
35031                 */
35032                     Roo.log(cfg);
35033                     throw "Can not add '" + cfg.xtype + "' to Border";
35034                     return null;
35035              
35036                                 
35037              
35038         }
35039         this.beginUpdate();
35040         // add children..
35041         var region = '';
35042         var abn = {};
35043         Roo.each(xitems, function(i)  {
35044             region = nb && i.region ? i.region : false;
35045             
35046             var add = ret.addxtype(i);
35047            
35048             if (region) {
35049                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35050                 if (!i.background) {
35051                     abn[region] = nb[region] ;
35052                 }
35053             }
35054             
35055         });
35056         this.endUpdate();
35057
35058         // make the last non-background panel active..
35059         //if (nb) { Roo.log(abn); }
35060         if (nb) {
35061             
35062             for(var r in abn) {
35063                 region = this.getRegion(r);
35064                 if (region) {
35065                     // tried using nb[r], but it does not work..
35066                      
35067                     region.showPanel(abn[r]);
35068                    
35069                 }
35070             }
35071         }
35072         return ret;
35073         
35074     },
35075     
35076     
35077 // private
35078     factory : function(cfg)
35079     {
35080         
35081         var validRegions = Roo.bootstrap.layout.Border.regions;
35082
35083         var target = cfg.region;
35084         cfg.mgr = this;
35085         
35086         var r = Roo.bootstrap.layout;
35087         Roo.log(target);
35088         switch(target){
35089             case "north":
35090                 return new r.North(cfg);
35091             case "south":
35092                 return new r.South(cfg);
35093             case "east":
35094                 return new r.East(cfg);
35095             case "west":
35096                 return new r.West(cfg);
35097             case "center":
35098                 return new r.Center(cfg);
35099         }
35100         throw 'Layout region "'+target+'" not supported.';
35101     }
35102     
35103     
35104 });
35105  /*
35106  * Based on:
35107  * Ext JS Library 1.1.1
35108  * Copyright(c) 2006-2007, Ext JS, LLC.
35109  *
35110  * Originally Released Under LGPL - original licence link has changed is not relivant.
35111  *
35112  * Fork - LGPL
35113  * <script type="text/javascript">
35114  */
35115  
35116 /**
35117  * @class Roo.bootstrap.layout.Basic
35118  * @extends Roo.util.Observable
35119  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35120  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35121  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35122  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35123  * @cfg {string}   region  the region that it inhabits..
35124  * @cfg {bool}   skipConfig skip config?
35125  * 
35126
35127  */
35128 Roo.bootstrap.layout.Basic = function(config){
35129     
35130     this.mgr = config.mgr;
35131     
35132     this.position = config.region;
35133     
35134     var skipConfig = config.skipConfig;
35135     
35136     this.events = {
35137         /**
35138          * @scope Roo.BasicLayoutRegion
35139          */
35140         
35141         /**
35142          * @event beforeremove
35143          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35144          * @param {Roo.LayoutRegion} this
35145          * @param {Roo.ContentPanel} panel The panel
35146          * @param {Object} e The cancel event object
35147          */
35148         "beforeremove" : true,
35149         /**
35150          * @event invalidated
35151          * Fires when the layout for this region is changed.
35152          * @param {Roo.LayoutRegion} this
35153          */
35154         "invalidated" : true,
35155         /**
35156          * @event visibilitychange
35157          * Fires when this region is shown or hidden 
35158          * @param {Roo.LayoutRegion} this
35159          * @param {Boolean} visibility true or false
35160          */
35161         "visibilitychange" : true,
35162         /**
35163          * @event paneladded
35164          * Fires when a panel is added. 
35165          * @param {Roo.LayoutRegion} this
35166          * @param {Roo.ContentPanel} panel The panel
35167          */
35168         "paneladded" : true,
35169         /**
35170          * @event panelremoved
35171          * Fires when a panel is removed. 
35172          * @param {Roo.LayoutRegion} this
35173          * @param {Roo.ContentPanel} panel The panel
35174          */
35175         "panelremoved" : true,
35176         /**
35177          * @event beforecollapse
35178          * Fires when this region before collapse.
35179          * @param {Roo.LayoutRegion} this
35180          */
35181         "beforecollapse" : true,
35182         /**
35183          * @event collapsed
35184          * Fires when this region is collapsed.
35185          * @param {Roo.LayoutRegion} this
35186          */
35187         "collapsed" : true,
35188         /**
35189          * @event expanded
35190          * Fires when this region is expanded.
35191          * @param {Roo.LayoutRegion} this
35192          */
35193         "expanded" : true,
35194         /**
35195          * @event slideshow
35196          * Fires when this region is slid into view.
35197          * @param {Roo.LayoutRegion} this
35198          */
35199         "slideshow" : true,
35200         /**
35201          * @event slidehide
35202          * Fires when this region slides out of view. 
35203          * @param {Roo.LayoutRegion} this
35204          */
35205         "slidehide" : true,
35206         /**
35207          * @event panelactivated
35208          * Fires when a panel is activated. 
35209          * @param {Roo.LayoutRegion} this
35210          * @param {Roo.ContentPanel} panel The activated panel
35211          */
35212         "panelactivated" : true,
35213         /**
35214          * @event resized
35215          * Fires when the user resizes this region. 
35216          * @param {Roo.LayoutRegion} this
35217          * @param {Number} newSize The new size (width for east/west, height for north/south)
35218          */
35219         "resized" : true
35220     };
35221     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35222     this.panels = new Roo.util.MixedCollection();
35223     this.panels.getKey = this.getPanelId.createDelegate(this);
35224     this.box = null;
35225     this.activePanel = null;
35226     // ensure listeners are added...
35227     
35228     if (config.listeners || config.events) {
35229         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35230             listeners : config.listeners || {},
35231             events : config.events || {}
35232         });
35233     }
35234     
35235     if(skipConfig !== true){
35236         this.applyConfig(config);
35237     }
35238 };
35239
35240 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35241 {
35242     getPanelId : function(p){
35243         return p.getId();
35244     },
35245     
35246     applyConfig : function(config){
35247         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35248         this.config = config;
35249         
35250     },
35251     
35252     /**
35253      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35254      * the width, for horizontal (north, south) the height.
35255      * @param {Number} newSize The new width or height
35256      */
35257     resizeTo : function(newSize){
35258         var el = this.el ? this.el :
35259                  (this.activePanel ? this.activePanel.getEl() : null);
35260         if(el){
35261             switch(this.position){
35262                 case "east":
35263                 case "west":
35264                     el.setWidth(newSize);
35265                     this.fireEvent("resized", this, newSize);
35266                 break;
35267                 case "north":
35268                 case "south":
35269                     el.setHeight(newSize);
35270                     this.fireEvent("resized", this, newSize);
35271                 break;                
35272             }
35273         }
35274     },
35275     
35276     getBox : function(){
35277         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35278     },
35279     
35280     getMargins : function(){
35281         return this.margins;
35282     },
35283     
35284     updateBox : function(box){
35285         this.box = box;
35286         var el = this.activePanel.getEl();
35287         el.dom.style.left = box.x + "px";
35288         el.dom.style.top = box.y + "px";
35289         this.activePanel.setSize(box.width, box.height);
35290     },
35291     
35292     /**
35293      * Returns the container element for this region.
35294      * @return {Roo.Element}
35295      */
35296     getEl : function(){
35297         return this.activePanel;
35298     },
35299     
35300     /**
35301      * Returns true if this region is currently visible.
35302      * @return {Boolean}
35303      */
35304     isVisible : function(){
35305         return this.activePanel ? true : false;
35306     },
35307     
35308     setActivePanel : function(panel){
35309         panel = this.getPanel(panel);
35310         if(this.activePanel && this.activePanel != panel){
35311             this.activePanel.setActiveState(false);
35312             this.activePanel.getEl().setLeftTop(-10000,-10000);
35313         }
35314         this.activePanel = panel;
35315         panel.setActiveState(true);
35316         if(this.box){
35317             panel.setSize(this.box.width, this.box.height);
35318         }
35319         this.fireEvent("panelactivated", this, panel);
35320         this.fireEvent("invalidated");
35321     },
35322     
35323     /**
35324      * Show the specified panel.
35325      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35326      * @return {Roo.ContentPanel} The shown panel or null
35327      */
35328     showPanel : function(panel){
35329         panel = this.getPanel(panel);
35330         if(panel){
35331             this.setActivePanel(panel);
35332         }
35333         return panel;
35334     },
35335     
35336     /**
35337      * Get the active panel for this region.
35338      * @return {Roo.ContentPanel} The active panel or null
35339      */
35340     getActivePanel : function(){
35341         return this.activePanel;
35342     },
35343     
35344     /**
35345      * Add the passed ContentPanel(s)
35346      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35347      * @return {Roo.ContentPanel} The panel added (if only one was added)
35348      */
35349     add : function(panel){
35350         if(arguments.length > 1){
35351             for(var i = 0, len = arguments.length; i < len; i++) {
35352                 this.add(arguments[i]);
35353             }
35354             return null;
35355         }
35356         if(this.hasPanel(panel)){
35357             this.showPanel(panel);
35358             return panel;
35359         }
35360         var el = panel.getEl();
35361         if(el.dom.parentNode != this.mgr.el.dom){
35362             this.mgr.el.dom.appendChild(el.dom);
35363         }
35364         if(panel.setRegion){
35365             panel.setRegion(this);
35366         }
35367         this.panels.add(panel);
35368         el.setStyle("position", "absolute");
35369         if(!panel.background){
35370             this.setActivePanel(panel);
35371             if(this.config.initialSize && this.panels.getCount()==1){
35372                 this.resizeTo(this.config.initialSize);
35373             }
35374         }
35375         this.fireEvent("paneladded", this, panel);
35376         return panel;
35377     },
35378     
35379     /**
35380      * Returns true if the panel is in this region.
35381      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35382      * @return {Boolean}
35383      */
35384     hasPanel : function(panel){
35385         if(typeof panel == "object"){ // must be panel obj
35386             panel = panel.getId();
35387         }
35388         return this.getPanel(panel) ? true : false;
35389     },
35390     
35391     /**
35392      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35393      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35394      * @param {Boolean} preservePanel Overrides the config preservePanel option
35395      * @return {Roo.ContentPanel} The panel that was removed
35396      */
35397     remove : function(panel, preservePanel){
35398         panel = this.getPanel(panel);
35399         if(!panel){
35400             return null;
35401         }
35402         var e = {};
35403         this.fireEvent("beforeremove", this, panel, e);
35404         if(e.cancel === true){
35405             return null;
35406         }
35407         var panelId = panel.getId();
35408         this.panels.removeKey(panelId);
35409         return panel;
35410     },
35411     
35412     /**
35413      * Returns the panel specified or null if it's not in this region.
35414      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35415      * @return {Roo.ContentPanel}
35416      */
35417     getPanel : function(id){
35418         if(typeof id == "object"){ // must be panel obj
35419             return id;
35420         }
35421         return this.panels.get(id);
35422     },
35423     
35424     /**
35425      * Returns this regions position (north/south/east/west/center).
35426      * @return {String} 
35427      */
35428     getPosition: function(){
35429         return this.position;    
35430     }
35431 });/*
35432  * Based on:
35433  * Ext JS Library 1.1.1
35434  * Copyright(c) 2006-2007, Ext JS, LLC.
35435  *
35436  * Originally Released Under LGPL - original licence link has changed is not relivant.
35437  *
35438  * Fork - LGPL
35439  * <script type="text/javascript">
35440  */
35441  
35442 /**
35443  * @class Roo.bootstrap.layout.Region
35444  * @extends Roo.bootstrap.layout.Basic
35445  * This class represents a region in a layout manager.
35446  
35447  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35448  * @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})
35449  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35450  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35451  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35452  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35453  * @cfg {String}    title           The title for the region (overrides panel titles)
35454  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35455  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35456  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35457  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35458  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35459  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35460  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35461  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35462  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35463  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35464
35465  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35466  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35467  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35468  * @cfg {Number}    width           For East/West panels
35469  * @cfg {Number}    height          For North/South panels
35470  * @cfg {Boolean}   split           To show the splitter
35471  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35472  * 
35473  * @cfg {string}   cls             Extra CSS classes to add to region
35474  * 
35475  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35476  * @cfg {string}   region  the region that it inhabits..
35477  *
35478
35479  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35480  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35481
35482  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35483  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35484  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35485  */
35486 Roo.bootstrap.layout.Region = function(config)
35487 {
35488     this.applyConfig(config);
35489
35490     var mgr = config.mgr;
35491     var pos = config.region;
35492     config.skipConfig = true;
35493     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35494     
35495     if (mgr.el) {
35496         this.onRender(mgr.el);   
35497     }
35498      
35499     this.visible = true;
35500     this.collapsed = false;
35501     this.unrendered_panels = [];
35502 };
35503
35504 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35505
35506     position: '', // set by wrapper (eg. north/south etc..)
35507     unrendered_panels : null,  // unrendered panels.
35508     createBody : function(){
35509         /** This region's body element 
35510         * @type Roo.Element */
35511         this.bodyEl = this.el.createChild({
35512                 tag: "div",
35513                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35514         });
35515     },
35516
35517     onRender: function(ctr, pos)
35518     {
35519         var dh = Roo.DomHelper;
35520         /** This region's container element 
35521         * @type Roo.Element */
35522         this.el = dh.append(ctr.dom, {
35523                 tag: "div",
35524                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35525             }, true);
35526         /** This region's title element 
35527         * @type Roo.Element */
35528     
35529         this.titleEl = dh.append(this.el.dom,
35530             {
35531                     tag: "div",
35532                     unselectable: "on",
35533                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35534                     children:[
35535                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35536                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35537                     ]}, true);
35538         
35539         this.titleEl.enableDisplayMode();
35540         /** This region's title text element 
35541         * @type HTMLElement */
35542         this.titleTextEl = this.titleEl.dom.firstChild;
35543         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35544         /*
35545         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35546         this.closeBtn.enableDisplayMode();
35547         this.closeBtn.on("click", this.closeClicked, this);
35548         this.closeBtn.hide();
35549     */
35550         this.createBody(this.config);
35551         if(this.config.hideWhenEmpty){
35552             this.hide();
35553             this.on("paneladded", this.validateVisibility, this);
35554             this.on("panelremoved", this.validateVisibility, this);
35555         }
35556         if(this.autoScroll){
35557             this.bodyEl.setStyle("overflow", "auto");
35558         }else{
35559             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35560         }
35561         //if(c.titlebar !== false){
35562             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35563                 this.titleEl.hide();
35564             }else{
35565                 this.titleEl.show();
35566                 if(this.config.title){
35567                     this.titleTextEl.innerHTML = this.config.title;
35568                 }
35569             }
35570         //}
35571         if(this.config.collapsed){
35572             this.collapse(true);
35573         }
35574         if(this.config.hidden){
35575             this.hide();
35576         }
35577         
35578         if (this.unrendered_panels && this.unrendered_panels.length) {
35579             for (var i =0;i< this.unrendered_panels.length; i++) {
35580                 this.add(this.unrendered_panels[i]);
35581             }
35582             this.unrendered_panels = null;
35583             
35584         }
35585         
35586     },
35587     
35588     applyConfig : function(c)
35589     {
35590         /*
35591          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35592             var dh = Roo.DomHelper;
35593             if(c.titlebar !== false){
35594                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35595                 this.collapseBtn.on("click", this.collapse, this);
35596                 this.collapseBtn.enableDisplayMode();
35597                 /*
35598                 if(c.showPin === true || this.showPin){
35599                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35600                     this.stickBtn.enableDisplayMode();
35601                     this.stickBtn.on("click", this.expand, this);
35602                     this.stickBtn.hide();
35603                 }
35604                 
35605             }
35606             */
35607             /** This region's collapsed element
35608             * @type Roo.Element */
35609             /*
35610              *
35611             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35612                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35613             ]}, true);
35614             
35615             if(c.floatable !== false){
35616                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35617                this.collapsedEl.on("click", this.collapseClick, this);
35618             }
35619
35620             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35621                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35622                    id: "message", unselectable: "on", style:{"float":"left"}});
35623                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35624              }
35625             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35626             this.expandBtn.on("click", this.expand, this);
35627             
35628         }
35629         
35630         if(this.collapseBtn){
35631             this.collapseBtn.setVisible(c.collapsible == true);
35632         }
35633         
35634         this.cmargins = c.cmargins || this.cmargins ||
35635                          (this.position == "west" || this.position == "east" ?
35636                              {top: 0, left: 2, right:2, bottom: 0} :
35637                              {top: 2, left: 0, right:0, bottom: 2});
35638         */
35639         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35640         
35641         
35642         this.bottomTabs = c.tabPosition != "top";
35643         
35644         this.autoScroll = c.autoScroll || false;
35645         
35646         
35647        
35648         
35649         this.duration = c.duration || .30;
35650         this.slideDuration = c.slideDuration || .45;
35651         this.config = c;
35652        
35653     },
35654     /**
35655      * Returns true if this region is currently visible.
35656      * @return {Boolean}
35657      */
35658     isVisible : function(){
35659         return this.visible;
35660     },
35661
35662     /**
35663      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35664      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35665      */
35666     //setCollapsedTitle : function(title){
35667     //    title = title || "&#160;";
35668      //   if(this.collapsedTitleTextEl){
35669       //      this.collapsedTitleTextEl.innerHTML = title;
35670        // }
35671     //},
35672
35673     getBox : function(){
35674         var b;
35675       //  if(!this.collapsed){
35676             b = this.el.getBox(false, true);
35677        // }else{
35678           //  b = this.collapsedEl.getBox(false, true);
35679         //}
35680         return b;
35681     },
35682
35683     getMargins : function(){
35684         return this.margins;
35685         //return this.collapsed ? this.cmargins : this.margins;
35686     },
35687 /*
35688     highlight : function(){
35689         this.el.addClass("x-layout-panel-dragover");
35690     },
35691
35692     unhighlight : function(){
35693         this.el.removeClass("x-layout-panel-dragover");
35694     },
35695 */
35696     updateBox : function(box)
35697     {
35698         if (!this.bodyEl) {
35699             return; // not rendered yet..
35700         }
35701         
35702         this.box = box;
35703         if(!this.collapsed){
35704             this.el.dom.style.left = box.x + "px";
35705             this.el.dom.style.top = box.y + "px";
35706             this.updateBody(box.width, box.height);
35707         }else{
35708             this.collapsedEl.dom.style.left = box.x + "px";
35709             this.collapsedEl.dom.style.top = box.y + "px";
35710             this.collapsedEl.setSize(box.width, box.height);
35711         }
35712         if(this.tabs){
35713             this.tabs.autoSizeTabs();
35714         }
35715     },
35716
35717     updateBody : function(w, h)
35718     {
35719         if(w !== null){
35720             this.el.setWidth(w);
35721             w -= this.el.getBorderWidth("rl");
35722             if(this.config.adjustments){
35723                 w += this.config.adjustments[0];
35724             }
35725         }
35726         if(h !== null && h > 0){
35727             this.el.setHeight(h);
35728             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35729             h -= this.el.getBorderWidth("tb");
35730             if(this.config.adjustments){
35731                 h += this.config.adjustments[1];
35732             }
35733             this.bodyEl.setHeight(h);
35734             if(this.tabs){
35735                 h = this.tabs.syncHeight(h);
35736             }
35737         }
35738         if(this.panelSize){
35739             w = w !== null ? w : this.panelSize.width;
35740             h = h !== null ? h : this.panelSize.height;
35741         }
35742         if(this.activePanel){
35743             var el = this.activePanel.getEl();
35744             w = w !== null ? w : el.getWidth();
35745             h = h !== null ? h : el.getHeight();
35746             this.panelSize = {width: w, height: h};
35747             this.activePanel.setSize(w, h);
35748         }
35749         if(Roo.isIE && this.tabs){
35750             this.tabs.el.repaint();
35751         }
35752     },
35753
35754     /**
35755      * Returns the container element for this region.
35756      * @return {Roo.Element}
35757      */
35758     getEl : function(){
35759         return this.el;
35760     },
35761
35762     /**
35763      * Hides this region.
35764      */
35765     hide : function(){
35766         //if(!this.collapsed){
35767             this.el.dom.style.left = "-2000px";
35768             this.el.hide();
35769         //}else{
35770          //   this.collapsedEl.dom.style.left = "-2000px";
35771          //   this.collapsedEl.hide();
35772        // }
35773         this.visible = false;
35774         this.fireEvent("visibilitychange", this, false);
35775     },
35776
35777     /**
35778      * Shows this region if it was previously hidden.
35779      */
35780     show : function(){
35781         //if(!this.collapsed){
35782             this.el.show();
35783         //}else{
35784         //    this.collapsedEl.show();
35785        // }
35786         this.visible = true;
35787         this.fireEvent("visibilitychange", this, true);
35788     },
35789 /*
35790     closeClicked : function(){
35791         if(this.activePanel){
35792             this.remove(this.activePanel);
35793         }
35794     },
35795
35796     collapseClick : function(e){
35797         if(this.isSlid){
35798            e.stopPropagation();
35799            this.slideIn();
35800         }else{
35801            e.stopPropagation();
35802            this.slideOut();
35803         }
35804     },
35805 */
35806     /**
35807      * Collapses this region.
35808      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35809      */
35810     /*
35811     collapse : function(skipAnim, skipCheck = false){
35812         if(this.collapsed) {
35813             return;
35814         }
35815         
35816         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35817             
35818             this.collapsed = true;
35819             if(this.split){
35820                 this.split.el.hide();
35821             }
35822             if(this.config.animate && skipAnim !== true){
35823                 this.fireEvent("invalidated", this);
35824                 this.animateCollapse();
35825             }else{
35826                 this.el.setLocation(-20000,-20000);
35827                 this.el.hide();
35828                 this.collapsedEl.show();
35829                 this.fireEvent("collapsed", this);
35830                 this.fireEvent("invalidated", this);
35831             }
35832         }
35833         
35834     },
35835 */
35836     animateCollapse : function(){
35837         // overridden
35838     },
35839
35840     /**
35841      * Expands this region if it was previously collapsed.
35842      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35843      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35844      */
35845     /*
35846     expand : function(e, skipAnim){
35847         if(e) {
35848             e.stopPropagation();
35849         }
35850         if(!this.collapsed || this.el.hasActiveFx()) {
35851             return;
35852         }
35853         if(this.isSlid){
35854             this.afterSlideIn();
35855             skipAnim = true;
35856         }
35857         this.collapsed = false;
35858         if(this.config.animate && skipAnim !== true){
35859             this.animateExpand();
35860         }else{
35861             this.el.show();
35862             if(this.split){
35863                 this.split.el.show();
35864             }
35865             this.collapsedEl.setLocation(-2000,-2000);
35866             this.collapsedEl.hide();
35867             this.fireEvent("invalidated", this);
35868             this.fireEvent("expanded", this);
35869         }
35870     },
35871 */
35872     animateExpand : function(){
35873         // overridden
35874     },
35875
35876     initTabs : function()
35877     {
35878         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35879         
35880         var ts = new Roo.bootstrap.panel.Tabs({
35881                 el: this.bodyEl.dom,
35882                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35883                 disableTooltips: this.config.disableTabTips,
35884                 toolbar : this.config.toolbar
35885             });
35886         
35887         if(this.config.hideTabs){
35888             ts.stripWrap.setDisplayed(false);
35889         }
35890         this.tabs = ts;
35891         ts.resizeTabs = this.config.resizeTabs === true;
35892         ts.minTabWidth = this.config.minTabWidth || 40;
35893         ts.maxTabWidth = this.config.maxTabWidth || 250;
35894         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35895         ts.monitorResize = false;
35896         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35897         ts.bodyEl.addClass('roo-layout-tabs-body');
35898         this.panels.each(this.initPanelAsTab, this);
35899     },
35900
35901     initPanelAsTab : function(panel){
35902         var ti = this.tabs.addTab(
35903             panel.getEl().id,
35904             panel.getTitle(),
35905             null,
35906             this.config.closeOnTab && panel.isClosable(),
35907             panel.tpl
35908         );
35909         if(panel.tabTip !== undefined){
35910             ti.setTooltip(panel.tabTip);
35911         }
35912         ti.on("activate", function(){
35913               this.setActivePanel(panel);
35914         }, this);
35915         
35916         if(this.config.closeOnTab){
35917             ti.on("beforeclose", function(t, e){
35918                 e.cancel = true;
35919                 this.remove(panel);
35920             }, this);
35921         }
35922         
35923         panel.tabItem = ti;
35924         
35925         return ti;
35926     },
35927
35928     updatePanelTitle : function(panel, title)
35929     {
35930         if(this.activePanel == panel){
35931             this.updateTitle(title);
35932         }
35933         if(this.tabs){
35934             var ti = this.tabs.getTab(panel.getEl().id);
35935             ti.setText(title);
35936             if(panel.tabTip !== undefined){
35937                 ti.setTooltip(panel.tabTip);
35938             }
35939         }
35940     },
35941
35942     updateTitle : function(title){
35943         if(this.titleTextEl && !this.config.title){
35944             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35945         }
35946     },
35947
35948     setActivePanel : function(panel)
35949     {
35950         panel = this.getPanel(panel);
35951         if(this.activePanel && this.activePanel != panel){
35952             if(this.activePanel.setActiveState(false) === false){
35953                 return;
35954             }
35955         }
35956         this.activePanel = panel;
35957         panel.setActiveState(true);
35958         if(this.panelSize){
35959             panel.setSize(this.panelSize.width, this.panelSize.height);
35960         }
35961         if(this.closeBtn){
35962             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35963         }
35964         this.updateTitle(panel.getTitle());
35965         if(this.tabs){
35966             this.fireEvent("invalidated", this);
35967         }
35968         this.fireEvent("panelactivated", this, panel);
35969     },
35970
35971     /**
35972      * Shows the specified panel.
35973      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35974      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35975      */
35976     showPanel : function(panel)
35977     {
35978         panel = this.getPanel(panel);
35979         if(panel){
35980             if(this.tabs){
35981                 var tab = this.tabs.getTab(panel.getEl().id);
35982                 if(tab.isHidden()){
35983                     this.tabs.unhideTab(tab.id);
35984                 }
35985                 tab.activate();
35986             }else{
35987                 this.setActivePanel(panel);
35988             }
35989         }
35990         return panel;
35991     },
35992
35993     /**
35994      * Get the active panel for this region.
35995      * @return {Roo.ContentPanel} The active panel or null
35996      */
35997     getActivePanel : function(){
35998         return this.activePanel;
35999     },
36000
36001     validateVisibility : function(){
36002         if(this.panels.getCount() < 1){
36003             this.updateTitle("&#160;");
36004             this.closeBtn.hide();
36005             this.hide();
36006         }else{
36007             if(!this.isVisible()){
36008                 this.show();
36009             }
36010         }
36011     },
36012
36013     /**
36014      * Adds the passed ContentPanel(s) to this region.
36015      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36016      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36017      */
36018     add : function(panel)
36019     {
36020         if(arguments.length > 1){
36021             for(var i = 0, len = arguments.length; i < len; i++) {
36022                 this.add(arguments[i]);
36023             }
36024             return null;
36025         }
36026         
36027         // if we have not been rendered yet, then we can not really do much of this..
36028         if (!this.bodyEl) {
36029             this.unrendered_panels.push(panel);
36030             return panel;
36031         }
36032         
36033         
36034         
36035         
36036         if(this.hasPanel(panel)){
36037             this.showPanel(panel);
36038             return panel;
36039         }
36040         panel.setRegion(this);
36041         this.panels.add(panel);
36042        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36043             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36044             // and hide them... ???
36045             this.bodyEl.dom.appendChild(panel.getEl().dom);
36046             if(panel.background !== true){
36047                 this.setActivePanel(panel);
36048             }
36049             this.fireEvent("paneladded", this, panel);
36050             return panel;
36051         }
36052         */
36053         if(!this.tabs){
36054             this.initTabs();
36055         }else{
36056             this.initPanelAsTab(panel);
36057         }
36058         
36059         
36060         if(panel.background !== true){
36061             this.tabs.activate(panel.getEl().id);
36062         }
36063         this.fireEvent("paneladded", this, panel);
36064         return panel;
36065     },
36066
36067     /**
36068      * Hides the tab for the specified panel.
36069      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36070      */
36071     hidePanel : function(panel){
36072         if(this.tabs && (panel = this.getPanel(panel))){
36073             this.tabs.hideTab(panel.getEl().id);
36074         }
36075     },
36076
36077     /**
36078      * Unhides the tab for a previously hidden panel.
36079      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36080      */
36081     unhidePanel : function(panel){
36082         if(this.tabs && (panel = this.getPanel(panel))){
36083             this.tabs.unhideTab(panel.getEl().id);
36084         }
36085     },
36086
36087     clearPanels : function(){
36088         while(this.panels.getCount() > 0){
36089              this.remove(this.panels.first());
36090         }
36091     },
36092
36093     /**
36094      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36095      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36096      * @param {Boolean} preservePanel Overrides the config preservePanel option
36097      * @return {Roo.ContentPanel} The panel that was removed
36098      */
36099     remove : function(panel, preservePanel)
36100     {
36101         panel = this.getPanel(panel);
36102         if(!panel){
36103             return null;
36104         }
36105         var e = {};
36106         this.fireEvent("beforeremove", this, panel, e);
36107         if(e.cancel === true){
36108             return null;
36109         }
36110         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36111         var panelId = panel.getId();
36112         this.panels.removeKey(panelId);
36113         if(preservePanel){
36114             document.body.appendChild(panel.getEl().dom);
36115         }
36116         if(this.tabs){
36117             this.tabs.removeTab(panel.getEl().id);
36118         }else if (!preservePanel){
36119             this.bodyEl.dom.removeChild(panel.getEl().dom);
36120         }
36121         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36122             var p = this.panels.first();
36123             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36124             tempEl.appendChild(p.getEl().dom);
36125             this.bodyEl.update("");
36126             this.bodyEl.dom.appendChild(p.getEl().dom);
36127             tempEl = null;
36128             this.updateTitle(p.getTitle());
36129             this.tabs = null;
36130             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36131             this.setActivePanel(p);
36132         }
36133         panel.setRegion(null);
36134         if(this.activePanel == panel){
36135             this.activePanel = null;
36136         }
36137         if(this.config.autoDestroy !== false && preservePanel !== true){
36138             try{panel.destroy();}catch(e){}
36139         }
36140         this.fireEvent("panelremoved", this, panel);
36141         return panel;
36142     },
36143
36144     /**
36145      * Returns the TabPanel component used by this region
36146      * @return {Roo.TabPanel}
36147      */
36148     getTabs : function(){
36149         return this.tabs;
36150     },
36151
36152     createTool : function(parentEl, className){
36153         var btn = Roo.DomHelper.append(parentEl, {
36154             tag: "div",
36155             cls: "x-layout-tools-button",
36156             children: [ {
36157                 tag: "div",
36158                 cls: "roo-layout-tools-button-inner " + className,
36159                 html: "&#160;"
36160             }]
36161         }, true);
36162         btn.addClassOnOver("roo-layout-tools-button-over");
36163         return btn;
36164     }
36165 });/*
36166  * Based on:
36167  * Ext JS Library 1.1.1
36168  * Copyright(c) 2006-2007, Ext JS, LLC.
36169  *
36170  * Originally Released Under LGPL - original licence link has changed is not relivant.
36171  *
36172  * Fork - LGPL
36173  * <script type="text/javascript">
36174  */
36175  
36176
36177
36178 /**
36179  * @class Roo.SplitLayoutRegion
36180  * @extends Roo.LayoutRegion
36181  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36182  */
36183 Roo.bootstrap.layout.Split = function(config){
36184     this.cursor = config.cursor;
36185     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36186 };
36187
36188 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36189 {
36190     splitTip : "Drag to resize.",
36191     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36192     useSplitTips : false,
36193
36194     applyConfig : function(config){
36195         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36196     },
36197     
36198     onRender : function(ctr,pos) {
36199         
36200         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36201         if(!this.config.split){
36202             return;
36203         }
36204         if(!this.split){
36205             
36206             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36207                             tag: "div",
36208                             id: this.el.id + "-split",
36209                             cls: "roo-layout-split roo-layout-split-"+this.position,
36210                             html: "&#160;"
36211             });
36212             /** The SplitBar for this region 
36213             * @type Roo.SplitBar */
36214             // does not exist yet...
36215             Roo.log([this.position, this.orientation]);
36216             
36217             this.split = new Roo.bootstrap.SplitBar({
36218                 dragElement : splitEl,
36219                 resizingElement: this.el,
36220                 orientation : this.orientation
36221             });
36222             
36223             this.split.on("moved", this.onSplitMove, this);
36224             this.split.useShim = this.config.useShim === true;
36225             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36226             if(this.useSplitTips){
36227                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36228             }
36229             //if(config.collapsible){
36230             //    this.split.el.on("dblclick", this.collapse,  this);
36231             //}
36232         }
36233         if(typeof this.config.minSize != "undefined"){
36234             this.split.minSize = this.config.minSize;
36235         }
36236         if(typeof this.config.maxSize != "undefined"){
36237             this.split.maxSize = this.config.maxSize;
36238         }
36239         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36240             this.hideSplitter();
36241         }
36242         
36243     },
36244
36245     getHMaxSize : function(){
36246          var cmax = this.config.maxSize || 10000;
36247          var center = this.mgr.getRegion("center");
36248          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36249     },
36250
36251     getVMaxSize : function(){
36252          var cmax = this.config.maxSize || 10000;
36253          var center = this.mgr.getRegion("center");
36254          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36255     },
36256
36257     onSplitMove : function(split, newSize){
36258         this.fireEvent("resized", this, newSize);
36259     },
36260     
36261     /** 
36262      * Returns the {@link Roo.SplitBar} for this region.
36263      * @return {Roo.SplitBar}
36264      */
36265     getSplitBar : function(){
36266         return this.split;
36267     },
36268     
36269     hide : function(){
36270         this.hideSplitter();
36271         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36272     },
36273
36274     hideSplitter : function(){
36275         if(this.split){
36276             this.split.el.setLocation(-2000,-2000);
36277             this.split.el.hide();
36278         }
36279     },
36280
36281     show : function(){
36282         if(this.split){
36283             this.split.el.show();
36284         }
36285         Roo.bootstrap.layout.Split.superclass.show.call(this);
36286     },
36287     
36288     beforeSlide: function(){
36289         if(Roo.isGecko){// firefox overflow auto bug workaround
36290             this.bodyEl.clip();
36291             if(this.tabs) {
36292                 this.tabs.bodyEl.clip();
36293             }
36294             if(this.activePanel){
36295                 this.activePanel.getEl().clip();
36296                 
36297                 if(this.activePanel.beforeSlide){
36298                     this.activePanel.beforeSlide();
36299                 }
36300             }
36301         }
36302     },
36303     
36304     afterSlide : function(){
36305         if(Roo.isGecko){// firefox overflow auto bug workaround
36306             this.bodyEl.unclip();
36307             if(this.tabs) {
36308                 this.tabs.bodyEl.unclip();
36309             }
36310             if(this.activePanel){
36311                 this.activePanel.getEl().unclip();
36312                 if(this.activePanel.afterSlide){
36313                     this.activePanel.afterSlide();
36314                 }
36315             }
36316         }
36317     },
36318
36319     initAutoHide : function(){
36320         if(this.autoHide !== false){
36321             if(!this.autoHideHd){
36322                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36323                 this.autoHideHd = {
36324                     "mouseout": function(e){
36325                         if(!e.within(this.el, true)){
36326                             st.delay(500);
36327                         }
36328                     },
36329                     "mouseover" : function(e){
36330                         st.cancel();
36331                     },
36332                     scope : this
36333                 };
36334             }
36335             this.el.on(this.autoHideHd);
36336         }
36337     },
36338
36339     clearAutoHide : function(){
36340         if(this.autoHide !== false){
36341             this.el.un("mouseout", this.autoHideHd.mouseout);
36342             this.el.un("mouseover", this.autoHideHd.mouseover);
36343         }
36344     },
36345
36346     clearMonitor : function(){
36347         Roo.get(document).un("click", this.slideInIf, this);
36348     },
36349
36350     // these names are backwards but not changed for compat
36351     slideOut : function(){
36352         if(this.isSlid || this.el.hasActiveFx()){
36353             return;
36354         }
36355         this.isSlid = true;
36356         if(this.collapseBtn){
36357             this.collapseBtn.hide();
36358         }
36359         this.closeBtnState = this.closeBtn.getStyle('display');
36360         this.closeBtn.hide();
36361         if(this.stickBtn){
36362             this.stickBtn.show();
36363         }
36364         this.el.show();
36365         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36366         this.beforeSlide();
36367         this.el.setStyle("z-index", 10001);
36368         this.el.slideIn(this.getSlideAnchor(), {
36369             callback: function(){
36370                 this.afterSlide();
36371                 this.initAutoHide();
36372                 Roo.get(document).on("click", this.slideInIf, this);
36373                 this.fireEvent("slideshow", this);
36374             },
36375             scope: this,
36376             block: true
36377         });
36378     },
36379
36380     afterSlideIn : function(){
36381         this.clearAutoHide();
36382         this.isSlid = false;
36383         this.clearMonitor();
36384         this.el.setStyle("z-index", "");
36385         if(this.collapseBtn){
36386             this.collapseBtn.show();
36387         }
36388         this.closeBtn.setStyle('display', this.closeBtnState);
36389         if(this.stickBtn){
36390             this.stickBtn.hide();
36391         }
36392         this.fireEvent("slidehide", this);
36393     },
36394
36395     slideIn : function(cb){
36396         if(!this.isSlid || this.el.hasActiveFx()){
36397             Roo.callback(cb);
36398             return;
36399         }
36400         this.isSlid = false;
36401         this.beforeSlide();
36402         this.el.slideOut(this.getSlideAnchor(), {
36403             callback: function(){
36404                 this.el.setLeftTop(-10000, -10000);
36405                 this.afterSlide();
36406                 this.afterSlideIn();
36407                 Roo.callback(cb);
36408             },
36409             scope: this,
36410             block: true
36411         });
36412     },
36413     
36414     slideInIf : function(e){
36415         if(!e.within(this.el)){
36416             this.slideIn();
36417         }
36418     },
36419
36420     animateCollapse : function(){
36421         this.beforeSlide();
36422         this.el.setStyle("z-index", 20000);
36423         var anchor = this.getSlideAnchor();
36424         this.el.slideOut(anchor, {
36425             callback : function(){
36426                 this.el.setStyle("z-index", "");
36427                 this.collapsedEl.slideIn(anchor, {duration:.3});
36428                 this.afterSlide();
36429                 this.el.setLocation(-10000,-10000);
36430                 this.el.hide();
36431                 this.fireEvent("collapsed", this);
36432             },
36433             scope: this,
36434             block: true
36435         });
36436     },
36437
36438     animateExpand : function(){
36439         this.beforeSlide();
36440         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36441         this.el.setStyle("z-index", 20000);
36442         this.collapsedEl.hide({
36443             duration:.1
36444         });
36445         this.el.slideIn(this.getSlideAnchor(), {
36446             callback : function(){
36447                 this.el.setStyle("z-index", "");
36448                 this.afterSlide();
36449                 if(this.split){
36450                     this.split.el.show();
36451                 }
36452                 this.fireEvent("invalidated", this);
36453                 this.fireEvent("expanded", this);
36454             },
36455             scope: this,
36456             block: true
36457         });
36458     },
36459
36460     anchors : {
36461         "west" : "left",
36462         "east" : "right",
36463         "north" : "top",
36464         "south" : "bottom"
36465     },
36466
36467     sanchors : {
36468         "west" : "l",
36469         "east" : "r",
36470         "north" : "t",
36471         "south" : "b"
36472     },
36473
36474     canchors : {
36475         "west" : "tl-tr",
36476         "east" : "tr-tl",
36477         "north" : "tl-bl",
36478         "south" : "bl-tl"
36479     },
36480
36481     getAnchor : function(){
36482         return this.anchors[this.position];
36483     },
36484
36485     getCollapseAnchor : function(){
36486         return this.canchors[this.position];
36487     },
36488
36489     getSlideAnchor : function(){
36490         return this.sanchors[this.position];
36491     },
36492
36493     getAlignAdj : function(){
36494         var cm = this.cmargins;
36495         switch(this.position){
36496             case "west":
36497                 return [0, 0];
36498             break;
36499             case "east":
36500                 return [0, 0];
36501             break;
36502             case "north":
36503                 return [0, 0];
36504             break;
36505             case "south":
36506                 return [0, 0];
36507             break;
36508         }
36509     },
36510
36511     getExpandAdj : function(){
36512         var c = this.collapsedEl, cm = this.cmargins;
36513         switch(this.position){
36514             case "west":
36515                 return [-(cm.right+c.getWidth()+cm.left), 0];
36516             break;
36517             case "east":
36518                 return [cm.right+c.getWidth()+cm.left, 0];
36519             break;
36520             case "north":
36521                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36522             break;
36523             case "south":
36524                 return [0, cm.top+cm.bottom+c.getHeight()];
36525             break;
36526         }
36527     }
36528 });/*
36529  * Based on:
36530  * Ext JS Library 1.1.1
36531  * Copyright(c) 2006-2007, Ext JS, LLC.
36532  *
36533  * Originally Released Under LGPL - original licence link has changed is not relivant.
36534  *
36535  * Fork - LGPL
36536  * <script type="text/javascript">
36537  */
36538 /*
36539  * These classes are private internal classes
36540  */
36541 Roo.bootstrap.layout.Center = function(config){
36542     config.region = "center";
36543     Roo.bootstrap.layout.Region.call(this, config);
36544     this.visible = true;
36545     this.minWidth = config.minWidth || 20;
36546     this.minHeight = config.minHeight || 20;
36547 };
36548
36549 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36550     hide : function(){
36551         // center panel can't be hidden
36552     },
36553     
36554     show : function(){
36555         // center panel can't be hidden
36556     },
36557     
36558     getMinWidth: function(){
36559         return this.minWidth;
36560     },
36561     
36562     getMinHeight: function(){
36563         return this.minHeight;
36564     }
36565 });
36566
36567
36568
36569
36570  
36571
36572
36573
36574
36575
36576 Roo.bootstrap.layout.North = function(config)
36577 {
36578     config.region = 'north';
36579     config.cursor = 'n-resize';
36580     
36581     Roo.bootstrap.layout.Split.call(this, config);
36582     
36583     
36584     if(this.split){
36585         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36586         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36587         this.split.el.addClass("roo-layout-split-v");
36588     }
36589     var size = config.initialSize || config.height;
36590     if(typeof size != "undefined"){
36591         this.el.setHeight(size);
36592     }
36593 };
36594 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36595 {
36596     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36597     
36598     
36599     
36600     getBox : function(){
36601         if(this.collapsed){
36602             return this.collapsedEl.getBox();
36603         }
36604         var box = this.el.getBox();
36605         if(this.split){
36606             box.height += this.split.el.getHeight();
36607         }
36608         return box;
36609     },
36610     
36611     updateBox : function(box){
36612         if(this.split && !this.collapsed){
36613             box.height -= this.split.el.getHeight();
36614             this.split.el.setLeft(box.x);
36615             this.split.el.setTop(box.y+box.height);
36616             this.split.el.setWidth(box.width);
36617         }
36618         if(this.collapsed){
36619             this.updateBody(box.width, null);
36620         }
36621         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36622     }
36623 });
36624
36625
36626
36627
36628
36629 Roo.bootstrap.layout.South = function(config){
36630     config.region = 'south';
36631     config.cursor = 's-resize';
36632     Roo.bootstrap.layout.Split.call(this, config);
36633     if(this.split){
36634         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36635         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36636         this.split.el.addClass("roo-layout-split-v");
36637     }
36638     var size = config.initialSize || config.height;
36639     if(typeof size != "undefined"){
36640         this.el.setHeight(size);
36641     }
36642 };
36643
36644 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36645     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36646     getBox : function(){
36647         if(this.collapsed){
36648             return this.collapsedEl.getBox();
36649         }
36650         var box = this.el.getBox();
36651         if(this.split){
36652             var sh = this.split.el.getHeight();
36653             box.height += sh;
36654             box.y -= sh;
36655         }
36656         return box;
36657     },
36658     
36659     updateBox : function(box){
36660         if(this.split && !this.collapsed){
36661             var sh = this.split.el.getHeight();
36662             box.height -= sh;
36663             box.y += sh;
36664             this.split.el.setLeft(box.x);
36665             this.split.el.setTop(box.y-sh);
36666             this.split.el.setWidth(box.width);
36667         }
36668         if(this.collapsed){
36669             this.updateBody(box.width, null);
36670         }
36671         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36672     }
36673 });
36674
36675 Roo.bootstrap.layout.East = function(config){
36676     config.region = "east";
36677     config.cursor = "e-resize";
36678     Roo.bootstrap.layout.Split.call(this, config);
36679     if(this.split){
36680         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36681         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36682         this.split.el.addClass("roo-layout-split-h");
36683     }
36684     var size = config.initialSize || config.width;
36685     if(typeof size != "undefined"){
36686         this.el.setWidth(size);
36687     }
36688 };
36689 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36690     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36691     getBox : function(){
36692         if(this.collapsed){
36693             return this.collapsedEl.getBox();
36694         }
36695         var box = this.el.getBox();
36696         if(this.split){
36697             var sw = this.split.el.getWidth();
36698             box.width += sw;
36699             box.x -= sw;
36700         }
36701         return box;
36702     },
36703
36704     updateBox : function(box){
36705         if(this.split && !this.collapsed){
36706             var sw = this.split.el.getWidth();
36707             box.width -= sw;
36708             this.split.el.setLeft(box.x);
36709             this.split.el.setTop(box.y);
36710             this.split.el.setHeight(box.height);
36711             box.x += sw;
36712         }
36713         if(this.collapsed){
36714             this.updateBody(null, box.height);
36715         }
36716         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36717     }
36718 });
36719
36720 Roo.bootstrap.layout.West = function(config){
36721     config.region = "west";
36722     config.cursor = "w-resize";
36723     
36724     Roo.bootstrap.layout.Split.call(this, config);
36725     if(this.split){
36726         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36727         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36728         this.split.el.addClass("roo-layout-split-h");
36729     }
36730     
36731 };
36732 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36733     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36734     
36735     onRender: function(ctr, pos)
36736     {
36737         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36738         var size = this.config.initialSize || this.config.width;
36739         if(typeof size != "undefined"){
36740             this.el.setWidth(size);
36741         }
36742     },
36743     
36744     getBox : function(){
36745         if(this.collapsed){
36746             return this.collapsedEl.getBox();
36747         }
36748         var box = this.el.getBox();
36749         if(this.split){
36750             box.width += this.split.el.getWidth();
36751         }
36752         return box;
36753     },
36754     
36755     updateBox : function(box){
36756         if(this.split && !this.collapsed){
36757             var sw = this.split.el.getWidth();
36758             box.width -= sw;
36759             this.split.el.setLeft(box.x+box.width);
36760             this.split.el.setTop(box.y);
36761             this.split.el.setHeight(box.height);
36762         }
36763         if(this.collapsed){
36764             this.updateBody(null, box.height);
36765         }
36766         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36767     }
36768 });
36769 Roo.namespace("Roo.bootstrap.panel");/*
36770  * Based on:
36771  * Ext JS Library 1.1.1
36772  * Copyright(c) 2006-2007, Ext JS, LLC.
36773  *
36774  * Originally Released Under LGPL - original licence link has changed is not relivant.
36775  *
36776  * Fork - LGPL
36777  * <script type="text/javascript">
36778  */
36779 /**
36780  * @class Roo.ContentPanel
36781  * @extends Roo.util.Observable
36782  * A basic ContentPanel element.
36783  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36784  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36785  * @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
36786  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36787  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36788  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36789  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36790  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36791  * @cfg {String} title          The title for this panel
36792  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36793  * @cfg {String} url            Calls {@link #setUrl} with this value
36794  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36795  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36796  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36797  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36798  * @cfg {Boolean} badges render the badges
36799
36800  * @constructor
36801  * Create a new ContentPanel.
36802  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36803  * @param {String/Object} config A string to set only the title or a config object
36804  * @param {String} content (optional) Set the HTML content for this panel
36805  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36806  */
36807 Roo.bootstrap.panel.Content = function( config){
36808     
36809     this.tpl = config.tpl || false;
36810     
36811     var el = config.el;
36812     var content = config.content;
36813
36814     if(config.autoCreate){ // xtype is available if this is called from factory
36815         el = Roo.id();
36816     }
36817     this.el = Roo.get(el);
36818     if(!this.el && config && config.autoCreate){
36819         if(typeof config.autoCreate == "object"){
36820             if(!config.autoCreate.id){
36821                 config.autoCreate.id = config.id||el;
36822             }
36823             this.el = Roo.DomHelper.append(document.body,
36824                         config.autoCreate, true);
36825         }else{
36826             var elcfg =  {   tag: "div",
36827                             cls: "roo-layout-inactive-content",
36828                             id: config.id||el
36829                             };
36830             if (config.html) {
36831                 elcfg.html = config.html;
36832                 
36833             }
36834                         
36835             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36836         }
36837     } 
36838     this.closable = false;
36839     this.loaded = false;
36840     this.active = false;
36841    
36842       
36843     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36844         
36845         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36846         
36847         this.wrapEl = this.el; //this.el.wrap();
36848         var ti = [];
36849         if (config.toolbar.items) {
36850             ti = config.toolbar.items ;
36851             delete config.toolbar.items ;
36852         }
36853         
36854         var nitems = [];
36855         this.toolbar.render(this.wrapEl, 'before');
36856         for(var i =0;i < ti.length;i++) {
36857           //  Roo.log(['add child', items[i]]);
36858             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36859         }
36860         this.toolbar.items = nitems;
36861         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36862         delete config.toolbar;
36863         
36864     }
36865     /*
36866     // xtype created footer. - not sure if will work as we normally have to render first..
36867     if (this.footer && !this.footer.el && this.footer.xtype) {
36868         if (!this.wrapEl) {
36869             this.wrapEl = this.el.wrap();
36870         }
36871     
36872         this.footer.container = this.wrapEl.createChild();
36873          
36874         this.footer = Roo.factory(this.footer, Roo);
36875         
36876     }
36877     */
36878     
36879      if(typeof config == "string"){
36880         this.title = config;
36881     }else{
36882         Roo.apply(this, config);
36883     }
36884     
36885     if(this.resizeEl){
36886         this.resizeEl = Roo.get(this.resizeEl, true);
36887     }else{
36888         this.resizeEl = this.el;
36889     }
36890     // handle view.xtype
36891     
36892  
36893     
36894     
36895     this.addEvents({
36896         /**
36897          * @event activate
36898          * Fires when this panel is activated. 
36899          * @param {Roo.ContentPanel} this
36900          */
36901         "activate" : true,
36902         /**
36903          * @event deactivate
36904          * Fires when this panel is activated. 
36905          * @param {Roo.ContentPanel} this
36906          */
36907         "deactivate" : true,
36908
36909         /**
36910          * @event resize
36911          * Fires when this panel is resized if fitToFrame is true.
36912          * @param {Roo.ContentPanel} this
36913          * @param {Number} width The width after any component adjustments
36914          * @param {Number} height The height after any component adjustments
36915          */
36916         "resize" : true,
36917         
36918          /**
36919          * @event render
36920          * Fires when this tab is created
36921          * @param {Roo.ContentPanel} this
36922          */
36923         "render" : true
36924         
36925         
36926         
36927     });
36928     
36929
36930     
36931     
36932     if(this.autoScroll){
36933         this.resizeEl.setStyle("overflow", "auto");
36934     } else {
36935         // fix randome scrolling
36936         //this.el.on('scroll', function() {
36937         //    Roo.log('fix random scolling');
36938         //    this.scrollTo('top',0); 
36939         //});
36940     }
36941     content = content || this.content;
36942     if(content){
36943         this.setContent(content);
36944     }
36945     if(config && config.url){
36946         this.setUrl(this.url, this.params, this.loadOnce);
36947     }
36948     
36949     
36950     
36951     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36952     
36953     if (this.view && typeof(this.view.xtype) != 'undefined') {
36954         this.view.el = this.el.appendChild(document.createElement("div"));
36955         this.view = Roo.factory(this.view); 
36956         this.view.render  &&  this.view.render(false, '');  
36957     }
36958     
36959     
36960     this.fireEvent('render', this);
36961 };
36962
36963 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36964     
36965     tabTip : '',
36966     
36967     setRegion : function(region){
36968         this.region = region;
36969         this.setActiveClass(region && !this.background);
36970     },
36971     
36972     
36973     setActiveClass: function(state)
36974     {
36975         if(state){
36976            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36977            this.el.setStyle('position','relative');
36978         }else{
36979            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36980            this.el.setStyle('position', 'absolute');
36981         } 
36982     },
36983     
36984     /**
36985      * Returns the toolbar for this Panel if one was configured. 
36986      * @return {Roo.Toolbar} 
36987      */
36988     getToolbar : function(){
36989         return this.toolbar;
36990     },
36991     
36992     setActiveState : function(active)
36993     {
36994         this.active = active;
36995         this.setActiveClass(active);
36996         if(!active){
36997             if(this.fireEvent("deactivate", this) === false){
36998                 return false;
36999             }
37000             return true;
37001         }
37002         this.fireEvent("activate", this);
37003         return true;
37004     },
37005     /**
37006      * Updates this panel's element
37007      * @param {String} content The new content
37008      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37009     */
37010     setContent : function(content, loadScripts){
37011         this.el.update(content, loadScripts);
37012     },
37013
37014     ignoreResize : function(w, h){
37015         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37016             return true;
37017         }else{
37018             this.lastSize = {width: w, height: h};
37019             return false;
37020         }
37021     },
37022     /**
37023      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37024      * @return {Roo.UpdateManager} The UpdateManager
37025      */
37026     getUpdateManager : function(){
37027         return this.el.getUpdateManager();
37028     },
37029      /**
37030      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37031      * @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:
37032 <pre><code>
37033 panel.load({
37034     url: "your-url.php",
37035     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37036     callback: yourFunction,
37037     scope: yourObject, //(optional scope)
37038     discardUrl: false,
37039     nocache: false,
37040     text: "Loading...",
37041     timeout: 30,
37042     scripts: false
37043 });
37044 </code></pre>
37045      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37046      * 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.
37047      * @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}
37048      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37049      * @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.
37050      * @return {Roo.ContentPanel} this
37051      */
37052     load : function(){
37053         var um = this.el.getUpdateManager();
37054         um.update.apply(um, arguments);
37055         return this;
37056     },
37057
37058
37059     /**
37060      * 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.
37061      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37062      * @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)
37063      * @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)
37064      * @return {Roo.UpdateManager} The UpdateManager
37065      */
37066     setUrl : function(url, params, loadOnce){
37067         if(this.refreshDelegate){
37068             this.removeListener("activate", this.refreshDelegate);
37069         }
37070         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37071         this.on("activate", this.refreshDelegate);
37072         return this.el.getUpdateManager();
37073     },
37074     
37075     _handleRefresh : function(url, params, loadOnce){
37076         if(!loadOnce || !this.loaded){
37077             var updater = this.el.getUpdateManager();
37078             updater.update(url, params, this._setLoaded.createDelegate(this));
37079         }
37080     },
37081     
37082     _setLoaded : function(){
37083         this.loaded = true;
37084     }, 
37085     
37086     /**
37087      * Returns this panel's id
37088      * @return {String} 
37089      */
37090     getId : function(){
37091         return this.el.id;
37092     },
37093     
37094     /** 
37095      * Returns this panel's element - used by regiosn to add.
37096      * @return {Roo.Element} 
37097      */
37098     getEl : function(){
37099         return this.wrapEl || this.el;
37100     },
37101     
37102    
37103     
37104     adjustForComponents : function(width, height)
37105     {
37106         //Roo.log('adjustForComponents ');
37107         if(this.resizeEl != this.el){
37108             width -= this.el.getFrameWidth('lr');
37109             height -= this.el.getFrameWidth('tb');
37110         }
37111         if(this.toolbar){
37112             var te = this.toolbar.getEl();
37113             te.setWidth(width);
37114             height -= te.getHeight();
37115         }
37116         if(this.footer){
37117             var te = this.footer.getEl();
37118             te.setWidth(width);
37119             height -= te.getHeight();
37120         }
37121         
37122         
37123         if(this.adjustments){
37124             width += this.adjustments[0];
37125             height += this.adjustments[1];
37126         }
37127         return {"width": width, "height": height};
37128     },
37129     
37130     setSize : function(width, height){
37131         if(this.fitToFrame && !this.ignoreResize(width, height)){
37132             if(this.fitContainer && this.resizeEl != this.el){
37133                 this.el.setSize(width, height);
37134             }
37135             var size = this.adjustForComponents(width, height);
37136             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37137             this.fireEvent('resize', this, size.width, size.height);
37138         }
37139     },
37140     
37141     /**
37142      * Returns this panel's title
37143      * @return {String} 
37144      */
37145     getTitle : function(){
37146         
37147         if (typeof(this.title) != 'object') {
37148             return this.title;
37149         }
37150         
37151         var t = '';
37152         for (var k in this.title) {
37153             if (!this.title.hasOwnProperty(k)) {
37154                 continue;
37155             }
37156             
37157             if (k.indexOf('-') >= 0) {
37158                 var s = k.split('-');
37159                 for (var i = 0; i<s.length; i++) {
37160                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37161                 }
37162             } else {
37163                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37164             }
37165         }
37166         return t;
37167     },
37168     
37169     /**
37170      * Set this panel's title
37171      * @param {String} title
37172      */
37173     setTitle : function(title){
37174         this.title = title;
37175         if(this.region){
37176             this.region.updatePanelTitle(this, title);
37177         }
37178     },
37179     
37180     /**
37181      * Returns true is this panel was configured to be closable
37182      * @return {Boolean} 
37183      */
37184     isClosable : function(){
37185         return this.closable;
37186     },
37187     
37188     beforeSlide : function(){
37189         this.el.clip();
37190         this.resizeEl.clip();
37191     },
37192     
37193     afterSlide : function(){
37194         this.el.unclip();
37195         this.resizeEl.unclip();
37196     },
37197     
37198     /**
37199      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37200      *   Will fail silently if the {@link #setUrl} method has not been called.
37201      *   This does not activate the panel, just updates its content.
37202      */
37203     refresh : function(){
37204         if(this.refreshDelegate){
37205            this.loaded = false;
37206            this.refreshDelegate();
37207         }
37208     },
37209     
37210     /**
37211      * Destroys this panel
37212      */
37213     destroy : function(){
37214         this.el.removeAllListeners();
37215         var tempEl = document.createElement("span");
37216         tempEl.appendChild(this.el.dom);
37217         tempEl.innerHTML = "";
37218         this.el.remove();
37219         this.el = null;
37220     },
37221     
37222     /**
37223      * form - if the content panel contains a form - this is a reference to it.
37224      * @type {Roo.form.Form}
37225      */
37226     form : false,
37227     /**
37228      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37229      *    This contains a reference to it.
37230      * @type {Roo.View}
37231      */
37232     view : false,
37233     
37234       /**
37235      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37236      * <pre><code>
37237
37238 layout.addxtype({
37239        xtype : 'Form',
37240        items: [ .... ]
37241    }
37242 );
37243
37244 </code></pre>
37245      * @param {Object} cfg Xtype definition of item to add.
37246      */
37247     
37248     
37249     getChildContainer: function () {
37250         return this.getEl();
37251     }
37252     
37253     
37254     /*
37255         var  ret = new Roo.factory(cfg);
37256         return ret;
37257         
37258         
37259         // add form..
37260         if (cfg.xtype.match(/^Form$/)) {
37261             
37262             var el;
37263             //if (this.footer) {
37264             //    el = this.footer.container.insertSibling(false, 'before');
37265             //} else {
37266                 el = this.el.createChild();
37267             //}
37268
37269             this.form = new  Roo.form.Form(cfg);
37270             
37271             
37272             if ( this.form.allItems.length) {
37273                 this.form.render(el.dom);
37274             }
37275             return this.form;
37276         }
37277         // should only have one of theses..
37278         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37279             // views.. should not be just added - used named prop 'view''
37280             
37281             cfg.el = this.el.appendChild(document.createElement("div"));
37282             // factory?
37283             
37284             var ret = new Roo.factory(cfg);
37285              
37286              ret.render && ret.render(false, ''); // render blank..
37287             this.view = ret;
37288             return ret;
37289         }
37290         return false;
37291     }
37292     \*/
37293 });
37294  
37295 /**
37296  * @class Roo.bootstrap.panel.Grid
37297  * @extends Roo.bootstrap.panel.Content
37298  * @constructor
37299  * Create a new GridPanel.
37300  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37301  * @param {Object} config A the config object
37302   
37303  */
37304
37305
37306
37307 Roo.bootstrap.panel.Grid = function(config)
37308 {
37309     
37310       
37311     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37312         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37313
37314     config.el = this.wrapper;
37315     //this.el = this.wrapper;
37316     
37317       if (config.container) {
37318         // ctor'ed from a Border/panel.grid
37319         
37320         
37321         this.wrapper.setStyle("overflow", "hidden");
37322         this.wrapper.addClass('roo-grid-container');
37323
37324     }
37325     
37326     
37327     if(config.toolbar){
37328         var tool_el = this.wrapper.createChild();    
37329         this.toolbar = Roo.factory(config.toolbar);
37330         var ti = [];
37331         if (config.toolbar.items) {
37332             ti = config.toolbar.items ;
37333             delete config.toolbar.items ;
37334         }
37335         
37336         var nitems = [];
37337         this.toolbar.render(tool_el);
37338         for(var i =0;i < ti.length;i++) {
37339           //  Roo.log(['add child', items[i]]);
37340             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37341         }
37342         this.toolbar.items = nitems;
37343         
37344         delete config.toolbar;
37345     }
37346     
37347     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37348     config.grid.scrollBody = true;;
37349     config.grid.monitorWindowResize = false; // turn off autosizing
37350     config.grid.autoHeight = false;
37351     config.grid.autoWidth = false;
37352     
37353     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37354     
37355     if (config.background) {
37356         // render grid on panel activation (if panel background)
37357         this.on('activate', function(gp) {
37358             if (!gp.grid.rendered) {
37359                 gp.grid.render(this.wrapper);
37360                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37361             }
37362         });
37363             
37364     } else {
37365         this.grid.render(this.wrapper);
37366         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37367
37368     }
37369     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37370     // ??? needed ??? config.el = this.wrapper;
37371     
37372     
37373     
37374   
37375     // xtype created footer. - not sure if will work as we normally have to render first..
37376     if (this.footer && !this.footer.el && this.footer.xtype) {
37377         
37378         var ctr = this.grid.getView().getFooterPanel(true);
37379         this.footer.dataSource = this.grid.dataSource;
37380         this.footer = Roo.factory(this.footer, Roo);
37381         this.footer.render(ctr);
37382         
37383     }
37384     
37385     
37386     
37387     
37388      
37389 };
37390
37391 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37392     getId : function(){
37393         return this.grid.id;
37394     },
37395     
37396     /**
37397      * Returns the grid for this panel
37398      * @return {Roo.bootstrap.Table} 
37399      */
37400     getGrid : function(){
37401         return this.grid;    
37402     },
37403     
37404     setSize : function(width, height){
37405         if(!this.ignoreResize(width, height)){
37406             var grid = this.grid;
37407             var size = this.adjustForComponents(width, height);
37408             var gridel = grid.getGridEl();
37409             gridel.setSize(size.width, size.height);
37410             /*
37411             var thd = grid.getGridEl().select('thead',true).first();
37412             var tbd = grid.getGridEl().select('tbody', true).first();
37413             if (tbd) {
37414                 tbd.setSize(width, height - thd.getHeight());
37415             }
37416             */
37417             grid.autoSize();
37418         }
37419     },
37420      
37421     
37422     
37423     beforeSlide : function(){
37424         this.grid.getView().scroller.clip();
37425     },
37426     
37427     afterSlide : function(){
37428         this.grid.getView().scroller.unclip();
37429     },
37430     
37431     destroy : function(){
37432         this.grid.destroy();
37433         delete this.grid;
37434         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37435     }
37436 });
37437
37438 /**
37439  * @class Roo.bootstrap.panel.Nest
37440  * @extends Roo.bootstrap.panel.Content
37441  * @constructor
37442  * Create a new Panel, that can contain a layout.Border.
37443  * 
37444  * 
37445  * @param {Roo.BorderLayout} layout The layout for this panel
37446  * @param {String/Object} config A string to set only the title or a config object
37447  */
37448 Roo.bootstrap.panel.Nest = function(config)
37449 {
37450     // construct with only one argument..
37451     /* FIXME - implement nicer consturctors
37452     if (layout.layout) {
37453         config = layout;
37454         layout = config.layout;
37455         delete config.layout;
37456     }
37457     if (layout.xtype && !layout.getEl) {
37458         // then layout needs constructing..
37459         layout = Roo.factory(layout, Roo);
37460     }
37461     */
37462     
37463     config.el =  config.layout.getEl();
37464     
37465     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37466     
37467     config.layout.monitorWindowResize = false; // turn off autosizing
37468     this.layout = config.layout;
37469     this.layout.getEl().addClass("roo-layout-nested-layout");
37470     
37471     
37472     
37473     
37474 };
37475
37476 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37477
37478     setSize : function(width, height){
37479         if(!this.ignoreResize(width, height)){
37480             var size = this.adjustForComponents(width, height);
37481             var el = this.layout.getEl();
37482             if (size.height < 1) {
37483                 el.setWidth(size.width);   
37484             } else {
37485                 el.setSize(size.width, size.height);
37486             }
37487             var touch = el.dom.offsetWidth;
37488             this.layout.layout();
37489             // ie requires a double layout on the first pass
37490             if(Roo.isIE && !this.initialized){
37491                 this.initialized = true;
37492                 this.layout.layout();
37493             }
37494         }
37495     },
37496     
37497     // activate all subpanels if not currently active..
37498     
37499     setActiveState : function(active){
37500         this.active = active;
37501         this.setActiveClass(active);
37502         
37503         if(!active){
37504             this.fireEvent("deactivate", this);
37505             return;
37506         }
37507         
37508         this.fireEvent("activate", this);
37509         // not sure if this should happen before or after..
37510         if (!this.layout) {
37511             return; // should not happen..
37512         }
37513         var reg = false;
37514         for (var r in this.layout.regions) {
37515             reg = this.layout.getRegion(r);
37516             if (reg.getActivePanel()) {
37517                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37518                 reg.setActivePanel(reg.getActivePanel());
37519                 continue;
37520             }
37521             if (!reg.panels.length) {
37522                 continue;
37523             }
37524             reg.showPanel(reg.getPanel(0));
37525         }
37526         
37527         
37528         
37529         
37530     },
37531     
37532     /**
37533      * Returns the nested BorderLayout for this panel
37534      * @return {Roo.BorderLayout} 
37535      */
37536     getLayout : function(){
37537         return this.layout;
37538     },
37539     
37540      /**
37541      * Adds a xtype elements to the layout of the nested panel
37542      * <pre><code>
37543
37544 panel.addxtype({
37545        xtype : 'ContentPanel',
37546        region: 'west',
37547        items: [ .... ]
37548    }
37549 );
37550
37551 panel.addxtype({
37552         xtype : 'NestedLayoutPanel',
37553         region: 'west',
37554         layout: {
37555            center: { },
37556            west: { }   
37557         },
37558         items : [ ... list of content panels or nested layout panels.. ]
37559    }
37560 );
37561 </code></pre>
37562      * @param {Object} cfg Xtype definition of item to add.
37563      */
37564     addxtype : function(cfg) {
37565         return this.layout.addxtype(cfg);
37566     
37567     }
37568 });        /*
37569  * Based on:
37570  * Ext JS Library 1.1.1
37571  * Copyright(c) 2006-2007, Ext JS, LLC.
37572  *
37573  * Originally Released Under LGPL - original licence link has changed is not relivant.
37574  *
37575  * Fork - LGPL
37576  * <script type="text/javascript">
37577  */
37578 /**
37579  * @class Roo.TabPanel
37580  * @extends Roo.util.Observable
37581  * A lightweight tab container.
37582  * <br><br>
37583  * Usage:
37584  * <pre><code>
37585 // basic tabs 1, built from existing content
37586 var tabs = new Roo.TabPanel("tabs1");
37587 tabs.addTab("script", "View Script");
37588 tabs.addTab("markup", "View Markup");
37589 tabs.activate("script");
37590
37591 // more advanced tabs, built from javascript
37592 var jtabs = new Roo.TabPanel("jtabs");
37593 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37594
37595 // set up the UpdateManager
37596 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37597 var updater = tab2.getUpdateManager();
37598 updater.setDefaultUrl("ajax1.htm");
37599 tab2.on('activate', updater.refresh, updater, true);
37600
37601 // Use setUrl for Ajax loading
37602 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37603 tab3.setUrl("ajax2.htm", null, true);
37604
37605 // Disabled tab
37606 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37607 tab4.disable();
37608
37609 jtabs.activate("jtabs-1");
37610  * </code></pre>
37611  * @constructor
37612  * Create a new TabPanel.
37613  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37614  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37615  */
37616 Roo.bootstrap.panel.Tabs = function(config){
37617     /**
37618     * The container element for this TabPanel.
37619     * @type Roo.Element
37620     */
37621     this.el = Roo.get(config.el);
37622     delete config.el;
37623     if(config){
37624         if(typeof config == "boolean"){
37625             this.tabPosition = config ? "bottom" : "top";
37626         }else{
37627             Roo.apply(this, config);
37628         }
37629     }
37630     
37631     if(this.tabPosition == "bottom"){
37632         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37633         this.el.addClass("roo-tabs-bottom");
37634     }
37635     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37636     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37637     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37638     if(Roo.isIE){
37639         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37640     }
37641     if(this.tabPosition != "bottom"){
37642         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37643          * @type Roo.Element
37644          */
37645         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37646         this.el.addClass("roo-tabs-top");
37647     }
37648     this.items = [];
37649
37650     this.bodyEl.setStyle("position", "relative");
37651
37652     this.active = null;
37653     this.activateDelegate = this.activate.createDelegate(this);
37654
37655     this.addEvents({
37656         /**
37657          * @event tabchange
37658          * Fires when the active tab changes
37659          * @param {Roo.TabPanel} this
37660          * @param {Roo.TabPanelItem} activePanel The new active tab
37661          */
37662         "tabchange": true,
37663         /**
37664          * @event beforetabchange
37665          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37666          * @param {Roo.TabPanel} this
37667          * @param {Object} e Set cancel to true on this object to cancel the tab change
37668          * @param {Roo.TabPanelItem} tab The tab being changed to
37669          */
37670         "beforetabchange" : true
37671     });
37672
37673     Roo.EventManager.onWindowResize(this.onResize, this);
37674     this.cpad = this.el.getPadding("lr");
37675     this.hiddenCount = 0;
37676
37677
37678     // toolbar on the tabbar support...
37679     if (this.toolbar) {
37680         alert("no toolbar support yet");
37681         this.toolbar  = false;
37682         /*
37683         var tcfg = this.toolbar;
37684         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37685         this.toolbar = new Roo.Toolbar(tcfg);
37686         if (Roo.isSafari) {
37687             var tbl = tcfg.container.child('table', true);
37688             tbl.setAttribute('width', '100%');
37689         }
37690         */
37691         
37692     }
37693    
37694
37695
37696     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37697 };
37698
37699 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37700     /*
37701      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37702      */
37703     tabPosition : "top",
37704     /*
37705      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37706      */
37707     currentTabWidth : 0,
37708     /*
37709      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37710      */
37711     minTabWidth : 40,
37712     /*
37713      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37714      */
37715     maxTabWidth : 250,
37716     /*
37717      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37718      */
37719     preferredTabWidth : 175,
37720     /*
37721      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37722      */
37723     resizeTabs : false,
37724     /*
37725      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37726      */
37727     monitorResize : true,
37728     /*
37729      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37730      */
37731     toolbar : false,
37732
37733     /**
37734      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37735      * @param {String} id The id of the div to use <b>or create</b>
37736      * @param {String} text The text for the tab
37737      * @param {String} content (optional) Content to put in the TabPanelItem body
37738      * @param {Boolean} closable (optional) True to create a close icon on the tab
37739      * @return {Roo.TabPanelItem} The created TabPanelItem
37740      */
37741     addTab : function(id, text, content, closable, tpl)
37742     {
37743         var item = new Roo.bootstrap.panel.TabItem({
37744             panel: this,
37745             id : id,
37746             text : text,
37747             closable : closable,
37748             tpl : tpl
37749         });
37750         this.addTabItem(item);
37751         if(content){
37752             item.setContent(content);
37753         }
37754         return item;
37755     },
37756
37757     /**
37758      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37759      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37760      * @return {Roo.TabPanelItem}
37761      */
37762     getTab : function(id){
37763         return this.items[id];
37764     },
37765
37766     /**
37767      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37768      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37769      */
37770     hideTab : function(id){
37771         var t = this.items[id];
37772         if(!t.isHidden()){
37773            t.setHidden(true);
37774            this.hiddenCount++;
37775            this.autoSizeTabs();
37776         }
37777     },
37778
37779     /**
37780      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37781      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37782      */
37783     unhideTab : function(id){
37784         var t = this.items[id];
37785         if(t.isHidden()){
37786            t.setHidden(false);
37787            this.hiddenCount--;
37788            this.autoSizeTabs();
37789         }
37790     },
37791
37792     /**
37793      * Adds an existing {@link Roo.TabPanelItem}.
37794      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37795      */
37796     addTabItem : function(item){
37797         this.items[item.id] = item;
37798         this.items.push(item);
37799       //  if(this.resizeTabs){
37800     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37801   //         this.autoSizeTabs();
37802 //        }else{
37803 //            item.autoSize();
37804        // }
37805     },
37806
37807     /**
37808      * Removes a {@link Roo.TabPanelItem}.
37809      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37810      */
37811     removeTab : function(id){
37812         var items = this.items;
37813         var tab = items[id];
37814         if(!tab) { return; }
37815         var index = items.indexOf(tab);
37816         if(this.active == tab && items.length > 1){
37817             var newTab = this.getNextAvailable(index);
37818             if(newTab) {
37819                 newTab.activate();
37820             }
37821         }
37822         this.stripEl.dom.removeChild(tab.pnode.dom);
37823         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37824             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37825         }
37826         items.splice(index, 1);
37827         delete this.items[tab.id];
37828         tab.fireEvent("close", tab);
37829         tab.purgeListeners();
37830         this.autoSizeTabs();
37831     },
37832
37833     getNextAvailable : function(start){
37834         var items = this.items;
37835         var index = start;
37836         // look for a next tab that will slide over to
37837         // replace the one being removed
37838         while(index < items.length){
37839             var item = items[++index];
37840             if(item && !item.isHidden()){
37841                 return item;
37842             }
37843         }
37844         // if one isn't found select the previous tab (on the left)
37845         index = start;
37846         while(index >= 0){
37847             var item = items[--index];
37848             if(item && !item.isHidden()){
37849                 return item;
37850             }
37851         }
37852         return null;
37853     },
37854
37855     /**
37856      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37857      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37858      */
37859     disableTab : function(id){
37860         var tab = this.items[id];
37861         if(tab && this.active != tab){
37862             tab.disable();
37863         }
37864     },
37865
37866     /**
37867      * Enables a {@link Roo.TabPanelItem} that is disabled.
37868      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37869      */
37870     enableTab : function(id){
37871         var tab = this.items[id];
37872         tab.enable();
37873     },
37874
37875     /**
37876      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37877      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37878      * @return {Roo.TabPanelItem} The TabPanelItem.
37879      */
37880     activate : function(id){
37881         var tab = this.items[id];
37882         if(!tab){
37883             return null;
37884         }
37885         if(tab == this.active || tab.disabled){
37886             return tab;
37887         }
37888         var e = {};
37889         this.fireEvent("beforetabchange", this, e, tab);
37890         if(e.cancel !== true && !tab.disabled){
37891             if(this.active){
37892                 this.active.hide();
37893             }
37894             this.active = this.items[id];
37895             this.active.show();
37896             this.fireEvent("tabchange", this, this.active);
37897         }
37898         return tab;
37899     },
37900
37901     /**
37902      * Gets the active {@link Roo.TabPanelItem}.
37903      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37904      */
37905     getActiveTab : function(){
37906         return this.active;
37907     },
37908
37909     /**
37910      * Updates the tab body element to fit the height of the container element
37911      * for overflow scrolling
37912      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37913      */
37914     syncHeight : function(targetHeight){
37915         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37916         var bm = this.bodyEl.getMargins();
37917         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37918         this.bodyEl.setHeight(newHeight);
37919         return newHeight;
37920     },
37921
37922     onResize : function(){
37923         if(this.monitorResize){
37924             this.autoSizeTabs();
37925         }
37926     },
37927
37928     /**
37929      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37930      */
37931     beginUpdate : function(){
37932         this.updating = true;
37933     },
37934
37935     /**
37936      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37937      */
37938     endUpdate : function(){
37939         this.updating = false;
37940         this.autoSizeTabs();
37941     },
37942
37943     /**
37944      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37945      */
37946     autoSizeTabs : function(){
37947         var count = this.items.length;
37948         var vcount = count - this.hiddenCount;
37949         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37950             return;
37951         }
37952         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37953         var availWidth = Math.floor(w / vcount);
37954         var b = this.stripBody;
37955         if(b.getWidth() > w){
37956             var tabs = this.items;
37957             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37958             if(availWidth < this.minTabWidth){
37959                 /*if(!this.sleft){    // incomplete scrolling code
37960                     this.createScrollButtons();
37961                 }
37962                 this.showScroll();
37963                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37964             }
37965         }else{
37966             if(this.currentTabWidth < this.preferredTabWidth){
37967                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37968             }
37969         }
37970     },
37971
37972     /**
37973      * Returns the number of tabs in this TabPanel.
37974      * @return {Number}
37975      */
37976      getCount : function(){
37977          return this.items.length;
37978      },
37979
37980     /**
37981      * Resizes all the tabs to the passed width
37982      * @param {Number} The new width
37983      */
37984     setTabWidth : function(width){
37985         this.currentTabWidth = width;
37986         for(var i = 0, len = this.items.length; i < len; i++) {
37987                 if(!this.items[i].isHidden()) {
37988                 this.items[i].setWidth(width);
37989             }
37990         }
37991     },
37992
37993     /**
37994      * Destroys this TabPanel
37995      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37996      */
37997     destroy : function(removeEl){
37998         Roo.EventManager.removeResizeListener(this.onResize, this);
37999         for(var i = 0, len = this.items.length; i < len; i++){
38000             this.items[i].purgeListeners();
38001         }
38002         if(removeEl === true){
38003             this.el.update("");
38004             this.el.remove();
38005         }
38006     },
38007     
38008     createStrip : function(container)
38009     {
38010         var strip = document.createElement("nav");
38011         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38012         container.appendChild(strip);
38013         return strip;
38014     },
38015     
38016     createStripList : function(strip)
38017     {
38018         // div wrapper for retard IE
38019         // returns the "tr" element.
38020         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38021         //'<div class="x-tabs-strip-wrap">'+
38022           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38023           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38024         return strip.firstChild; //.firstChild.firstChild.firstChild;
38025     },
38026     createBody : function(container)
38027     {
38028         var body = document.createElement("div");
38029         Roo.id(body, "tab-body");
38030         //Roo.fly(body).addClass("x-tabs-body");
38031         Roo.fly(body).addClass("tab-content");
38032         container.appendChild(body);
38033         return body;
38034     },
38035     createItemBody :function(bodyEl, id){
38036         var body = Roo.getDom(id);
38037         if(!body){
38038             body = document.createElement("div");
38039             body.id = id;
38040         }
38041         //Roo.fly(body).addClass("x-tabs-item-body");
38042         Roo.fly(body).addClass("tab-pane");
38043          bodyEl.insertBefore(body, bodyEl.firstChild);
38044         return body;
38045     },
38046     /** @private */
38047     createStripElements :  function(stripEl, text, closable, tpl)
38048     {
38049         var td = document.createElement("li"); // was td..
38050         
38051         
38052         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38053         
38054         
38055         stripEl.appendChild(td);
38056         /*if(closable){
38057             td.className = "x-tabs-closable";
38058             if(!this.closeTpl){
38059                 this.closeTpl = new Roo.Template(
38060                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38061                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38062                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38063                 );
38064             }
38065             var el = this.closeTpl.overwrite(td, {"text": text});
38066             var close = el.getElementsByTagName("div")[0];
38067             var inner = el.getElementsByTagName("em")[0];
38068             return {"el": el, "close": close, "inner": inner};
38069         } else {
38070         */
38071         // not sure what this is..
38072 //            if(!this.tabTpl){
38073                 //this.tabTpl = new Roo.Template(
38074                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38075                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38076                 //);
38077 //                this.tabTpl = new Roo.Template(
38078 //                   '<a href="#">' +
38079 //                   '<span unselectable="on"' +
38080 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38081 //                            ' >{text}</span></a>'
38082 //                );
38083 //                
38084 //            }
38085
38086
38087             var template = tpl || this.tabTpl || false;
38088             
38089             if(!template){
38090                 
38091                 template = new Roo.Template(
38092                    '<a href="#">' +
38093                    '<span unselectable="on"' +
38094                             (this.disableTooltips ? '' : ' title="{text}"') +
38095                             ' >{text}</span></a>'
38096                 );
38097             }
38098             
38099             switch (typeof(template)) {
38100                 case 'object' :
38101                     break;
38102                 case 'string' :
38103                     template = new Roo.Template(template);
38104                     break;
38105                 default :
38106                     break;
38107             }
38108             
38109             var el = template.overwrite(td, {"text": text});
38110             
38111             var inner = el.getElementsByTagName("span")[0];
38112             
38113             return {"el": el, "inner": inner};
38114             
38115     }
38116         
38117     
38118 });
38119
38120 /**
38121  * @class Roo.TabPanelItem
38122  * @extends Roo.util.Observable
38123  * Represents an individual item (tab plus body) in a TabPanel.
38124  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38125  * @param {String} id The id of this TabPanelItem
38126  * @param {String} text The text for the tab of this TabPanelItem
38127  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38128  */
38129 Roo.bootstrap.panel.TabItem = function(config){
38130     /**
38131      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38132      * @type Roo.TabPanel
38133      */
38134     this.tabPanel = config.panel;
38135     /**
38136      * The id for this TabPanelItem
38137      * @type String
38138      */
38139     this.id = config.id;
38140     /** @private */
38141     this.disabled = false;
38142     /** @private */
38143     this.text = config.text;
38144     /** @private */
38145     this.loaded = false;
38146     this.closable = config.closable;
38147
38148     /**
38149      * The body element for this TabPanelItem.
38150      * @type Roo.Element
38151      */
38152     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38153     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38154     this.bodyEl.setStyle("display", "block");
38155     this.bodyEl.setStyle("zoom", "1");
38156     //this.hideAction();
38157
38158     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38159     /** @private */
38160     this.el = Roo.get(els.el);
38161     this.inner = Roo.get(els.inner, true);
38162     this.textEl = Roo.get(this.el.dom.firstChild, true);
38163     this.pnode = Roo.get(els.el.parentNode, true);
38164 //    this.el.on("mousedown", this.onTabMouseDown, this);
38165     this.el.on("click", this.onTabClick, this);
38166     /** @private */
38167     if(config.closable){
38168         var c = Roo.get(els.close, true);
38169         c.dom.title = this.closeText;
38170         c.addClassOnOver("close-over");
38171         c.on("click", this.closeClick, this);
38172      }
38173
38174     this.addEvents({
38175          /**
38176          * @event activate
38177          * Fires when this tab becomes the active tab.
38178          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38179          * @param {Roo.TabPanelItem} this
38180          */
38181         "activate": true,
38182         /**
38183          * @event beforeclose
38184          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38185          * @param {Roo.TabPanelItem} this
38186          * @param {Object} e Set cancel to true on this object to cancel the close.
38187          */
38188         "beforeclose": true,
38189         /**
38190          * @event close
38191          * Fires when this tab is closed.
38192          * @param {Roo.TabPanelItem} this
38193          */
38194          "close": true,
38195         /**
38196          * @event deactivate
38197          * Fires when this tab is no longer the active tab.
38198          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38199          * @param {Roo.TabPanelItem} this
38200          */
38201          "deactivate" : true
38202     });
38203     this.hidden = false;
38204
38205     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38206 };
38207
38208 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38209            {
38210     purgeListeners : function(){
38211        Roo.util.Observable.prototype.purgeListeners.call(this);
38212        this.el.removeAllListeners();
38213     },
38214     /**
38215      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38216      */
38217     show : function(){
38218         this.pnode.addClass("active");
38219         this.showAction();
38220         if(Roo.isOpera){
38221             this.tabPanel.stripWrap.repaint();
38222         }
38223         this.fireEvent("activate", this.tabPanel, this);
38224     },
38225
38226     /**
38227      * Returns true if this tab is the active tab.
38228      * @return {Boolean}
38229      */
38230     isActive : function(){
38231         return this.tabPanel.getActiveTab() == this;
38232     },
38233
38234     /**
38235      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38236      */
38237     hide : function(){
38238         this.pnode.removeClass("active");
38239         this.hideAction();
38240         this.fireEvent("deactivate", this.tabPanel, this);
38241     },
38242
38243     hideAction : function(){
38244         this.bodyEl.hide();
38245         this.bodyEl.setStyle("position", "absolute");
38246         this.bodyEl.setLeft("-20000px");
38247         this.bodyEl.setTop("-20000px");
38248     },
38249
38250     showAction : function(){
38251         this.bodyEl.setStyle("position", "relative");
38252         this.bodyEl.setTop("");
38253         this.bodyEl.setLeft("");
38254         this.bodyEl.show();
38255     },
38256
38257     /**
38258      * Set the tooltip for the tab.
38259      * @param {String} tooltip The tab's tooltip
38260      */
38261     setTooltip : function(text){
38262         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38263             this.textEl.dom.qtip = text;
38264             this.textEl.dom.removeAttribute('title');
38265         }else{
38266             this.textEl.dom.title = text;
38267         }
38268     },
38269
38270     onTabClick : function(e){
38271         e.preventDefault();
38272         this.tabPanel.activate(this.id);
38273     },
38274
38275     onTabMouseDown : function(e){
38276         e.preventDefault();
38277         this.tabPanel.activate(this.id);
38278     },
38279 /*
38280     getWidth : function(){
38281         return this.inner.getWidth();
38282     },
38283
38284     setWidth : function(width){
38285         var iwidth = width - this.pnode.getPadding("lr");
38286         this.inner.setWidth(iwidth);
38287         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38288         this.pnode.setWidth(width);
38289     },
38290 */
38291     /**
38292      * Show or hide the tab
38293      * @param {Boolean} hidden True to hide or false to show.
38294      */
38295     setHidden : function(hidden){
38296         this.hidden = hidden;
38297         this.pnode.setStyle("display", hidden ? "none" : "");
38298     },
38299
38300     /**
38301      * Returns true if this tab is "hidden"
38302      * @return {Boolean}
38303      */
38304     isHidden : function(){
38305         return this.hidden;
38306     },
38307
38308     /**
38309      * Returns the text for this tab
38310      * @return {String}
38311      */
38312     getText : function(){
38313         return this.text;
38314     },
38315     /*
38316     autoSize : function(){
38317         //this.el.beginMeasure();
38318         this.textEl.setWidth(1);
38319         /*
38320          *  #2804 [new] Tabs in Roojs
38321          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38322          */
38323         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38324         //this.el.endMeasure();
38325     //},
38326
38327     /**
38328      * Sets the text for the tab (Note: this also sets the tooltip text)
38329      * @param {String} text The tab's text and tooltip
38330      */
38331     setText : function(text){
38332         this.text = text;
38333         this.textEl.update(text);
38334         this.setTooltip(text);
38335         //if(!this.tabPanel.resizeTabs){
38336         //    this.autoSize();
38337         //}
38338     },
38339     /**
38340      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38341      */
38342     activate : function(){
38343         this.tabPanel.activate(this.id);
38344     },
38345
38346     /**
38347      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38348      */
38349     disable : function(){
38350         if(this.tabPanel.active != this){
38351             this.disabled = true;
38352             this.pnode.addClass("disabled");
38353         }
38354     },
38355
38356     /**
38357      * Enables this TabPanelItem if it was previously disabled.
38358      */
38359     enable : function(){
38360         this.disabled = false;
38361         this.pnode.removeClass("disabled");
38362     },
38363
38364     /**
38365      * Sets the content for this TabPanelItem.
38366      * @param {String} content The content
38367      * @param {Boolean} loadScripts true to look for and load scripts
38368      */
38369     setContent : function(content, loadScripts){
38370         this.bodyEl.update(content, loadScripts);
38371     },
38372
38373     /**
38374      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38375      * @return {Roo.UpdateManager} The UpdateManager
38376      */
38377     getUpdateManager : function(){
38378         return this.bodyEl.getUpdateManager();
38379     },
38380
38381     /**
38382      * Set a URL to be used to load the content for this TabPanelItem.
38383      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38384      * @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)
38385      * @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)
38386      * @return {Roo.UpdateManager} The UpdateManager
38387      */
38388     setUrl : function(url, params, loadOnce){
38389         if(this.refreshDelegate){
38390             this.un('activate', this.refreshDelegate);
38391         }
38392         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38393         this.on("activate", this.refreshDelegate);
38394         return this.bodyEl.getUpdateManager();
38395     },
38396
38397     /** @private */
38398     _handleRefresh : function(url, params, loadOnce){
38399         if(!loadOnce || !this.loaded){
38400             var updater = this.bodyEl.getUpdateManager();
38401             updater.update(url, params, this._setLoaded.createDelegate(this));
38402         }
38403     },
38404
38405     /**
38406      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38407      *   Will fail silently if the setUrl method has not been called.
38408      *   This does not activate the panel, just updates its content.
38409      */
38410     refresh : function(){
38411         if(this.refreshDelegate){
38412            this.loaded = false;
38413            this.refreshDelegate();
38414         }
38415     },
38416
38417     /** @private */
38418     _setLoaded : function(){
38419         this.loaded = true;
38420     },
38421
38422     /** @private */
38423     closeClick : function(e){
38424         var o = {};
38425         e.stopEvent();
38426         this.fireEvent("beforeclose", this, o);
38427         if(o.cancel !== true){
38428             this.tabPanel.removeTab(this.id);
38429         }
38430     },
38431     /**
38432      * The text displayed in the tooltip for the close icon.
38433      * @type String
38434      */
38435     closeText : "Close this tab"
38436 });
38437 /**
38438 *    This script refer to:
38439 *    Title: International Telephone Input
38440 *    Author: Jack O'Connor
38441 *    Code version:  v12.1.12
38442 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38443 **/
38444
38445 Roo.bootstrap.PhoneInputData = function() {
38446     var d = [
38447       [
38448         "Afghanistan (‫افغانستان‬‎)",
38449         "af",
38450         "93"
38451       ],
38452       [
38453         "Albania (Shqipëri)",
38454         "al",
38455         "355"
38456       ],
38457       [
38458         "Algeria (‫الجزائر‬‎)",
38459         "dz",
38460         "213"
38461       ],
38462       [
38463         "American Samoa",
38464         "as",
38465         "1684"
38466       ],
38467       [
38468         "Andorra",
38469         "ad",
38470         "376"
38471       ],
38472       [
38473         "Angola",
38474         "ao",
38475         "244"
38476       ],
38477       [
38478         "Anguilla",
38479         "ai",
38480         "1264"
38481       ],
38482       [
38483         "Antigua and Barbuda",
38484         "ag",
38485         "1268"
38486       ],
38487       [
38488         "Argentina",
38489         "ar",
38490         "54"
38491       ],
38492       [
38493         "Armenia (Հայաստան)",
38494         "am",
38495         "374"
38496       ],
38497       [
38498         "Aruba",
38499         "aw",
38500         "297"
38501       ],
38502       [
38503         "Australia",
38504         "au",
38505         "61",
38506         0
38507       ],
38508       [
38509         "Austria (Österreich)",
38510         "at",
38511         "43"
38512       ],
38513       [
38514         "Azerbaijan (Azərbaycan)",
38515         "az",
38516         "994"
38517       ],
38518       [
38519         "Bahamas",
38520         "bs",
38521         "1242"
38522       ],
38523       [
38524         "Bahrain (‫البحرين‬‎)",
38525         "bh",
38526         "973"
38527       ],
38528       [
38529         "Bangladesh (বাংলাদেশ)",
38530         "bd",
38531         "880"
38532       ],
38533       [
38534         "Barbados",
38535         "bb",
38536         "1246"
38537       ],
38538       [
38539         "Belarus (Беларусь)",
38540         "by",
38541         "375"
38542       ],
38543       [
38544         "Belgium (België)",
38545         "be",
38546         "32"
38547       ],
38548       [
38549         "Belize",
38550         "bz",
38551         "501"
38552       ],
38553       [
38554         "Benin (Bénin)",
38555         "bj",
38556         "229"
38557       ],
38558       [
38559         "Bermuda",
38560         "bm",
38561         "1441"
38562       ],
38563       [
38564         "Bhutan (འབྲུག)",
38565         "bt",
38566         "975"
38567       ],
38568       [
38569         "Bolivia",
38570         "bo",
38571         "591"
38572       ],
38573       [
38574         "Bosnia and Herzegovina (Босна и Херцеговина)",
38575         "ba",
38576         "387"
38577       ],
38578       [
38579         "Botswana",
38580         "bw",
38581         "267"
38582       ],
38583       [
38584         "Brazil (Brasil)",
38585         "br",
38586         "55"
38587       ],
38588       [
38589         "British Indian Ocean Territory",
38590         "io",
38591         "246"
38592       ],
38593       [
38594         "British Virgin Islands",
38595         "vg",
38596         "1284"
38597       ],
38598       [
38599         "Brunei",
38600         "bn",
38601         "673"
38602       ],
38603       [
38604         "Bulgaria (България)",
38605         "bg",
38606         "359"
38607       ],
38608       [
38609         "Burkina Faso",
38610         "bf",
38611         "226"
38612       ],
38613       [
38614         "Burundi (Uburundi)",
38615         "bi",
38616         "257"
38617       ],
38618       [
38619         "Cambodia (កម្ពុជា)",
38620         "kh",
38621         "855"
38622       ],
38623       [
38624         "Cameroon (Cameroun)",
38625         "cm",
38626         "237"
38627       ],
38628       [
38629         "Canada",
38630         "ca",
38631         "1",
38632         1,
38633         ["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"]
38634       ],
38635       [
38636         "Cape Verde (Kabu Verdi)",
38637         "cv",
38638         "238"
38639       ],
38640       [
38641         "Caribbean Netherlands",
38642         "bq",
38643         "599",
38644         1
38645       ],
38646       [
38647         "Cayman Islands",
38648         "ky",
38649         "1345"
38650       ],
38651       [
38652         "Central African Republic (République centrafricaine)",
38653         "cf",
38654         "236"
38655       ],
38656       [
38657         "Chad (Tchad)",
38658         "td",
38659         "235"
38660       ],
38661       [
38662         "Chile",
38663         "cl",
38664         "56"
38665       ],
38666       [
38667         "China (中国)",
38668         "cn",
38669         "86"
38670       ],
38671       [
38672         "Christmas Island",
38673         "cx",
38674         "61",
38675         2
38676       ],
38677       [
38678         "Cocos (Keeling) Islands",
38679         "cc",
38680         "61",
38681         1
38682       ],
38683       [
38684         "Colombia",
38685         "co",
38686         "57"
38687       ],
38688       [
38689         "Comoros (‫جزر القمر‬‎)",
38690         "km",
38691         "269"
38692       ],
38693       [
38694         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38695         "cd",
38696         "243"
38697       ],
38698       [
38699         "Congo (Republic) (Congo-Brazzaville)",
38700         "cg",
38701         "242"
38702       ],
38703       [
38704         "Cook Islands",
38705         "ck",
38706         "682"
38707       ],
38708       [
38709         "Costa Rica",
38710         "cr",
38711         "506"
38712       ],
38713       [
38714         "Côte d’Ivoire",
38715         "ci",
38716         "225"
38717       ],
38718       [
38719         "Croatia (Hrvatska)",
38720         "hr",
38721         "385"
38722       ],
38723       [
38724         "Cuba",
38725         "cu",
38726         "53"
38727       ],
38728       [
38729         "Curaçao",
38730         "cw",
38731         "599",
38732         0
38733       ],
38734       [
38735         "Cyprus (Κύπρος)",
38736         "cy",
38737         "357"
38738       ],
38739       [
38740         "Czech Republic (Česká republika)",
38741         "cz",
38742         "420"
38743       ],
38744       [
38745         "Denmark (Danmark)",
38746         "dk",
38747         "45"
38748       ],
38749       [
38750         "Djibouti",
38751         "dj",
38752         "253"
38753       ],
38754       [
38755         "Dominica",
38756         "dm",
38757         "1767"
38758       ],
38759       [
38760         "Dominican Republic (República Dominicana)",
38761         "do",
38762         "1",
38763         2,
38764         ["809", "829", "849"]
38765       ],
38766       [
38767         "Ecuador",
38768         "ec",
38769         "593"
38770       ],
38771       [
38772         "Egypt (‫مصر‬‎)",
38773         "eg",
38774         "20"
38775       ],
38776       [
38777         "El Salvador",
38778         "sv",
38779         "503"
38780       ],
38781       [
38782         "Equatorial Guinea (Guinea Ecuatorial)",
38783         "gq",
38784         "240"
38785       ],
38786       [
38787         "Eritrea",
38788         "er",
38789         "291"
38790       ],
38791       [
38792         "Estonia (Eesti)",
38793         "ee",
38794         "372"
38795       ],
38796       [
38797         "Ethiopia",
38798         "et",
38799         "251"
38800       ],
38801       [
38802         "Falkland Islands (Islas Malvinas)",
38803         "fk",
38804         "500"
38805       ],
38806       [
38807         "Faroe Islands (Føroyar)",
38808         "fo",
38809         "298"
38810       ],
38811       [
38812         "Fiji",
38813         "fj",
38814         "679"
38815       ],
38816       [
38817         "Finland (Suomi)",
38818         "fi",
38819         "358",
38820         0
38821       ],
38822       [
38823         "France",
38824         "fr",
38825         "33"
38826       ],
38827       [
38828         "French Guiana (Guyane française)",
38829         "gf",
38830         "594"
38831       ],
38832       [
38833         "French Polynesia (Polynésie française)",
38834         "pf",
38835         "689"
38836       ],
38837       [
38838         "Gabon",
38839         "ga",
38840         "241"
38841       ],
38842       [
38843         "Gambia",
38844         "gm",
38845         "220"
38846       ],
38847       [
38848         "Georgia (საქართველო)",
38849         "ge",
38850         "995"
38851       ],
38852       [
38853         "Germany (Deutschland)",
38854         "de",
38855         "49"
38856       ],
38857       [
38858         "Ghana (Gaana)",
38859         "gh",
38860         "233"
38861       ],
38862       [
38863         "Gibraltar",
38864         "gi",
38865         "350"
38866       ],
38867       [
38868         "Greece (Ελλάδα)",
38869         "gr",
38870         "30"
38871       ],
38872       [
38873         "Greenland (Kalaallit Nunaat)",
38874         "gl",
38875         "299"
38876       ],
38877       [
38878         "Grenada",
38879         "gd",
38880         "1473"
38881       ],
38882       [
38883         "Guadeloupe",
38884         "gp",
38885         "590",
38886         0
38887       ],
38888       [
38889         "Guam",
38890         "gu",
38891         "1671"
38892       ],
38893       [
38894         "Guatemala",
38895         "gt",
38896         "502"
38897       ],
38898       [
38899         "Guernsey",
38900         "gg",
38901         "44",
38902         1
38903       ],
38904       [
38905         "Guinea (Guinée)",
38906         "gn",
38907         "224"
38908       ],
38909       [
38910         "Guinea-Bissau (Guiné Bissau)",
38911         "gw",
38912         "245"
38913       ],
38914       [
38915         "Guyana",
38916         "gy",
38917         "592"
38918       ],
38919       [
38920         "Haiti",
38921         "ht",
38922         "509"
38923       ],
38924       [
38925         "Honduras",
38926         "hn",
38927         "504"
38928       ],
38929       [
38930         "Hong Kong (香港)",
38931         "hk",
38932         "852"
38933       ],
38934       [
38935         "Hungary (Magyarország)",
38936         "hu",
38937         "36"
38938       ],
38939       [
38940         "Iceland (Ísland)",
38941         "is",
38942         "354"
38943       ],
38944       [
38945         "India (भारत)",
38946         "in",
38947         "91"
38948       ],
38949       [
38950         "Indonesia",
38951         "id",
38952         "62"
38953       ],
38954       [
38955         "Iran (‫ایران‬‎)",
38956         "ir",
38957         "98"
38958       ],
38959       [
38960         "Iraq (‫العراق‬‎)",
38961         "iq",
38962         "964"
38963       ],
38964       [
38965         "Ireland",
38966         "ie",
38967         "353"
38968       ],
38969       [
38970         "Isle of Man",
38971         "im",
38972         "44",
38973         2
38974       ],
38975       [
38976         "Israel (‫ישראל‬‎)",
38977         "il",
38978         "972"
38979       ],
38980       [
38981         "Italy (Italia)",
38982         "it",
38983         "39",
38984         0
38985       ],
38986       [
38987         "Jamaica",
38988         "jm",
38989         "1876"
38990       ],
38991       [
38992         "Japan (日本)",
38993         "jp",
38994         "81"
38995       ],
38996       [
38997         "Jersey",
38998         "je",
38999         "44",
39000         3
39001       ],
39002       [
39003         "Jordan (‫الأردن‬‎)",
39004         "jo",
39005         "962"
39006       ],
39007       [
39008         "Kazakhstan (Казахстан)",
39009         "kz",
39010         "7",
39011         1
39012       ],
39013       [
39014         "Kenya",
39015         "ke",
39016         "254"
39017       ],
39018       [
39019         "Kiribati",
39020         "ki",
39021         "686"
39022       ],
39023       [
39024         "Kosovo",
39025         "xk",
39026         "383"
39027       ],
39028       [
39029         "Kuwait (‫الكويت‬‎)",
39030         "kw",
39031         "965"
39032       ],
39033       [
39034         "Kyrgyzstan (Кыргызстан)",
39035         "kg",
39036         "996"
39037       ],
39038       [
39039         "Laos (ລາວ)",
39040         "la",
39041         "856"
39042       ],
39043       [
39044         "Latvia (Latvija)",
39045         "lv",
39046         "371"
39047       ],
39048       [
39049         "Lebanon (‫لبنان‬‎)",
39050         "lb",
39051         "961"
39052       ],
39053       [
39054         "Lesotho",
39055         "ls",
39056         "266"
39057       ],
39058       [
39059         "Liberia",
39060         "lr",
39061         "231"
39062       ],
39063       [
39064         "Libya (‫ليبيا‬‎)",
39065         "ly",
39066         "218"
39067       ],
39068       [
39069         "Liechtenstein",
39070         "li",
39071         "423"
39072       ],
39073       [
39074         "Lithuania (Lietuva)",
39075         "lt",
39076         "370"
39077       ],
39078       [
39079         "Luxembourg",
39080         "lu",
39081         "352"
39082       ],
39083       [
39084         "Macau (澳門)",
39085         "mo",
39086         "853"
39087       ],
39088       [
39089         "Macedonia (FYROM) (Македонија)",
39090         "mk",
39091         "389"
39092       ],
39093       [
39094         "Madagascar (Madagasikara)",
39095         "mg",
39096         "261"
39097       ],
39098       [
39099         "Malawi",
39100         "mw",
39101         "265"
39102       ],
39103       [
39104         "Malaysia",
39105         "my",
39106         "60"
39107       ],
39108       [
39109         "Maldives",
39110         "mv",
39111         "960"
39112       ],
39113       [
39114         "Mali",
39115         "ml",
39116         "223"
39117       ],
39118       [
39119         "Malta",
39120         "mt",
39121         "356"
39122       ],
39123       [
39124         "Marshall Islands",
39125         "mh",
39126         "692"
39127       ],
39128       [
39129         "Martinique",
39130         "mq",
39131         "596"
39132       ],
39133       [
39134         "Mauritania (‫موريتانيا‬‎)",
39135         "mr",
39136         "222"
39137       ],
39138       [
39139         "Mauritius (Moris)",
39140         "mu",
39141         "230"
39142       ],
39143       [
39144         "Mayotte",
39145         "yt",
39146         "262",
39147         1
39148       ],
39149       [
39150         "Mexico (México)",
39151         "mx",
39152         "52"
39153       ],
39154       [
39155         "Micronesia",
39156         "fm",
39157         "691"
39158       ],
39159       [
39160         "Moldova (Republica Moldova)",
39161         "md",
39162         "373"
39163       ],
39164       [
39165         "Monaco",
39166         "mc",
39167         "377"
39168       ],
39169       [
39170         "Mongolia (Монгол)",
39171         "mn",
39172         "976"
39173       ],
39174       [
39175         "Montenegro (Crna Gora)",
39176         "me",
39177         "382"
39178       ],
39179       [
39180         "Montserrat",
39181         "ms",
39182         "1664"
39183       ],
39184       [
39185         "Morocco (‫المغرب‬‎)",
39186         "ma",
39187         "212",
39188         0
39189       ],
39190       [
39191         "Mozambique (Moçambique)",
39192         "mz",
39193         "258"
39194       ],
39195       [
39196         "Myanmar (Burma) (မြန်မာ)",
39197         "mm",
39198         "95"
39199       ],
39200       [
39201         "Namibia (Namibië)",
39202         "na",
39203         "264"
39204       ],
39205       [
39206         "Nauru",
39207         "nr",
39208         "674"
39209       ],
39210       [
39211         "Nepal (नेपाल)",
39212         "np",
39213         "977"
39214       ],
39215       [
39216         "Netherlands (Nederland)",
39217         "nl",
39218         "31"
39219       ],
39220       [
39221         "New Caledonia (Nouvelle-Calédonie)",
39222         "nc",
39223         "687"
39224       ],
39225       [
39226         "New Zealand",
39227         "nz",
39228         "64"
39229       ],
39230       [
39231         "Nicaragua",
39232         "ni",
39233         "505"
39234       ],
39235       [
39236         "Niger (Nijar)",
39237         "ne",
39238         "227"
39239       ],
39240       [
39241         "Nigeria",
39242         "ng",
39243         "234"
39244       ],
39245       [
39246         "Niue",
39247         "nu",
39248         "683"
39249       ],
39250       [
39251         "Norfolk Island",
39252         "nf",
39253         "672"
39254       ],
39255       [
39256         "North Korea (조선 민주주의 인민 공화국)",
39257         "kp",
39258         "850"
39259       ],
39260       [
39261         "Northern Mariana Islands",
39262         "mp",
39263         "1670"
39264       ],
39265       [
39266         "Norway (Norge)",
39267         "no",
39268         "47",
39269         0
39270       ],
39271       [
39272         "Oman (‫عُمان‬‎)",
39273         "om",
39274         "968"
39275       ],
39276       [
39277         "Pakistan (‫پاکستان‬‎)",
39278         "pk",
39279         "92"
39280       ],
39281       [
39282         "Palau",
39283         "pw",
39284         "680"
39285       ],
39286       [
39287         "Palestine (‫فلسطين‬‎)",
39288         "ps",
39289         "970"
39290       ],
39291       [
39292         "Panama (Panamá)",
39293         "pa",
39294         "507"
39295       ],
39296       [
39297         "Papua New Guinea",
39298         "pg",
39299         "675"
39300       ],
39301       [
39302         "Paraguay",
39303         "py",
39304         "595"
39305       ],
39306       [
39307         "Peru (Perú)",
39308         "pe",
39309         "51"
39310       ],
39311       [
39312         "Philippines",
39313         "ph",
39314         "63"
39315       ],
39316       [
39317         "Poland (Polska)",
39318         "pl",
39319         "48"
39320       ],
39321       [
39322         "Portugal",
39323         "pt",
39324         "351"
39325       ],
39326       [
39327         "Puerto Rico",
39328         "pr",
39329         "1",
39330         3,
39331         ["787", "939"]
39332       ],
39333       [
39334         "Qatar (‫قطر‬‎)",
39335         "qa",
39336         "974"
39337       ],
39338       [
39339         "Réunion (La Réunion)",
39340         "re",
39341         "262",
39342         0
39343       ],
39344       [
39345         "Romania (România)",
39346         "ro",
39347         "40"
39348       ],
39349       [
39350         "Russia (Россия)",
39351         "ru",
39352         "7",
39353         0
39354       ],
39355       [
39356         "Rwanda",
39357         "rw",
39358         "250"
39359       ],
39360       [
39361         "Saint Barthélemy",
39362         "bl",
39363         "590",
39364         1
39365       ],
39366       [
39367         "Saint Helena",
39368         "sh",
39369         "290"
39370       ],
39371       [
39372         "Saint Kitts and Nevis",
39373         "kn",
39374         "1869"
39375       ],
39376       [
39377         "Saint Lucia",
39378         "lc",
39379         "1758"
39380       ],
39381       [
39382         "Saint Martin (Saint-Martin (partie française))",
39383         "mf",
39384         "590",
39385         2
39386       ],
39387       [
39388         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39389         "pm",
39390         "508"
39391       ],
39392       [
39393         "Saint Vincent and the Grenadines",
39394         "vc",
39395         "1784"
39396       ],
39397       [
39398         "Samoa",
39399         "ws",
39400         "685"
39401       ],
39402       [
39403         "San Marino",
39404         "sm",
39405         "378"
39406       ],
39407       [
39408         "São Tomé and Príncipe (São Tomé e Príncipe)",
39409         "st",
39410         "239"
39411       ],
39412       [
39413         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39414         "sa",
39415         "966"
39416       ],
39417       [
39418         "Senegal (Sénégal)",
39419         "sn",
39420         "221"
39421       ],
39422       [
39423         "Serbia (Србија)",
39424         "rs",
39425         "381"
39426       ],
39427       [
39428         "Seychelles",
39429         "sc",
39430         "248"
39431       ],
39432       [
39433         "Sierra Leone",
39434         "sl",
39435         "232"
39436       ],
39437       [
39438         "Singapore",
39439         "sg",
39440         "65"
39441       ],
39442       [
39443         "Sint Maarten",
39444         "sx",
39445         "1721"
39446       ],
39447       [
39448         "Slovakia (Slovensko)",
39449         "sk",
39450         "421"
39451       ],
39452       [
39453         "Slovenia (Slovenija)",
39454         "si",
39455         "386"
39456       ],
39457       [
39458         "Solomon Islands",
39459         "sb",
39460         "677"
39461       ],
39462       [
39463         "Somalia (Soomaaliya)",
39464         "so",
39465         "252"
39466       ],
39467       [
39468         "South Africa",
39469         "za",
39470         "27"
39471       ],
39472       [
39473         "South Korea (대한민국)",
39474         "kr",
39475         "82"
39476       ],
39477       [
39478         "South Sudan (‫جنوب السودان‬‎)",
39479         "ss",
39480         "211"
39481       ],
39482       [
39483         "Spain (España)",
39484         "es",
39485         "34"
39486       ],
39487       [
39488         "Sri Lanka (ශ්‍රී ලංකාව)",
39489         "lk",
39490         "94"
39491       ],
39492       [
39493         "Sudan (‫السودان‬‎)",
39494         "sd",
39495         "249"
39496       ],
39497       [
39498         "Suriname",
39499         "sr",
39500         "597"
39501       ],
39502       [
39503         "Svalbard and Jan Mayen",
39504         "sj",
39505         "47",
39506         1
39507       ],
39508       [
39509         "Swaziland",
39510         "sz",
39511         "268"
39512       ],
39513       [
39514         "Sweden (Sverige)",
39515         "se",
39516         "46"
39517       ],
39518       [
39519         "Switzerland (Schweiz)",
39520         "ch",
39521         "41"
39522       ],
39523       [
39524         "Syria (‫سوريا‬‎)",
39525         "sy",
39526         "963"
39527       ],
39528       [
39529         "Taiwan (台灣)",
39530         "tw",
39531         "886"
39532       ],
39533       [
39534         "Tajikistan",
39535         "tj",
39536         "992"
39537       ],
39538       [
39539         "Tanzania",
39540         "tz",
39541         "255"
39542       ],
39543       [
39544         "Thailand (ไทย)",
39545         "th",
39546         "66"
39547       ],
39548       [
39549         "Timor-Leste",
39550         "tl",
39551         "670"
39552       ],
39553       [
39554         "Togo",
39555         "tg",
39556         "228"
39557       ],
39558       [
39559         "Tokelau",
39560         "tk",
39561         "690"
39562       ],
39563       [
39564         "Tonga",
39565         "to",
39566         "676"
39567       ],
39568       [
39569         "Trinidad and Tobago",
39570         "tt",
39571         "1868"
39572       ],
39573       [
39574         "Tunisia (‫تونس‬‎)",
39575         "tn",
39576         "216"
39577       ],
39578       [
39579         "Turkey (Türkiye)",
39580         "tr",
39581         "90"
39582       ],
39583       [
39584         "Turkmenistan",
39585         "tm",
39586         "993"
39587       ],
39588       [
39589         "Turks and Caicos Islands",
39590         "tc",
39591         "1649"
39592       ],
39593       [
39594         "Tuvalu",
39595         "tv",
39596         "688"
39597       ],
39598       [
39599         "U.S. Virgin Islands",
39600         "vi",
39601         "1340"
39602       ],
39603       [
39604         "Uganda",
39605         "ug",
39606         "256"
39607       ],
39608       [
39609         "Ukraine (Україна)",
39610         "ua",
39611         "380"
39612       ],
39613       [
39614         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39615         "ae",
39616         "971"
39617       ],
39618       [
39619         "United Kingdom",
39620         "gb",
39621         "44",
39622         0
39623       ],
39624       [
39625         "United States",
39626         "us",
39627         "1",
39628         0
39629       ],
39630       [
39631         "Uruguay",
39632         "uy",
39633         "598"
39634       ],
39635       [
39636         "Uzbekistan (Oʻzbekiston)",
39637         "uz",
39638         "998"
39639       ],
39640       [
39641         "Vanuatu",
39642         "vu",
39643         "678"
39644       ],
39645       [
39646         "Vatican City (Città del Vaticano)",
39647         "va",
39648         "39",
39649         1
39650       ],
39651       [
39652         "Venezuela",
39653         "ve",
39654         "58"
39655       ],
39656       [
39657         "Vietnam (Việt Nam)",
39658         "vn",
39659         "84"
39660       ],
39661       [
39662         "Wallis and Futuna (Wallis-et-Futuna)",
39663         "wf",
39664         "681"
39665       ],
39666       [
39667         "Western Sahara (‫الصحراء الغربية‬‎)",
39668         "eh",
39669         "212",
39670         1
39671       ],
39672       [
39673         "Yemen (‫اليمن‬‎)",
39674         "ye",
39675         "967"
39676       ],
39677       [
39678         "Zambia",
39679         "zm",
39680         "260"
39681       ],
39682       [
39683         "Zimbabwe",
39684         "zw",
39685         "263"
39686       ],
39687       [
39688         "Åland Islands",
39689         "ax",
39690         "358",
39691         1
39692       ]
39693   ];
39694   
39695   return d;
39696 }/**
39697 *    This script refer to:
39698 *    Title: International Telephone Input
39699 *    Author: Jack O'Connor
39700 *    Code version:  v12.1.12
39701 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39702 **/
39703
39704 /**
39705  * @class Roo.bootstrap.PhoneInput
39706  * @extends Roo.bootstrap.TriggerField
39707  * An input with International dial-code selection
39708  
39709  * @cfg {String} defaultDialCode default '+852'
39710  * @cfg {Array} preferedCountries default []
39711   
39712  * @constructor
39713  * Create a new PhoneInput.
39714  * @param {Object} config Configuration options
39715  */
39716
39717 Roo.bootstrap.PhoneInput = function(config) {
39718     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39719 };
39720
39721 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39722         
39723         listWidth: undefined,
39724         
39725         selectedClass: 'active',
39726         
39727         invalidClass : "has-warning",
39728         
39729         validClass: 'has-success',
39730         
39731         allowed: '0123456789',
39732         
39733         /**
39734          * @cfg {String} defaultDialCode The default dial code when initializing the input
39735          */
39736         defaultDialCode: '+852',
39737         
39738         /**
39739          * @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
39740          */
39741         preferedCountries: false,
39742         
39743         getAutoCreate : function()
39744         {
39745             var data = Roo.bootstrap.PhoneInputData();
39746             var align = this.labelAlign || this.parentLabelAlign();
39747             var id = Roo.id();
39748             
39749             this.allCountries = [];
39750             this.dialCodeMapping = [];
39751             
39752             for (var i = 0; i < data.length; i++) {
39753               var c = data[i];
39754               this.allCountries[i] = {
39755                 name: c[0],
39756                 iso2: c[1],
39757                 dialCode: c[2],
39758                 priority: c[3] || 0,
39759                 areaCodes: c[4] || null
39760               };
39761               this.dialCodeMapping[c[2]] = {
39762                   name: c[0],
39763                   iso2: c[1],
39764                   priority: c[3] || 0,
39765                   areaCodes: c[4] || null
39766               };
39767             }
39768             
39769             var cfg = {
39770                 cls: 'form-group',
39771                 cn: []
39772             };
39773             
39774             var input =  {
39775                 tag: 'input',
39776                 id : id,
39777                 cls : 'form-control tel-input',
39778                 autocomplete: 'new-password'
39779             };
39780             
39781             var hiddenInput = {
39782                 tag: 'input',
39783                 type: 'hidden',
39784                 cls: 'hidden-tel-input'
39785             };
39786             
39787             if (this.name) {
39788                 hiddenInput.name = this.name;
39789             }
39790             
39791             if (this.disabled) {
39792                 input.disabled = true;
39793             }
39794             
39795             var flag_container = {
39796                 tag: 'div',
39797                 cls: 'flag-box',
39798                 cn: [
39799                     {
39800                         tag: 'div',
39801                         cls: 'flag'
39802                     },
39803                     {
39804                         tag: 'div',
39805                         cls: 'caret'
39806                     }
39807                 ]
39808             };
39809             
39810             var box = {
39811                 tag: 'div',
39812                 cls: this.hasFeedback ? 'has-feedback' : '',
39813                 cn: [
39814                     hiddenInput,
39815                     input,
39816                     {
39817                         tag: 'input',
39818                         cls: 'dial-code-holder',
39819                         disabled: true
39820                     }
39821                 ]
39822             };
39823             
39824             var container = {
39825                 cls: 'roo-select2-container input-group',
39826                 cn: [
39827                     flag_container,
39828                     box
39829                 ]
39830             };
39831             
39832             if (this.fieldLabel.length) {
39833                 var indicator = {
39834                     tag: 'i',
39835                     tooltip: 'This field is required'
39836                 };
39837                 
39838                 var label = {
39839                     tag: 'label',
39840                     'for':  id,
39841                     cls: 'control-label',
39842                     cn: []
39843                 };
39844                 
39845                 var label_text = {
39846                     tag: 'span',
39847                     html: this.fieldLabel
39848                 };
39849                 
39850                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39851                 label.cn = [
39852                     indicator,
39853                     label_text
39854                 ];
39855                 
39856                 if(this.indicatorpos == 'right') {
39857                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39858                     label.cn = [
39859                         label_text,
39860                         indicator
39861                     ];
39862                 }
39863                 
39864                 if(align == 'left') {
39865                     container = {
39866                         tag: 'div',
39867                         cn: [
39868                             container
39869                         ]
39870                     };
39871                     
39872                     if(this.labelWidth > 12){
39873                         label.style = "width: " + this.labelWidth + 'px';
39874                     }
39875                     if(this.labelWidth < 13 && this.labelmd == 0){
39876                         this.labelmd = this.labelWidth;
39877                     }
39878                     if(this.labellg > 0){
39879                         label.cls += ' col-lg-' + this.labellg;
39880                         input.cls += ' col-lg-' + (12 - this.labellg);
39881                     }
39882                     if(this.labelmd > 0){
39883                         label.cls += ' col-md-' + this.labelmd;
39884                         container.cls += ' col-md-' + (12 - this.labelmd);
39885                     }
39886                     if(this.labelsm > 0){
39887                         label.cls += ' col-sm-' + this.labelsm;
39888                         container.cls += ' col-sm-' + (12 - this.labelsm);
39889                     }
39890                     if(this.labelxs > 0){
39891                         label.cls += ' col-xs-' + this.labelxs;
39892                         container.cls += ' col-xs-' + (12 - this.labelxs);
39893                     }
39894                 }
39895             }
39896             
39897             cfg.cn = [
39898                 label,
39899                 container
39900             ];
39901             
39902             var settings = this;
39903             
39904             ['xs','sm','md','lg'].map(function(size){
39905                 if (settings[size]) {
39906                     cfg.cls += ' col-' + size + '-' + settings[size];
39907                 }
39908             });
39909             
39910             this.store = new Roo.data.Store({
39911                 proxy : new Roo.data.MemoryProxy({}),
39912                 reader : new Roo.data.JsonReader({
39913                     fields : [
39914                         {
39915                             'name' : 'name',
39916                             'type' : 'string'
39917                         },
39918                         {
39919                             'name' : 'iso2',
39920                             'type' : 'string'
39921                         },
39922                         {
39923                             'name' : 'dialCode',
39924                             'type' : 'string'
39925                         },
39926                         {
39927                             'name' : 'priority',
39928                             'type' : 'string'
39929                         },
39930                         {
39931                             'name' : 'areaCodes',
39932                             'type' : 'string'
39933                         }
39934                     ]
39935                 })
39936             });
39937             
39938             if(!this.preferedCountries) {
39939                 this.preferedCountries = [
39940                     'hk',
39941                     'gb',
39942                     'us'
39943                 ];
39944             }
39945             
39946             var p = this.preferedCountries.reverse();
39947             
39948             if(p) {
39949                 for (var i = 0; i < p.length; i++) {
39950                     for (var j = 0; j < this.allCountries.length; j++) {
39951                         if(this.allCountries[j].iso2 == p[i]) {
39952                             var t = this.allCountries[j];
39953                             this.allCountries.splice(j,1);
39954                             this.allCountries.unshift(t);
39955                         }
39956                     } 
39957                 }
39958             }
39959             
39960             this.store.proxy.data = {
39961                 success: true,
39962                 data: this.allCountries
39963             };
39964             
39965             return cfg;
39966         },
39967         
39968         initEvents : function()
39969         {
39970             this.createList();
39971             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39972             
39973             this.indicator = this.indicatorEl();
39974             this.flag = this.flagEl();
39975             this.dialCodeHolder = this.dialCodeHolderEl();
39976             
39977             this.trigger = this.el.select('div.flag-box',true).first();
39978             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39979             
39980             var _this = this;
39981             
39982             (function(){
39983                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39984                 _this.list.setWidth(lw);
39985             }).defer(100);
39986             
39987             this.list.on('mouseover', this.onViewOver, this);
39988             this.list.on('mousemove', this.onViewMove, this);
39989             this.inputEl().on("keyup", this.onKeyUp, this);
39990             
39991             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39992
39993             this.view = new Roo.View(this.list, this.tpl, {
39994                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39995             });
39996             
39997             this.view.on('click', this.onViewClick, this);
39998             this.setValue(this.defaultDialCode);
39999         },
40000         
40001         onTriggerClick : function(e)
40002         {
40003             Roo.log('trigger click');
40004             if(this.disabled){
40005                 return;
40006             }
40007             
40008             if(this.isExpanded()){
40009                 this.collapse();
40010                 this.hasFocus = false;
40011             }else {
40012                 this.store.load({});
40013                 this.hasFocus = true;
40014                 this.expand();
40015             }
40016         },
40017         
40018         isExpanded : function()
40019         {
40020             return this.list.isVisible();
40021         },
40022         
40023         collapse : function()
40024         {
40025             if(!this.isExpanded()){
40026                 return;
40027             }
40028             this.list.hide();
40029             Roo.get(document).un('mousedown', this.collapseIf, this);
40030             Roo.get(document).un('mousewheel', this.collapseIf, this);
40031             this.fireEvent('collapse', this);
40032             this.validate();
40033         },
40034         
40035         expand : function()
40036         {
40037             Roo.log('expand');
40038
40039             if(this.isExpanded() || !this.hasFocus){
40040                 return;
40041             }
40042             
40043             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40044             this.list.setWidth(lw);
40045             
40046             this.list.show();
40047             this.restrictHeight();
40048             
40049             Roo.get(document).on('mousedown', this.collapseIf, this);
40050             Roo.get(document).on('mousewheel', this.collapseIf, this);
40051             
40052             this.fireEvent('expand', this);
40053         },
40054         
40055         restrictHeight : function()
40056         {
40057             this.list.alignTo(this.inputEl(), this.listAlign);
40058             this.list.alignTo(this.inputEl(), this.listAlign);
40059         },
40060         
40061         onViewOver : function(e, t)
40062         {
40063             if(this.inKeyMode){
40064                 return;
40065             }
40066             var item = this.view.findItemFromChild(t);
40067             
40068             if(item){
40069                 var index = this.view.indexOf(item);
40070                 this.select(index, false);
40071             }
40072         },
40073
40074         // private
40075         onViewClick : function(view, doFocus, el, e)
40076         {
40077             var index = this.view.getSelectedIndexes()[0];
40078             
40079             var r = this.store.getAt(index);
40080             
40081             if(r){
40082                 this.onSelect(r, index);
40083             }
40084             if(doFocus !== false && !this.blockFocus){
40085                 this.inputEl().focus();
40086             }
40087         },
40088         
40089         onViewMove : function(e, t)
40090         {
40091             this.inKeyMode = false;
40092         },
40093         
40094         select : function(index, scrollIntoView)
40095         {
40096             this.selectedIndex = index;
40097             this.view.select(index);
40098             if(scrollIntoView !== false){
40099                 var el = this.view.getNode(index);
40100                 if(el){
40101                     this.list.scrollChildIntoView(el, false);
40102                 }
40103             }
40104         },
40105         
40106         createList : function()
40107         {
40108             this.list = Roo.get(document.body).createChild({
40109                 tag: 'ul',
40110                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40111                 style: 'display:none'
40112             });
40113             
40114             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40115         },
40116         
40117         collapseIf : function(e)
40118         {
40119             var in_combo  = e.within(this.el);
40120             var in_list =  e.within(this.list);
40121             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40122             
40123             if (in_combo || in_list || is_list) {
40124                 return;
40125             }
40126             this.collapse();
40127         },
40128         
40129         onSelect : function(record, index)
40130         {
40131             if(this.fireEvent('beforeselect', this, record, index) !== false){
40132                 
40133                 this.setFlagClass(record.data.iso2);
40134                 this.setDialCode(record.data.dialCode);
40135                 this.hasFocus = false;
40136                 this.collapse();
40137                 this.fireEvent('select', this, record, index);
40138             }
40139         },
40140         
40141         flagEl : function()
40142         {
40143             var flag = this.el.select('div.flag',true).first();
40144             if(!flag){
40145                 return false;
40146             }
40147             return flag;
40148         },
40149         
40150         dialCodeHolderEl : function()
40151         {
40152             var d = this.el.select('input.dial-code-holder',true).first();
40153             if(!d){
40154                 return false;
40155             }
40156             return d;
40157         },
40158         
40159         setDialCode : function(v)
40160         {
40161             this.dialCodeHolder.dom.value = '+'+v;
40162         },
40163         
40164         setFlagClass : function(n)
40165         {
40166             this.flag.dom.className = 'flag '+n;
40167         },
40168         
40169         getValue : function()
40170         {
40171             var v = this.inputEl().getValue();
40172             if(this.dialCodeHolder) {
40173                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40174             }
40175             return v;
40176         },
40177         
40178         setValue : function(v)
40179         {
40180             var d = this.getDialCode(v);
40181             
40182             //invalid dial code
40183             if(v.length == 0 || !d || d.length == 0) {
40184                 if(this.rendered){
40185                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40186                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40187                 }
40188                 return;
40189             }
40190             
40191             //valid dial code
40192             this.setFlagClass(this.dialCodeMapping[d].iso2);
40193             this.setDialCode(d);
40194             this.inputEl().dom.value = v.replace('+'+d,'');
40195             this.hiddenEl().dom.value = this.getValue();
40196             
40197             this.validate();
40198         },
40199         
40200         getDialCode : function(v)
40201         {
40202             v = v ||  '';
40203             
40204             if (v.length == 0) {
40205                 return this.dialCodeHolder.dom.value;
40206             }
40207             
40208             var dialCode = "";
40209             if (v.charAt(0) != "+") {
40210                 return false;
40211             }
40212             var numericChars = "";
40213             for (var i = 1; i < v.length; i++) {
40214               var c = v.charAt(i);
40215               if (!isNaN(c)) {
40216                 numericChars += c;
40217                 if (this.dialCodeMapping[numericChars]) {
40218                   dialCode = v.substr(1, i);
40219                 }
40220                 if (numericChars.length == 4) {
40221                   break;
40222                 }
40223               }
40224             }
40225             return dialCode;
40226         },
40227         
40228         reset : function()
40229         {
40230             this.setValue(this.defaultDialCode);
40231             this.validate();
40232         },
40233         
40234         hiddenEl : function()
40235         {
40236             return this.el.select('input.hidden-tel-input',true).first();
40237         },
40238         
40239         onKeyUp : function(e){
40240             
40241             var k = e.getKey();
40242             var c = e.getCharCode();
40243             
40244             if(
40245                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40246                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40247             ){
40248                 e.stopEvent();
40249             }
40250             
40251             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40252             //     return;
40253             // }
40254             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40255                 e.stopEvent();
40256             }
40257             
40258             this.setValue(this.getValue());
40259         }
40260         
40261 });
40262 /**
40263  * @class Roo.bootstrap.MoneyField
40264  * @extends Roo.bootstrap.ComboBox
40265  * Bootstrap MoneyField class
40266  * 
40267  * @constructor
40268  * Create a new MoneyField.
40269  * @param {Object} config Configuration options
40270  */
40271
40272 Roo.bootstrap.MoneyField = function(config) {
40273     
40274     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40275     
40276 };
40277
40278 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40279     
40280     /**
40281      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40282      */
40283     allowDecimals : true,
40284     /**
40285      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40286      */
40287     decimalSeparator : ".",
40288     /**
40289      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40290      */
40291     decimalPrecision : 0,
40292     /**
40293      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40294      */
40295     allowNegative : true,
40296     /**
40297      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40298      */
40299     allowZero: true,
40300     /**
40301      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40302      */
40303     minValue : Number.NEGATIVE_INFINITY,
40304     /**
40305      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40306      */
40307     maxValue : Number.MAX_VALUE,
40308     /**
40309      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40310      */
40311     minText : "The minimum value for this field is {0}",
40312     /**
40313      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40314      */
40315     maxText : "The maximum value for this field is {0}",
40316     /**
40317      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40318      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40319      */
40320     nanText : "{0} is not a valid number",
40321     /**
40322      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40323      */
40324     castInt : true,
40325     /**
40326      * @cfg {String} defaults currency of the MoneyField
40327      * value should be in lkey
40328      */
40329     defaultCurrency : false,
40330     /**
40331      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40332      */
40333     thousandsDelimiter : false,
40334     
40335     
40336     inputlg : 9,
40337     inputmd : 9,
40338     inputsm : 9,
40339     inputxs : 6,
40340     
40341     store : false,
40342     
40343     getAutoCreate : function()
40344     {
40345         var align = this.labelAlign || this.parentLabelAlign();
40346         
40347         var id = Roo.id();
40348
40349         var cfg = {
40350             cls: 'form-group',
40351             cn: []
40352         };
40353
40354         var input =  {
40355             tag: 'input',
40356             id : id,
40357             cls : 'form-control roo-money-amount-input',
40358             autocomplete: 'new-password'
40359         };
40360         
40361         var hiddenInput = {
40362             tag: 'input',
40363             type: 'hidden',
40364             id: Roo.id(),
40365             cls: 'hidden-number-input'
40366         };
40367         
40368         if (this.name) {
40369             hiddenInput.name = this.name;
40370         }
40371
40372         if (this.disabled) {
40373             input.disabled = true;
40374         }
40375
40376         var clg = 12 - this.inputlg;
40377         var cmd = 12 - this.inputmd;
40378         var csm = 12 - this.inputsm;
40379         var cxs = 12 - this.inputxs;
40380         
40381         var container = {
40382             tag : 'div',
40383             cls : 'row roo-money-field',
40384             cn : [
40385                 {
40386                     tag : 'div',
40387                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40388                     cn : [
40389                         {
40390                             tag : 'div',
40391                             cls: 'roo-select2-container input-group',
40392                             cn: [
40393                                 {
40394                                     tag : 'input',
40395                                     cls : 'form-control roo-money-currency-input',
40396                                     autocomplete: 'new-password',
40397                                     readOnly : 1,
40398                                     name : this.currencyName
40399                                 },
40400                                 {
40401                                     tag :'span',
40402                                     cls : 'input-group-addon',
40403                                     cn : [
40404                                         {
40405                                             tag: 'span',
40406                                             cls: 'caret'
40407                                         }
40408                                     ]
40409                                 }
40410                             ]
40411                         }
40412                     ]
40413                 },
40414                 {
40415                     tag : 'div',
40416                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40417                     cn : [
40418                         {
40419                             tag: 'div',
40420                             cls: this.hasFeedback ? 'has-feedback' : '',
40421                             cn: [
40422                                 input
40423                             ]
40424                         }
40425                     ]
40426                 }
40427             ]
40428             
40429         };
40430         
40431         if (this.fieldLabel.length) {
40432             var indicator = {
40433                 tag: 'i',
40434                 tooltip: 'This field is required'
40435             };
40436
40437             var label = {
40438                 tag: 'label',
40439                 'for':  id,
40440                 cls: 'control-label',
40441                 cn: []
40442             };
40443
40444             var label_text = {
40445                 tag: 'span',
40446                 html: this.fieldLabel
40447             };
40448
40449             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40450             label.cn = [
40451                 indicator,
40452                 label_text
40453             ];
40454
40455             if(this.indicatorpos == 'right') {
40456                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40457                 label.cn = [
40458                     label_text,
40459                     indicator
40460                 ];
40461             }
40462
40463             if(align == 'left') {
40464                 container = {
40465                     tag: 'div',
40466                     cn: [
40467                         container
40468                     ]
40469                 };
40470
40471                 if(this.labelWidth > 12){
40472                     label.style = "width: " + this.labelWidth + 'px';
40473                 }
40474                 if(this.labelWidth < 13 && this.labelmd == 0){
40475                     this.labelmd = this.labelWidth;
40476                 }
40477                 if(this.labellg > 0){
40478                     label.cls += ' col-lg-' + this.labellg;
40479                     input.cls += ' col-lg-' + (12 - this.labellg);
40480                 }
40481                 if(this.labelmd > 0){
40482                     label.cls += ' col-md-' + this.labelmd;
40483                     container.cls += ' col-md-' + (12 - this.labelmd);
40484                 }
40485                 if(this.labelsm > 0){
40486                     label.cls += ' col-sm-' + this.labelsm;
40487                     container.cls += ' col-sm-' + (12 - this.labelsm);
40488                 }
40489                 if(this.labelxs > 0){
40490                     label.cls += ' col-xs-' + this.labelxs;
40491                     container.cls += ' col-xs-' + (12 - this.labelxs);
40492                 }
40493             }
40494         }
40495
40496         cfg.cn = [
40497             label,
40498             container,
40499             hiddenInput
40500         ];
40501         
40502         var settings = this;
40503
40504         ['xs','sm','md','lg'].map(function(size){
40505             if (settings[size]) {
40506                 cfg.cls += ' col-' + size + '-' + settings[size];
40507             }
40508         });
40509         
40510         return cfg;
40511     },
40512     
40513     initEvents : function()
40514     {
40515         this.indicator = this.indicatorEl();
40516         
40517         this.initCurrencyEvent();
40518         
40519         this.initNumberEvent();
40520     },
40521     
40522     initCurrencyEvent : function()
40523     {
40524         if (!this.store) {
40525             throw "can not find store for combo";
40526         }
40527         
40528         this.store = Roo.factory(this.store, Roo.data);
40529         this.store.parent = this;
40530         
40531         this.createList();
40532         
40533         this.triggerEl = this.el.select('.input-group-addon', true).first();
40534         
40535         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40536         
40537         var _this = this;
40538         
40539         (function(){
40540             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40541             _this.list.setWidth(lw);
40542         }).defer(100);
40543         
40544         this.list.on('mouseover', this.onViewOver, this);
40545         this.list.on('mousemove', this.onViewMove, this);
40546         this.list.on('scroll', this.onViewScroll, this);
40547         
40548         if(!this.tpl){
40549             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40550         }
40551         
40552         this.view = new Roo.View(this.list, this.tpl, {
40553             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40554         });
40555         
40556         this.view.on('click', this.onViewClick, this);
40557         
40558         this.store.on('beforeload', this.onBeforeLoad, this);
40559         this.store.on('load', this.onLoad, this);
40560         this.store.on('loadexception', this.onLoadException, this);
40561         
40562         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40563             "up" : function(e){
40564                 this.inKeyMode = true;
40565                 this.selectPrev();
40566             },
40567
40568             "down" : function(e){
40569                 if(!this.isExpanded()){
40570                     this.onTriggerClick();
40571                 }else{
40572                     this.inKeyMode = true;
40573                     this.selectNext();
40574                 }
40575             },
40576
40577             "enter" : function(e){
40578                 this.collapse();
40579                 
40580                 if(this.fireEvent("specialkey", this, e)){
40581                     this.onViewClick(false);
40582                 }
40583                 
40584                 return true;
40585             },
40586
40587             "esc" : function(e){
40588                 this.collapse();
40589             },
40590
40591             "tab" : function(e){
40592                 this.collapse();
40593                 
40594                 if(this.fireEvent("specialkey", this, e)){
40595                     this.onViewClick(false);
40596                 }
40597                 
40598                 return true;
40599             },
40600
40601             scope : this,
40602
40603             doRelay : function(foo, bar, hname){
40604                 if(hname == 'down' || this.scope.isExpanded()){
40605                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40606                 }
40607                 return true;
40608             },
40609
40610             forceKeyDown: true
40611         });
40612         
40613         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40614         
40615     },
40616     
40617     initNumberEvent : function(e)
40618     {
40619         this.inputEl().on("keydown" , this.fireKey,  this);
40620         this.inputEl().on("focus", this.onFocus,  this);
40621         this.inputEl().on("blur", this.onBlur,  this);
40622         
40623         this.inputEl().relayEvent('keyup', this);
40624         
40625         if(this.indicator){
40626             this.indicator.addClass('invisible');
40627         }
40628  
40629         this.originalValue = this.getValue();
40630         
40631         if(this.validationEvent == 'keyup'){
40632             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40633             this.inputEl().on('keyup', this.filterValidation, this);
40634         }
40635         else if(this.validationEvent !== false){
40636             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40637         }
40638         
40639         if(this.selectOnFocus){
40640             this.on("focus", this.preFocus, this);
40641             
40642         }
40643         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40644             this.inputEl().on("keypress", this.filterKeys, this);
40645         } else {
40646             this.inputEl().relayEvent('keypress', this);
40647         }
40648         
40649         var allowed = "0123456789";
40650         
40651         if(this.allowDecimals){
40652             allowed += this.decimalSeparator;
40653         }
40654         
40655         if(this.allowNegative){
40656             allowed += "-";
40657         }
40658         
40659         if(this.thousandsDelimiter) {
40660             allowed += ",";
40661         }
40662         
40663         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40664         
40665         var keyPress = function(e){
40666             
40667             var k = e.getKey();
40668             
40669             var c = e.getCharCode();
40670             
40671             if(
40672                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40673                     allowed.indexOf(String.fromCharCode(c)) === -1
40674             ){
40675                 e.stopEvent();
40676                 return;
40677             }
40678             
40679             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40680                 return;
40681             }
40682             
40683             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40684                 e.stopEvent();
40685             }
40686         };
40687         
40688         this.inputEl().on("keypress", keyPress, this);
40689         
40690     },
40691     
40692     onTriggerClick : function(e)
40693     {   
40694         if(this.disabled){
40695             return;
40696         }
40697         
40698         this.page = 0;
40699         this.loadNext = false;
40700         
40701         if(this.isExpanded()){
40702             this.collapse();
40703             return;
40704         }
40705         
40706         this.hasFocus = true;
40707         
40708         if(this.triggerAction == 'all') {
40709             this.doQuery(this.allQuery, true);
40710             return;
40711         }
40712         
40713         this.doQuery(this.getRawValue());
40714     },
40715     
40716     getCurrency : function()
40717     {   
40718         var v = this.currencyEl().getValue();
40719         
40720         return v;
40721     },
40722     
40723     restrictHeight : function()
40724     {
40725         this.list.alignTo(this.currencyEl(), this.listAlign);
40726         this.list.alignTo(this.currencyEl(), this.listAlign);
40727     },
40728     
40729     onViewClick : function(view, doFocus, el, e)
40730     {
40731         var index = this.view.getSelectedIndexes()[0];
40732         
40733         var r = this.store.getAt(index);
40734         
40735         if(r){
40736             this.onSelect(r, index);
40737         }
40738     },
40739     
40740     onSelect : function(record, index){
40741         
40742         if(this.fireEvent('beforeselect', this, record, index) !== false){
40743         
40744             this.setFromCurrencyData(index > -1 ? record.data : false);
40745             
40746             this.collapse();
40747             
40748             this.fireEvent('select', this, record, index);
40749         }
40750     },
40751     
40752     setFromCurrencyData : function(o)
40753     {
40754         var currency = '';
40755         
40756         this.lastCurrency = o;
40757         
40758         if (this.currencyField) {
40759             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40760         } else {
40761             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40762         }
40763         
40764         this.lastSelectionText = currency;
40765         
40766         //setting default currency
40767         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40768             this.setCurrency(this.defaultCurrency);
40769             return;
40770         }
40771         
40772         this.setCurrency(currency);
40773     },
40774     
40775     setFromData : function(o)
40776     {
40777         var c = {};
40778         
40779         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40780         
40781         this.setFromCurrencyData(c);
40782         
40783         var value = '';
40784         
40785         if (this.name) {
40786             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40787         } else {
40788             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40789         }
40790         
40791         this.setValue(value);
40792         
40793     },
40794     
40795     setCurrency : function(v)
40796     {   
40797         this.currencyValue = v;
40798         
40799         if(this.rendered){
40800             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40801             this.validate();
40802         }
40803     },
40804     
40805     setValue : function(v)
40806     {
40807         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40808         
40809         this.value = v;
40810         
40811         if(this.rendered){
40812             
40813             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40814             
40815             this.inputEl().dom.value = (v == '') ? '' :
40816                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40817             
40818             if(!this.allowZero && v === '0') {
40819                 this.hiddenEl().dom.value = '';
40820                 this.inputEl().dom.value = '';
40821             }
40822             
40823             this.validate();
40824         }
40825     },
40826     
40827     getRawValue : function()
40828     {
40829         var v = this.inputEl().getValue();
40830         
40831         return v;
40832     },
40833     
40834     getValue : function()
40835     {
40836         return this.fixPrecision(this.parseValue(this.getRawValue()));
40837     },
40838     
40839     parseValue : function(value)
40840     {
40841         if(this.thousandsDelimiter) {
40842             value += "";
40843             r = new RegExp(",", "g");
40844             value = value.replace(r, "");
40845         }
40846         
40847         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40848         return isNaN(value) ? '' : value;
40849         
40850     },
40851     
40852     fixPrecision : function(value)
40853     {
40854         if(this.thousandsDelimiter) {
40855             value += "";
40856             r = new RegExp(",", "g");
40857             value = value.replace(r, "");
40858         }
40859         
40860         var nan = isNaN(value);
40861         
40862         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40863             return nan ? '' : value;
40864         }
40865         return parseFloat(value).toFixed(this.decimalPrecision);
40866     },
40867     
40868     decimalPrecisionFcn : function(v)
40869     {
40870         return Math.floor(v);
40871     },
40872     
40873     validateValue : function(value)
40874     {
40875         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40876             return false;
40877         }
40878         
40879         var num = this.parseValue(value);
40880         
40881         if(isNaN(num)){
40882             this.markInvalid(String.format(this.nanText, value));
40883             return false;
40884         }
40885         
40886         if(num < this.minValue){
40887             this.markInvalid(String.format(this.minText, this.minValue));
40888             return false;
40889         }
40890         
40891         if(num > this.maxValue){
40892             this.markInvalid(String.format(this.maxText, this.maxValue));
40893             return false;
40894         }
40895         
40896         return true;
40897     },
40898     
40899     validate : function()
40900     {
40901         if(this.disabled || this.allowBlank){
40902             this.markValid();
40903             return true;
40904         }
40905         
40906         var currency = this.getCurrency();
40907         
40908         if(this.validateValue(this.getRawValue()) && currency.length){
40909             this.markValid();
40910             return true;
40911         }
40912         
40913         this.markInvalid();
40914         return false;
40915     },
40916     
40917     getName: function()
40918     {
40919         return this.name;
40920     },
40921     
40922     beforeBlur : function()
40923     {
40924         if(!this.castInt){
40925             return;
40926         }
40927         
40928         var v = this.parseValue(this.getRawValue());
40929         
40930         if(v || v == 0){
40931             this.setValue(v);
40932         }
40933     },
40934     
40935     onBlur : function()
40936     {
40937         this.beforeBlur();
40938         
40939         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40940             //this.el.removeClass(this.focusClass);
40941         }
40942         
40943         this.hasFocus = false;
40944         
40945         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40946             this.validate();
40947         }
40948         
40949         var v = this.getValue();
40950         
40951         if(String(v) !== String(this.startValue)){
40952             this.fireEvent('change', this, v, this.startValue);
40953         }
40954         
40955         this.fireEvent("blur", this);
40956     },
40957     
40958     inputEl : function()
40959     {
40960         return this.el.select('.roo-money-amount-input', true).first();
40961     },
40962     
40963     currencyEl : function()
40964     {
40965         return this.el.select('.roo-money-currency-input', true).first();
40966     },
40967     
40968     hiddenEl : function()
40969     {
40970         return this.el.select('input.hidden-number-input',true).first();
40971     }
40972     
40973 });