sync
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368     
369     /**
370      * Set the element that will be used to show or hide
371      */
372     setVisibilityEl : function(el)
373     {
374         this.visibilityEl = el;
375     },
376     
377      /**
378      * Get the element that will be used to show or hide
379      */
380     getVisibilityEl : function()
381     {
382         if (typeof(this.visibilityEl) == 'object') {
383             return this.visibilityEl;
384         }
385         
386         if (typeof(this.visibilityEl) == 'string') {
387             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
388         }
389         
390         return this.getEl();
391     },
392     
393     /**
394      * Show a component - removes 'hidden' class
395      */
396     show : function()
397     {
398         if(!this.getVisibilityEl()){
399             return;
400         }
401          
402         this.getVisibilityEl().removeClass('hidden');
403         
404         this.fireEvent('show', this);
405         
406         
407     },
408     /**
409      * Hide a component - adds 'hidden' class
410      */
411     hide: function()
412     {
413         if(!this.getVisibilityEl()){
414             return;
415         }
416         
417         this.getVisibilityEl().addClass('hidden');
418         
419         this.fireEvent('hide', this);
420         
421     }
422 });
423
424  /*
425  * - LGPL
426  *
427  * Body
428  *
429  */
430
431 /**
432  * @class Roo.bootstrap.Body
433  * @extends Roo.bootstrap.Component
434  * Bootstrap Body class
435  *
436  * @constructor
437  * Create a new body
438  * @param {Object} config The config object
439  */
440
441 Roo.bootstrap.Body = function(config){
442
443     config = config || {};
444
445     Roo.bootstrap.Body.superclass.constructor.call(this, config);
446     this.el = Roo.get(config.el ? config.el : document.body );
447     if (this.cls && this.cls.length) {
448         Roo.get(document.body).addClass(this.cls);
449     }
450 };
451
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
453
454     is_body : true,// just to make sure it's constructed?
455
456         autoCreate : {
457         cls: 'container'
458     },
459     onRender : function(ct, position)
460     {
461        /* Roo.log("Roo.bootstrap.Body - onRender");
462         if (this.cls && this.cls.length) {
463             Roo.get(document.body).addClass(this.cls);
464         }
465         // style??? xttr???
466         */
467     }
468
469
470
471
472 });
473 /*
474  * - LGPL
475  *
476  * button group
477  * 
478  */
479
480
481 /**
482  * @class Roo.bootstrap.ButtonGroup
483  * @extends Roo.bootstrap.Component
484  * Bootstrap ButtonGroup class
485  * @cfg {String} size lg | sm | xs (default empty normal)
486  * @cfg {String} align vertical | justified  (default none)
487  * @cfg {String} direction up | down (default down)
488  * @cfg {Boolean} toolbar false | true
489  * @cfg {Boolean} btn true | false
490  * 
491  * 
492  * @constructor
493  * Create a new Input
494  * @param {Object} config The config object
495  */
496
497 Roo.bootstrap.ButtonGroup = function(config){
498     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
499 };
500
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
502     
503     size: '',
504     align: '',
505     direction: '',
506     toolbar: false,
507     btn: true,
508
509     getAutoCreate : function(){
510         var cfg = {
511             cls: 'btn-group',
512             html : null
513         };
514         
515         cfg.html = this.html || cfg.html;
516         
517         if (this.toolbar) {
518             cfg = {
519                 cls: 'btn-toolbar',
520                 html: null
521             };
522             
523             return cfg;
524         }
525         
526         if (['vertical','justified'].indexOf(this.align)!==-1) {
527             cfg.cls = 'btn-group-' + this.align;
528             
529             if (this.align == 'justified') {
530                 console.log(this.items);
531             }
532         }
533         
534         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535             cfg.cls += ' btn-group-' + this.size;
536         }
537         
538         if (this.direction == 'up') {
539             cfg.cls += ' dropup' ;
540         }
541         
542         return cfg;
543     }
544    
545 });
546
547  /*
548  * - LGPL
549  *
550  * button
551  * 
552  */
553
554 /**
555  * @class Roo.bootstrap.Button
556  * @extends Roo.bootstrap.Component
557  * Bootstrap Button class
558  * @cfg {String} html The button content
559  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
560  * @cfg {String} size ( lg | sm | xs)
561  * @cfg {String} tag ( a | input | submit)
562  * @cfg {String} href empty or href
563  * @cfg {Boolean} disabled default false;
564  * @cfg {Boolean} isClose default false;
565  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566  * @cfg {String} badge text for badge
567  * @cfg {String} theme (default|glow)  
568  * @cfg {Boolean} inverse dark themed version
569  * @cfg {Boolean} toggle is it a slidy toggle button
570  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571  * @cfg {String} ontext text for on slidy toggle state
572  * @cfg {String} offtext text for off slidy toggle state
573  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
574  * @cfg {Boolean} removeClass remove the standard class..
575  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
576  * 
577  * @constructor
578  * Create a new button
579  * @param {Object} config The config object
580  */
581
582
583 Roo.bootstrap.Button = function(config){
584     Roo.bootstrap.Button.superclass.constructor.call(this, config);
585     this.weightClass = ["btn-default", 
586                        "btn-primary", 
587                        "btn-success", 
588                        "btn-info", 
589                        "btn-warning",
590                        "btn-danger",
591                        "btn-link"
592                       ],  
593     this.addEvents({
594         // raw events
595         /**
596          * @event click
597          * When a butotn is pressed
598          * @param {Roo.bootstrap.Button} btn
599          * @param {Roo.EventObject} e
600          */
601         "click" : true,
602          /**
603          * @event toggle
604          * After the button has been toggles
605          * @param {Roo.bootstrap.Button} btn
606          * @param {Roo.EventObject} e
607          * @param {boolean} pressed (also available as button.pressed)
608          */
609         "toggle" : true
610     });
611 };
612
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
614     html: false,
615     active: false,
616     weight: '',
617     size: '',
618     tag: 'button',
619     href: '',
620     disabled: false,
621     isClose: false,
622     glyphicon: '',
623     badge: '',
624     theme: 'default',
625     inverse: false,
626     
627     toggle: false,
628     ontext: 'ON',
629     offtext: 'OFF',
630     defaulton: true,
631     preventDefault: true,
632     removeClass: false,
633     name: false,
634     target: false,
635      
636     pressed : null,
637      
638     
639     getAutoCreate : function(){
640         
641         var cfg = {
642             tag : 'button',
643             cls : 'roo-button',
644             html: ''
645         };
646         
647         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
649             this.tag = 'button';
650         } else {
651             cfg.tag = this.tag;
652         }
653         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
654         
655         if (this.toggle == true) {
656             cfg={
657                 tag: 'div',
658                 cls: 'slider-frame roo-button',
659                 cn: [
660                     {
661                         tag: 'span',
662                         'data-on-text':'ON',
663                         'data-off-text':'OFF',
664                         cls: 'slider-button',
665                         html: this.offtext
666                     }
667                 ]
668             };
669             
670             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671                 cfg.cls += ' '+this.weight;
672             }
673             
674             return cfg;
675         }
676         
677         if (this.isClose) {
678             cfg.cls += ' close';
679             
680             cfg["aria-hidden"] = true;
681             
682             cfg.html = "&times;";
683             
684             return cfg;
685         }
686         
687          
688         if (this.theme==='default') {
689             cfg.cls = 'btn roo-button';
690             
691             //if (this.parentType != 'Navbar') {
692             this.weight = this.weight.length ?  this.weight : 'default';
693             //}
694             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
695                 
696                 cfg.cls += ' btn-' + this.weight;
697             }
698         } else if (this.theme==='glow') {
699             
700             cfg.tag = 'a';
701             cfg.cls = 'btn-glow roo-button';
702             
703             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
704                 
705                 cfg.cls += ' ' + this.weight;
706             }
707         }
708    
709         
710         if (this.inverse) {
711             this.cls += ' inverse';
712         }
713         
714         
715         if (this.active || this.pressed === true) {
716             cfg.cls += ' active';
717         }
718         
719         if (this.disabled) {
720             cfg.disabled = 'disabled';
721         }
722         
723         if (this.items) {
724             Roo.log('changing to ul' );
725             cfg.tag = 'ul';
726             this.glyphicon = 'caret';
727         }
728         
729         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
730          
731         //gsRoo.log(this.parentType);
732         if (this.parentType === 'Navbar' && !this.parent().bar) {
733             Roo.log('changing to li?');
734             
735             cfg.tag = 'li';
736             
737             cfg.cls = '';
738             cfg.cn =  [{
739                 tag : 'a',
740                 cls : 'roo-button',
741                 html : this.html,
742                 href : this.href || '#'
743             }];
744             if (this.menu) {
745                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
746                 cfg.cls += ' dropdown';
747             }   
748             
749             delete cfg.html;
750             
751         }
752         
753        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
754         
755         if (this.glyphicon) {
756             cfg.html = ' ' + cfg.html;
757             
758             cfg.cn = [
759                 {
760                     tag: 'span',
761                     cls: 'glyphicon glyphicon-' + this.glyphicon
762                 }
763             ];
764         }
765         
766         if (this.badge) {
767             cfg.html += ' ';
768             
769             cfg.tag = 'a';
770             
771 //            cfg.cls='btn roo-button';
772             
773             cfg.href=this.href;
774             
775             var value = cfg.html;
776             
777             if(this.glyphicon){
778                 value = {
779                             tag: 'span',
780                             cls: 'glyphicon glyphicon-' + this.glyphicon,
781                             html: this.html
782                         };
783                 
784             }
785             
786             cfg.cn = [
787                 value,
788                 {
789                     tag: 'span',
790                     cls: 'badge',
791                     html: this.badge
792                 }
793             ];
794             
795             cfg.html='';
796         }
797         
798         if (this.menu) {
799             cfg.cls += ' dropdown';
800             cfg.html = typeof(cfg.html) != 'undefined' ?
801                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
802         }
803         
804         if (cfg.tag !== 'a' && this.href !== '') {
805             throw "Tag must be a to set href.";
806         } else if (this.href.length > 0) {
807             cfg.href = this.href;
808         }
809         
810         if(this.removeClass){
811             cfg.cls = '';
812         }
813         
814         if(this.target){
815             cfg.target = this.target;
816         }
817         
818         return cfg;
819     },
820     initEvents: function() {
821        // Roo.log('init events?');
822 //        Roo.log(this.el.dom);
823         // add the menu...
824         
825         if (typeof (this.menu) != 'undefined') {
826             this.menu.parentType = this.xtype;
827             this.menu.triggerEl = this.el;
828             this.addxtype(Roo.apply({}, this.menu));
829         }
830
831
832        if (this.el.hasClass('roo-button')) {
833             this.el.on('click', this.onClick, this);
834        } else {
835             this.el.select('.roo-button').on('click', this.onClick, this);
836        }
837        
838        if(this.removeClass){
839            this.el.on('click', this.onClick, this);
840        }
841        
842        this.el.enableDisplayMode();
843         
844     },
845     onClick : function(e)
846     {
847         if (this.disabled) {
848             return;
849         }
850         
851         Roo.log('button on click ');
852         if(this.preventDefault){
853             e.preventDefault();
854         }
855         
856         if (this.pressed === true || this.pressed === false) {
857             this.toggleActive(e);
858         }
859         
860         
861         this.fireEvent('click', this, e);
862     },
863     
864     /**
865      * Enables this button
866      */
867     enable : function()
868     {
869         this.disabled = false;
870         this.el.removeClass('disabled');
871     },
872     
873     /**
874      * Disable this button
875      */
876     disable : function()
877     {
878         this.disabled = true;
879         this.el.addClass('disabled');
880     },
881      /**
882      * sets the active state on/off, 
883      * @param {Boolean} state (optional) Force a particular state
884      */
885     setActive : function(v) {
886         
887         this.el[v ? 'addClass' : 'removeClass']('active');
888         this.pressed = v;
889     },
890      /**
891      * toggles the current active state 
892      */
893     toggleActive : function(e)
894     {
895         this.setActive(!this.pressed);
896         this.fireEvent('toggle', this, e, !this.pressed);
897     },
898      /**
899      * get the current active state
900      * @return {boolean} true if it's active
901      */
902     isActive : function()
903     {
904         return this.el.hasClass('active');
905     },
906     /**
907      * set the text of the first selected button
908      */
909     setText : function(str)
910     {
911         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
912     },
913     /**
914      * get the text of the first selected button
915      */
916     getText : function()
917     {
918         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
919     },
920     hide: function() {
921        
922      
923         this.el.hide();   
924     },
925     show: function() {
926        
927         this.el.show();   
928     },
929     setWeight : function(str)
930     {
931         this.el.removeClass(this.weightClass);
932         this.el.addClass('btn-' + str);        
933     }
934     
935     
936 });
937
938  /*
939  * - LGPL
940  *
941  * column
942  * 
943  */
944
945 /**
946  * @class Roo.bootstrap.Column
947  * @extends Roo.bootstrap.Component
948  * Bootstrap Column class
949  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
950  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
951  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
952  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
953  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
954  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
955  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
956  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
957  *
958  * 
959  * @cfg {Boolean} hidden (true|false) hide the element
960  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
961  * @cfg {String} fa (ban|check|...) font awesome icon
962  * @cfg {Number} fasize (1|2|....) font awsome size
963
964  * @cfg {String} icon (info-sign|check|...) glyphicon name
965
966  * @cfg {String} html content of column.
967  * 
968  * @constructor
969  * Create a new Column
970  * @param {Object} config The config object
971  */
972
973 Roo.bootstrap.Column = function(config){
974     Roo.bootstrap.Column.superclass.constructor.call(this, config);
975 };
976
977 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
978     
979     xs: false,
980     sm: false,
981     md: false,
982     lg: false,
983     xsoff: false,
984     smoff: false,
985     mdoff: false,
986     lgoff: false,
987     html: '',
988     offset: 0,
989     alert: false,
990     fa: false,
991     icon : false,
992     hidden : false,
993     fasize : 1,
994     
995     getAutoCreate : function(){
996         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
997         
998         cfg = {
999             tag: 'div',
1000             cls: 'column'
1001         };
1002         
1003         var settings=this;
1004         ['xs','sm','md','lg'].map(function(size){
1005             //Roo.log( size + ':' + settings[size]);
1006             
1007             if (settings[size+'off'] !== false) {
1008                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1009             }
1010             
1011             if (settings[size] === false) {
1012                 return;
1013             }
1014             
1015             if (!settings[size]) { // 0 = hidden
1016                 cfg.cls += ' hidden-' + size;
1017                 return;
1018             }
1019             cfg.cls += ' col-' + size + '-' + settings[size];
1020             
1021         });
1022         
1023         if (this.hidden) {
1024             cfg.cls += ' hidden';
1025         }
1026         
1027         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1028             cfg.cls +=' alert alert-' + this.alert;
1029         }
1030         
1031         
1032         if (this.html.length) {
1033             cfg.html = this.html;
1034         }
1035         if (this.fa) {
1036             var fasize = '';
1037             if (this.fasize > 1) {
1038                 fasize = ' fa-' + this.fasize + 'x';
1039             }
1040             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1041             
1042             
1043         }
1044         if (this.icon) {
1045             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1046         }
1047         
1048         return cfg;
1049     }
1050    
1051 });
1052
1053  
1054
1055  /*
1056  * - LGPL
1057  *
1058  * page container.
1059  * 
1060  */
1061
1062
1063 /**
1064  * @class Roo.bootstrap.Container
1065  * @extends Roo.bootstrap.Component
1066  * Bootstrap Container class
1067  * @cfg {Boolean} jumbotron is it a jumbotron element
1068  * @cfg {String} html content of element
1069  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1070  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1071  * @cfg {String} header content of header (for panel)
1072  * @cfg {String} footer content of footer (for panel)
1073  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1074  * @cfg {String} tag (header|aside|section) type of HTML tag.
1075  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1076  * @cfg {String} fa font awesome icon
1077  * @cfg {String} icon (info-sign|check|...) glyphicon name
1078  * @cfg {Boolean} hidden (true|false) hide the element
1079  * @cfg {Boolean} expandable (true|false) default false
1080  * @cfg {Boolean} expanded (true|false) default true
1081  * @cfg {String} rheader contet on the right of header
1082  * @cfg {Boolean} clickable (true|false) default false
1083
1084  *     
1085  * @constructor
1086  * Create a new Container
1087  * @param {Object} config The config object
1088  */
1089
1090 Roo.bootstrap.Container = function(config){
1091     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1092     
1093     this.addEvents({
1094         // raw events
1095          /**
1096          * @event expand
1097          * After the panel has been expand
1098          * 
1099          * @param {Roo.bootstrap.Container} this
1100          */
1101         "expand" : true,
1102         /**
1103          * @event collapse
1104          * After the panel has been collapsed
1105          * 
1106          * @param {Roo.bootstrap.Container} this
1107          */
1108         "collapse" : true,
1109         /**
1110          * @event click
1111          * When a element is chick
1112          * @param {Roo.bootstrap.Container} this
1113          * @param {Roo.EventObject} e
1114          */
1115         "click" : true
1116     });
1117 };
1118
1119 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1120     
1121     jumbotron : false,
1122     well: '',
1123     panel : '',
1124     header: '',
1125     footer : '',
1126     sticky: '',
1127     tag : false,
1128     alert : false,
1129     fa: false,
1130     icon : false,
1131     expandable : false,
1132     rheader : '',
1133     expanded : true,
1134     clickable: false,
1135   
1136      
1137     getChildContainer : function() {
1138         
1139         if(!this.el){
1140             return false;
1141         }
1142         
1143         if (this.panel.length) {
1144             return this.el.select('.panel-body',true).first();
1145         }
1146         
1147         return this.el;
1148     },
1149     
1150     
1151     getAutoCreate : function(){
1152         
1153         var cfg = {
1154             tag : this.tag || 'div',
1155             html : '',
1156             cls : ''
1157         };
1158         if (this.jumbotron) {
1159             cfg.cls = 'jumbotron';
1160         }
1161         
1162         
1163         
1164         // - this is applied by the parent..
1165         //if (this.cls) {
1166         //    cfg.cls = this.cls + '';
1167         //}
1168         
1169         if (this.sticky.length) {
1170             
1171             var bd = Roo.get(document.body);
1172             if (!bd.hasClass('bootstrap-sticky')) {
1173                 bd.addClass('bootstrap-sticky');
1174                 Roo.select('html',true).setStyle('height', '100%');
1175             }
1176              
1177             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1178         }
1179         
1180         
1181         if (this.well.length) {
1182             switch (this.well) {
1183                 case 'lg':
1184                 case 'sm':
1185                     cfg.cls +=' well well-' +this.well;
1186                     break;
1187                 default:
1188                     cfg.cls +=' well';
1189                     break;
1190             }
1191         }
1192         
1193         if (this.hidden) {
1194             cfg.cls += ' hidden';
1195         }
1196         
1197         
1198         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1199             cfg.cls +=' alert alert-' + this.alert;
1200         }
1201         
1202         var body = cfg;
1203         
1204         if (this.panel.length) {
1205             cfg.cls += ' panel panel-' + this.panel;
1206             cfg.cn = [];
1207             if (this.header.length) {
1208                 
1209                 var h = [];
1210                 
1211                 if(this.expandable){
1212                     
1213                     cfg.cls = cfg.cls + ' expandable';
1214                     
1215                     h.push({
1216                         tag: 'i',
1217                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1218                     });
1219                     
1220                 }
1221                 
1222                 h.push(
1223                     {
1224                         tag: 'span',
1225                         cls : 'panel-title',
1226                         html : (this.expandable ? '&nbsp;' : '') + this.header
1227                     },
1228                     {
1229                         tag: 'span',
1230                         cls: 'panel-header-right',
1231                         html: this.rheader
1232                     }
1233                 );
1234                 
1235                 cfg.cn.push({
1236                     cls : 'panel-heading',
1237                     style : this.expandable ? 'cursor: pointer' : '',
1238                     cn : h
1239                 });
1240                 
1241             }
1242             
1243             body = false;
1244             cfg.cn.push({
1245                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1246                 html : this.html
1247             });
1248             
1249             
1250             if (this.footer.length) {
1251                 cfg.cn.push({
1252                     cls : 'panel-footer',
1253                     html : this.footer
1254                     
1255                 });
1256             }
1257             
1258         }
1259         
1260         if (body) {
1261             body.html = this.html || cfg.html;
1262             // prefix with the icons..
1263             if (this.fa) {
1264                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1265             }
1266             if (this.icon) {
1267                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1268             }
1269             
1270             
1271         }
1272         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1273             cfg.cls =  'container';
1274         }
1275         
1276         return cfg;
1277     },
1278     
1279     initEvents: function() 
1280     {
1281         if(this.expandable){
1282             var headerEl = this.headerEl();
1283         
1284             if(headerEl){
1285                 headerEl.on('click', this.onToggleClick, this);
1286             }
1287         }
1288         
1289         if(this.clickable){
1290             this.el.on('click', this.onClick, this);
1291         }
1292         
1293     },
1294     
1295     onToggleClick : function()
1296     {
1297         var headerEl = this.headerEl();
1298         
1299         if(!headerEl){
1300             return;
1301         }
1302         
1303         if(this.expanded){
1304             this.collapse();
1305             return;
1306         }
1307         
1308         this.expand();
1309     },
1310     
1311     expand : function()
1312     {
1313         if(this.fireEvent('expand', this)) {
1314             
1315             this.expanded = true;
1316             
1317             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1318             
1319             this.el.select('.panel-body',true).first().removeClass('hide');
1320             
1321             var toggleEl = this.toggleEl();
1322
1323             if(!toggleEl){
1324                 return;
1325             }
1326
1327             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1328         }
1329         
1330     },
1331     
1332     collapse : function()
1333     {
1334         if(this.fireEvent('collapse', this)) {
1335             
1336             this.expanded = false;
1337             
1338             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1339             this.el.select('.panel-body',true).first().addClass('hide');
1340         
1341             var toggleEl = this.toggleEl();
1342
1343             if(!toggleEl){
1344                 return;
1345             }
1346
1347             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1348         }
1349     },
1350     
1351     toggleEl : function()
1352     {
1353         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1354             return;
1355         }
1356         
1357         return this.el.select('.panel-heading .fa',true).first();
1358     },
1359     
1360     headerEl : function()
1361     {
1362         if(!this.el || !this.panel.length || !this.header.length){
1363             return;
1364         }
1365         
1366         return this.el.select('.panel-heading',true).first()
1367     },
1368     
1369     bodyEl : function()
1370     {
1371         if(!this.el || !this.panel.length){
1372             return;
1373         }
1374         
1375         return this.el.select('.panel-body',true).first()
1376     },
1377     
1378     titleEl : function()
1379     {
1380         if(!this.el || !this.panel.length || !this.header.length){
1381             return;
1382         }
1383         
1384         return this.el.select('.panel-title',true).first();
1385     },
1386     
1387     setTitle : function(v)
1388     {
1389         var titleEl = this.titleEl();
1390         
1391         if(!titleEl){
1392             return;
1393         }
1394         
1395         titleEl.dom.innerHTML = v;
1396     },
1397     
1398     getTitle : function()
1399     {
1400         
1401         var titleEl = this.titleEl();
1402         
1403         if(!titleEl){
1404             return '';
1405         }
1406         
1407         return titleEl.dom.innerHTML;
1408     },
1409     
1410     setRightTitle : function(v)
1411     {
1412         var t = this.el.select('.panel-header-right',true).first();
1413         
1414         if(!t){
1415             return;
1416         }
1417         
1418         t.dom.innerHTML = v;
1419     },
1420     
1421     onClick : function(e)
1422     {
1423         e.preventDefault();
1424         
1425         this.fireEvent('click', this, e);
1426     }
1427 });
1428
1429  /*
1430  * - LGPL
1431  *
1432  * image
1433  * 
1434  */
1435
1436
1437 /**
1438  * @class Roo.bootstrap.Img
1439  * @extends Roo.bootstrap.Component
1440  * Bootstrap Img class
1441  * @cfg {Boolean} imgResponsive false | true
1442  * @cfg {String} border rounded | circle | thumbnail
1443  * @cfg {String} src image source
1444  * @cfg {String} alt image alternative text
1445  * @cfg {String} href a tag href
1446  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1447  * @cfg {String} xsUrl xs image source
1448  * @cfg {String} smUrl sm image source
1449  * @cfg {String} mdUrl md image source
1450  * @cfg {String} lgUrl lg image source
1451  * 
1452  * @constructor
1453  * Create a new Input
1454  * @param {Object} config The config object
1455  */
1456
1457 Roo.bootstrap.Img = function(config){
1458     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1459     
1460     this.addEvents({
1461         // img events
1462         /**
1463          * @event click
1464          * The img click event for the img.
1465          * @param {Roo.EventObject} e
1466          */
1467         "click" : true
1468     });
1469 };
1470
1471 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1472     
1473     imgResponsive: true,
1474     border: '',
1475     src: 'about:blank',
1476     href: false,
1477     target: false,
1478     xsUrl: '',
1479     smUrl: '',
1480     mdUrl: '',
1481     lgUrl: '',
1482
1483     getAutoCreate : function()
1484     {   
1485         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1486             return this.createSingleImg();
1487         }
1488         
1489         var cfg = {
1490             tag: 'div',
1491             cls: 'roo-image-responsive-group',
1492             cn: []
1493         };
1494         var _this = this;
1495         
1496         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1497             
1498             if(!_this[size + 'Url']){
1499                 return;
1500             }
1501             
1502             var img = {
1503                 tag: 'img',
1504                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1505                 html: _this.html || cfg.html,
1506                 src: _this[size + 'Url']
1507             };
1508             
1509             img.cls += ' roo-image-responsive-' + size;
1510             
1511             var s = ['xs', 'sm', 'md', 'lg'];
1512             
1513             s.splice(s.indexOf(size), 1);
1514             
1515             Roo.each(s, function(ss){
1516                 img.cls += ' hidden-' + ss;
1517             });
1518             
1519             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1520                 cfg.cls += ' img-' + _this.border;
1521             }
1522             
1523             if(_this.alt){
1524                 cfg.alt = _this.alt;
1525             }
1526             
1527             if(_this.href){
1528                 var a = {
1529                     tag: 'a',
1530                     href: _this.href,
1531                     cn: [
1532                         img
1533                     ]
1534                 };
1535
1536                 if(this.target){
1537                     a.target = _this.target;
1538                 }
1539             }
1540             
1541             cfg.cn.push((_this.href) ? a : img);
1542             
1543         });
1544         
1545         return cfg;
1546     },
1547     
1548     createSingleImg : function()
1549     {
1550         var cfg = {
1551             tag: 'img',
1552             cls: (this.imgResponsive) ? 'img-responsive' : '',
1553             html : null,
1554             src : 'about:blank'  // just incase src get's set to undefined?!?
1555         };
1556         
1557         cfg.html = this.html || cfg.html;
1558         
1559         cfg.src = this.src || cfg.src;
1560         
1561         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1562             cfg.cls += ' img-' + this.border;
1563         }
1564         
1565         if(this.alt){
1566             cfg.alt = this.alt;
1567         }
1568         
1569         if(this.href){
1570             var a = {
1571                 tag: 'a',
1572                 href: this.href,
1573                 cn: [
1574                     cfg
1575                 ]
1576             };
1577             
1578             if(this.target){
1579                 a.target = this.target;
1580             }
1581             
1582         }
1583         
1584         return (this.href) ? a : cfg;
1585     },
1586     
1587     initEvents: function() 
1588     {
1589         if(!this.href){
1590             this.el.on('click', this.onClick, this);
1591         }
1592         
1593     },
1594     
1595     onClick : function(e)
1596     {
1597         Roo.log('img onclick');
1598         this.fireEvent('click', this, e);
1599     },
1600     /**
1601      * Sets the url of the image - used to update it
1602      * @param {String} url the url of the image
1603      */
1604     
1605     setSrc : function(url)
1606     {
1607         this.src =  url;
1608         
1609         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1610             this.el.dom.src =  url;
1611             return;
1612         }
1613         
1614         this.el.select('img', true).first().dom.src =  url;
1615     }
1616     
1617     
1618    
1619 });
1620
1621  /*
1622  * - LGPL
1623  *
1624  * image
1625  * 
1626  */
1627
1628
1629 /**
1630  * @class Roo.bootstrap.Link
1631  * @extends Roo.bootstrap.Component
1632  * Bootstrap Link Class
1633  * @cfg {String} alt image alternative text
1634  * @cfg {String} href a tag href
1635  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1636  * @cfg {String} html the content of the link.
1637  * @cfg {String} anchor name for the anchor link
1638  * @cfg {String} fa - favicon
1639
1640  * @cfg {Boolean} preventDefault (true | false) default false
1641
1642  * 
1643  * @constructor
1644  * Create a new Input
1645  * @param {Object} config The config object
1646  */
1647
1648 Roo.bootstrap.Link = function(config){
1649     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1650     
1651     this.addEvents({
1652         // img events
1653         /**
1654          * @event click
1655          * The img click event for the img.
1656          * @param {Roo.EventObject} e
1657          */
1658         "click" : true
1659     });
1660 };
1661
1662 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1663     
1664     href: false,
1665     target: false,
1666     preventDefault: false,
1667     anchor : false,
1668     alt : false,
1669     fa: false,
1670
1671
1672     getAutoCreate : function()
1673     {
1674         var html = this.html || '';
1675         
1676         if (this.fa !== false) {
1677             html = '<i class="fa fa-' + this.fa + '"></i>';
1678         }
1679         var cfg = {
1680             tag: 'a'
1681         };
1682         // anchor's do not require html/href...
1683         if (this.anchor === false) {
1684             cfg.html = html;
1685             cfg.href = this.href || '#';
1686         } else {
1687             cfg.name = this.anchor;
1688             if (this.html !== false || this.fa !== false) {
1689                 cfg.html = html;
1690             }
1691             if (this.href !== false) {
1692                 cfg.href = this.href;
1693             }
1694         }
1695         
1696         if(this.alt !== false){
1697             cfg.alt = this.alt;
1698         }
1699         
1700         
1701         if(this.target !== false) {
1702             cfg.target = this.target;
1703         }
1704         
1705         return cfg;
1706     },
1707     
1708     initEvents: function() {
1709         
1710         if(!this.href || this.preventDefault){
1711             this.el.on('click', this.onClick, this);
1712         }
1713     },
1714     
1715     onClick : function(e)
1716     {
1717         if(this.preventDefault){
1718             e.preventDefault();
1719         }
1720         //Roo.log('img onclick');
1721         this.fireEvent('click', this, e);
1722     }
1723    
1724 });
1725
1726  /*
1727  * - LGPL
1728  *
1729  * header
1730  * 
1731  */
1732
1733 /**
1734  * @class Roo.bootstrap.Header
1735  * @extends Roo.bootstrap.Component
1736  * Bootstrap Header class
1737  * @cfg {String} html content of header
1738  * @cfg {Number} level (1|2|3|4|5|6) default 1
1739  * 
1740  * @constructor
1741  * Create a new Header
1742  * @param {Object} config The config object
1743  */
1744
1745
1746 Roo.bootstrap.Header  = function(config){
1747     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1748 };
1749
1750 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1751     
1752     //href : false,
1753     html : false,
1754     level : 1,
1755     
1756     
1757     
1758     getAutoCreate : function(){
1759         
1760         
1761         
1762         var cfg = {
1763             tag: 'h' + (1 *this.level),
1764             html: this.html || ''
1765         } ;
1766         
1767         return cfg;
1768     }
1769    
1770 });
1771
1772  
1773
1774  /*
1775  * Based on:
1776  * Ext JS Library 1.1.1
1777  * Copyright(c) 2006-2007, Ext JS, LLC.
1778  *
1779  * Originally Released Under LGPL - original licence link has changed is not relivant.
1780  *
1781  * Fork - LGPL
1782  * <script type="text/javascript">
1783  */
1784  
1785 /**
1786  * @class Roo.bootstrap.MenuMgr
1787  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1788  * @singleton
1789  */
1790 Roo.bootstrap.MenuMgr = function(){
1791    var menus, active, groups = {}, attached = false, lastShow = new Date();
1792
1793    // private - called when first menu is created
1794    function init(){
1795        menus = {};
1796        active = new Roo.util.MixedCollection();
1797        Roo.get(document).addKeyListener(27, function(){
1798            if(active.length > 0){
1799                hideAll();
1800            }
1801        });
1802    }
1803
1804    // private
1805    function hideAll(){
1806        if(active && active.length > 0){
1807            var c = active.clone();
1808            c.each(function(m){
1809                m.hide();
1810            });
1811        }
1812    }
1813
1814    // private
1815    function onHide(m){
1816        active.remove(m);
1817        if(active.length < 1){
1818            Roo.get(document).un("mouseup", onMouseDown);
1819             
1820            attached = false;
1821        }
1822    }
1823
1824    // private
1825    function onShow(m){
1826        var last = active.last();
1827        lastShow = new Date();
1828        active.add(m);
1829        if(!attached){
1830           Roo.get(document).on("mouseup", onMouseDown);
1831            
1832            attached = true;
1833        }
1834        if(m.parentMenu){
1835           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1836           m.parentMenu.activeChild = m;
1837        }else if(last && last.isVisible()){
1838           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1839        }
1840    }
1841
1842    // private
1843    function onBeforeHide(m){
1844        if(m.activeChild){
1845            m.activeChild.hide();
1846        }
1847        if(m.autoHideTimer){
1848            clearTimeout(m.autoHideTimer);
1849            delete m.autoHideTimer;
1850        }
1851    }
1852
1853    // private
1854    function onBeforeShow(m){
1855        var pm = m.parentMenu;
1856        if(!pm && !m.allowOtherMenus){
1857            hideAll();
1858        }else if(pm && pm.activeChild && active != m){
1859            pm.activeChild.hide();
1860        }
1861    }
1862
1863    // private this should really trigger on mouseup..
1864    function onMouseDown(e){
1865         Roo.log("on Mouse Up");
1866         
1867         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1868             Roo.log("MenuManager hideAll");
1869             hideAll();
1870             e.stopEvent();
1871         }
1872         
1873         
1874    }
1875
1876    // private
1877    function onBeforeCheck(mi, state){
1878        if(state){
1879            var g = groups[mi.group];
1880            for(var i = 0, l = g.length; i < l; i++){
1881                if(g[i] != mi){
1882                    g[i].setChecked(false);
1883                }
1884            }
1885        }
1886    }
1887
1888    return {
1889
1890        /**
1891         * Hides all menus that are currently visible
1892         */
1893        hideAll : function(){
1894             hideAll();  
1895        },
1896
1897        // private
1898        register : function(menu){
1899            if(!menus){
1900                init();
1901            }
1902            menus[menu.id] = menu;
1903            menu.on("beforehide", onBeforeHide);
1904            menu.on("hide", onHide);
1905            menu.on("beforeshow", onBeforeShow);
1906            menu.on("show", onShow);
1907            var g = menu.group;
1908            if(g && menu.events["checkchange"]){
1909                if(!groups[g]){
1910                    groups[g] = [];
1911                }
1912                groups[g].push(menu);
1913                menu.on("checkchange", onCheck);
1914            }
1915        },
1916
1917         /**
1918          * Returns a {@link Roo.menu.Menu} object
1919          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1920          * be used to generate and return a new Menu instance.
1921          */
1922        get : function(menu){
1923            if(typeof menu == "string"){ // menu id
1924                return menus[menu];
1925            }else if(menu.events){  // menu instance
1926                return menu;
1927            }
1928            /*else if(typeof menu.length == 'number'){ // array of menu items?
1929                return new Roo.bootstrap.Menu({items:menu});
1930            }else{ // otherwise, must be a config
1931                return new Roo.bootstrap.Menu(menu);
1932            }
1933            */
1934            return false;
1935        },
1936
1937        // private
1938        unregister : function(menu){
1939            delete menus[menu.id];
1940            menu.un("beforehide", onBeforeHide);
1941            menu.un("hide", onHide);
1942            menu.un("beforeshow", onBeforeShow);
1943            menu.un("show", onShow);
1944            var g = menu.group;
1945            if(g && menu.events["checkchange"]){
1946                groups[g].remove(menu);
1947                menu.un("checkchange", onCheck);
1948            }
1949        },
1950
1951        // private
1952        registerCheckable : function(menuItem){
1953            var g = menuItem.group;
1954            if(g){
1955                if(!groups[g]){
1956                    groups[g] = [];
1957                }
1958                groups[g].push(menuItem);
1959                menuItem.on("beforecheckchange", onBeforeCheck);
1960            }
1961        },
1962
1963        // private
1964        unregisterCheckable : function(menuItem){
1965            var g = menuItem.group;
1966            if(g){
1967                groups[g].remove(menuItem);
1968                menuItem.un("beforecheckchange", onBeforeCheck);
1969            }
1970        }
1971    };
1972 }();/*
1973  * - LGPL
1974  *
1975  * menu
1976  * 
1977  */
1978
1979 /**
1980  * @class Roo.bootstrap.Menu
1981  * @extends Roo.bootstrap.Component
1982  * Bootstrap Menu class - container for MenuItems
1983  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1984  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1985  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1986  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1987  * 
1988  * @constructor
1989  * Create a new Menu
1990  * @param {Object} config The config object
1991  */
1992
1993
1994 Roo.bootstrap.Menu = function(config){
1995     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1996     if (this.registerMenu && this.type != 'treeview')  {
1997         Roo.bootstrap.MenuMgr.register(this);
1998     }
1999     this.addEvents({
2000         /**
2001          * @event beforeshow
2002          * Fires before this menu is displayed
2003          * @param {Roo.menu.Menu} this
2004          */
2005         beforeshow : true,
2006         /**
2007          * @event beforehide
2008          * Fires before this menu is hidden
2009          * @param {Roo.menu.Menu} this
2010          */
2011         beforehide : true,
2012         /**
2013          * @event show
2014          * Fires after this menu is displayed
2015          * @param {Roo.menu.Menu} this
2016          */
2017         show : true,
2018         /**
2019          * @event hide
2020          * Fires after this menu is hidden
2021          * @param {Roo.menu.Menu} this
2022          */
2023         hide : true,
2024         /**
2025          * @event click
2026          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2027          * @param {Roo.menu.Menu} this
2028          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2029          * @param {Roo.EventObject} e
2030          */
2031         click : true,
2032         /**
2033          * @event mouseover
2034          * Fires when the mouse is hovering over this menu
2035          * @param {Roo.menu.Menu} this
2036          * @param {Roo.EventObject} e
2037          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2038          */
2039         mouseover : true,
2040         /**
2041          * @event mouseout
2042          * Fires when the mouse exits this menu
2043          * @param {Roo.menu.Menu} this
2044          * @param {Roo.EventObject} e
2045          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2046          */
2047         mouseout : true,
2048         /**
2049          * @event itemclick
2050          * Fires when a menu item contained in this menu is clicked
2051          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2052          * @param {Roo.EventObject} e
2053          */
2054         itemclick: true
2055     });
2056     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2057 };
2058
2059 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2060     
2061    /// html : false,
2062     //align : '',
2063     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2064     type: false,
2065     /**
2066      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2067      */
2068     registerMenu : true,
2069     
2070     menuItems :false, // stores the menu items..
2071     
2072     hidden:true,
2073         
2074     parentMenu : false,
2075     
2076     stopEvent : true,
2077     
2078     isLink : false,
2079     
2080     getChildContainer : function() {
2081         return this.el;  
2082     },
2083     
2084     getAutoCreate : function(){
2085          
2086         //if (['right'].indexOf(this.align)!==-1) {
2087         //    cfg.cn[1].cls += ' pull-right'
2088         //}
2089         
2090         
2091         var cfg = {
2092             tag : 'ul',
2093             cls : 'dropdown-menu' ,
2094             style : 'z-index:1000'
2095             
2096         };
2097         
2098         if (this.type === 'submenu') {
2099             cfg.cls = 'submenu active';
2100         }
2101         if (this.type === 'treeview') {
2102             cfg.cls = 'treeview-menu';
2103         }
2104         
2105         return cfg;
2106     },
2107     initEvents : function() {
2108         
2109        // Roo.log("ADD event");
2110        // Roo.log(this.triggerEl.dom);
2111         
2112         this.triggerEl.on('click', this.onTriggerClick, this);
2113         
2114         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2115         
2116         this.triggerEl.addClass('dropdown-toggle');
2117         
2118         if (Roo.isTouch) {
2119             this.el.on('touchstart'  , this.onTouch, this);
2120         }
2121         this.el.on('click' , this.onClick, this);
2122
2123         this.el.on("mouseover", this.onMouseOver, this);
2124         this.el.on("mouseout", this.onMouseOut, this);
2125         
2126     },
2127     
2128     findTargetItem : function(e)
2129     {
2130         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2131         if(!t){
2132             return false;
2133         }
2134         //Roo.log(t);         Roo.log(t.id);
2135         if(t && t.id){
2136             //Roo.log(this.menuitems);
2137             return this.menuitems.get(t.id);
2138             
2139             //return this.items.get(t.menuItemId);
2140         }
2141         
2142         return false;
2143     },
2144     
2145     onTouch : function(e) 
2146     {
2147         Roo.log("menu.onTouch");
2148         //e.stopEvent(); this make the user popdown broken
2149         this.onClick(e);
2150     },
2151     
2152     onClick : function(e)
2153     {
2154         Roo.log("menu.onClick");
2155         
2156         var t = this.findTargetItem(e);
2157         if(!t || t.isContainer){
2158             return;
2159         }
2160         Roo.log(e);
2161         /*
2162         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2163             if(t == this.activeItem && t.shouldDeactivate(e)){
2164                 this.activeItem.deactivate();
2165                 delete this.activeItem;
2166                 return;
2167             }
2168             if(t.canActivate){
2169                 this.setActiveItem(t, true);
2170             }
2171             return;
2172             
2173             
2174         }
2175         */
2176        
2177         Roo.log('pass click event');
2178         
2179         t.onClick(e);
2180         
2181         this.fireEvent("click", this, t, e);
2182         
2183         var _this = this;
2184         
2185         if(!t.href.length || t.href == '#'){
2186             (function() { _this.hide(); }).defer(100);
2187         }
2188         
2189     },
2190     
2191     onMouseOver : function(e){
2192         var t  = this.findTargetItem(e);
2193         //Roo.log(t);
2194         //if(t){
2195         //    if(t.canActivate && !t.disabled){
2196         //        this.setActiveItem(t, true);
2197         //    }
2198         //}
2199         
2200         this.fireEvent("mouseover", this, e, t);
2201     },
2202     isVisible : function(){
2203         return !this.hidden;
2204     },
2205      onMouseOut : function(e){
2206         var t  = this.findTargetItem(e);
2207         
2208         //if(t ){
2209         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2210         //        this.activeItem.deactivate();
2211         //        delete this.activeItem;
2212         //    }
2213         //}
2214         this.fireEvent("mouseout", this, e, t);
2215     },
2216     
2217     
2218     /**
2219      * Displays this menu relative to another element
2220      * @param {String/HTMLElement/Roo.Element} element The element to align to
2221      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2222      * the element (defaults to this.defaultAlign)
2223      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2224      */
2225     show : function(el, pos, parentMenu){
2226         this.parentMenu = parentMenu;
2227         if(!this.el){
2228             this.render();
2229         }
2230         this.fireEvent("beforeshow", this);
2231         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2232     },
2233      /**
2234      * Displays this menu at a specific xy position
2235      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2236      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2237      */
2238     showAt : function(xy, parentMenu, /* private: */_e){
2239         this.parentMenu = parentMenu;
2240         if(!this.el){
2241             this.render();
2242         }
2243         if(_e !== false){
2244             this.fireEvent("beforeshow", this);
2245             //xy = this.el.adjustForConstraints(xy);
2246         }
2247         
2248         //this.el.show();
2249         this.hideMenuItems();
2250         this.hidden = false;
2251         this.triggerEl.addClass('open');
2252         
2253         // reassign x when hitting right
2254         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2255             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2256         }
2257         
2258         // reassign y when hitting bottom
2259         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2260             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2261         }
2262         
2263         // but the list may align on trigger left or trigger top... should it be a properity?
2264         
2265         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2266             this.el.setXY(xy);
2267         }
2268         
2269         this.focus();
2270         this.fireEvent("show", this);
2271     },
2272     
2273     focus : function(){
2274         return;
2275         if(!this.hidden){
2276             this.doFocus.defer(50, this);
2277         }
2278     },
2279
2280     doFocus : function(){
2281         if(!this.hidden){
2282             this.focusEl.focus();
2283         }
2284     },
2285
2286     /**
2287      * Hides this menu and optionally all parent menus
2288      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2289      */
2290     hide : function(deep)
2291     {
2292         
2293         this.hideMenuItems();
2294         if(this.el && this.isVisible()){
2295             this.fireEvent("beforehide", this);
2296             if(this.activeItem){
2297                 this.activeItem.deactivate();
2298                 this.activeItem = null;
2299             }
2300             this.triggerEl.removeClass('open');;
2301             this.hidden = true;
2302             this.fireEvent("hide", this);
2303         }
2304         if(deep === true && this.parentMenu){
2305             this.parentMenu.hide(true);
2306         }
2307     },
2308     
2309     onTriggerClick : function(e)
2310     {
2311         Roo.log('trigger click');
2312         
2313         var target = e.getTarget();
2314         
2315         Roo.log(target.nodeName.toLowerCase());
2316         
2317         if(target.nodeName.toLowerCase() === 'i'){
2318             e.preventDefault();
2319         }
2320         
2321     },
2322     
2323     onTriggerPress  : function(e)
2324     {
2325         Roo.log('trigger press');
2326         //Roo.log(e.getTarget());
2327        // Roo.log(this.triggerEl.dom);
2328        
2329         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2330         var pel = Roo.get(e.getTarget());
2331         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2332             Roo.log('is treeview or dropdown?');
2333             return;
2334         }
2335         
2336         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2337             return;
2338         }
2339         
2340         if (this.isVisible()) {
2341             Roo.log('hide');
2342             this.hide();
2343         } else {
2344             Roo.log('show');
2345             this.show(this.triggerEl, false, false);
2346         }
2347         
2348         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2349             e.stopEvent();
2350         }
2351         
2352     },
2353        
2354     
2355     hideMenuItems : function()
2356     {
2357         Roo.log("hide Menu Items");
2358         if (!this.el) { 
2359             return;
2360         }
2361         //$(backdrop).remove()
2362         this.el.select('.open',true).each(function(aa) {
2363             
2364             aa.removeClass('open');
2365           //var parent = getParent($(this))
2366           //var relatedTarget = { relatedTarget: this }
2367           
2368            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2369           //if (e.isDefaultPrevented()) return
2370            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2371         });
2372     },
2373     addxtypeChild : function (tree, cntr) {
2374         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2375           
2376         this.menuitems.add(comp);
2377         return comp;
2378
2379     },
2380     getEl : function()
2381     {
2382         Roo.log(this.el);
2383         return this.el;
2384     },
2385     
2386     clear : function()
2387     {
2388         this.getEl().dom.innerHTML = '';
2389         this.menuitems.clear();
2390     }
2391 });
2392
2393  
2394  /*
2395  * - LGPL
2396  *
2397  * menu item
2398  * 
2399  */
2400
2401
2402 /**
2403  * @class Roo.bootstrap.MenuItem
2404  * @extends Roo.bootstrap.Component
2405  * Bootstrap MenuItem class
2406  * @cfg {String} html the menu label
2407  * @cfg {String} href the link
2408  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2409  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2410  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2411  * @cfg {String} fa favicon to show on left of menu item.
2412  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2413  * 
2414  * 
2415  * @constructor
2416  * Create a new MenuItem
2417  * @param {Object} config The config object
2418  */
2419
2420
2421 Roo.bootstrap.MenuItem = function(config){
2422     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2423     this.addEvents({
2424         // raw events
2425         /**
2426          * @event click
2427          * The raw click event for the entire grid.
2428          * @param {Roo.bootstrap.MenuItem} this
2429          * @param {Roo.EventObject} e
2430          */
2431         "click" : true
2432     });
2433 };
2434
2435 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2436     
2437     href : false,
2438     html : false,
2439     preventDefault: false,
2440     isContainer : false,
2441     active : false,
2442     fa: false,
2443     
2444     getAutoCreate : function(){
2445         
2446         if(this.isContainer){
2447             return {
2448                 tag: 'li',
2449                 cls: 'dropdown-menu-item'
2450             };
2451         }
2452         var ctag = {
2453             tag: 'span',
2454             html: 'Link'
2455         };
2456         
2457         var anc = {
2458             tag : 'a',
2459             href : '#',
2460             cn : [  ]
2461         };
2462         
2463         if (this.fa !== false) {
2464             anc.cn.push({
2465                 tag : 'i',
2466                 cls : 'fa fa-' + this.fa
2467             });
2468         }
2469         
2470         anc.cn.push(ctag);
2471         
2472         
2473         var cfg= {
2474             tag: 'li',
2475             cls: 'dropdown-menu-item',
2476             cn: [ anc ]
2477         };
2478         if (this.parent().type == 'treeview') {
2479             cfg.cls = 'treeview-menu';
2480         }
2481         if (this.active) {
2482             cfg.cls += ' active';
2483         }
2484         
2485         
2486         
2487         anc.href = this.href || cfg.cn[0].href ;
2488         ctag.html = this.html || cfg.cn[0].html ;
2489         return cfg;
2490     },
2491     
2492     initEvents: function()
2493     {
2494         if (this.parent().type == 'treeview') {
2495             this.el.select('a').on('click', this.onClick, this);
2496         }
2497         
2498         if (this.menu) {
2499             this.menu.parentType = this.xtype;
2500             this.menu.triggerEl = this.el;
2501             this.menu = this.addxtype(Roo.apply({}, this.menu));
2502         }
2503         
2504     },
2505     onClick : function(e)
2506     {
2507         Roo.log('item on click ');
2508         
2509         if(this.preventDefault){
2510             e.preventDefault();
2511         }
2512         //this.parent().hideMenuItems();
2513         
2514         this.fireEvent('click', this, e);
2515     },
2516     getEl : function()
2517     {
2518         return this.el;
2519     } 
2520 });
2521
2522  
2523
2524  /*
2525  * - LGPL
2526  *
2527  * menu separator
2528  * 
2529  */
2530
2531
2532 /**
2533  * @class Roo.bootstrap.MenuSeparator
2534  * @extends Roo.bootstrap.Component
2535  * Bootstrap MenuSeparator class
2536  * 
2537  * @constructor
2538  * Create a new MenuItem
2539  * @param {Object} config The config object
2540  */
2541
2542
2543 Roo.bootstrap.MenuSeparator = function(config){
2544     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2545 };
2546
2547 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2548     
2549     getAutoCreate : function(){
2550         var cfg = {
2551             cls: 'divider',
2552             tag : 'li'
2553         };
2554         
2555         return cfg;
2556     }
2557    
2558 });
2559
2560  
2561
2562  
2563 /*
2564 * Licence: LGPL
2565 */
2566
2567 /**
2568  * @class Roo.bootstrap.Modal
2569  * @extends Roo.bootstrap.Component
2570  * Bootstrap Modal class
2571  * @cfg {String} title Title of dialog
2572  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2573  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2574  * @cfg {Boolean} specificTitle default false
2575  * @cfg {Array} buttons Array of buttons or standard button set..
2576  * @cfg {String} buttonPosition (left|right|center) default right
2577  * @cfg {Boolean} animate default true
2578  * @cfg {Boolean} allow_close default true
2579  * @cfg {Boolean} fitwindow default false
2580  * @cfg {String} size (sm|lg) default empty
2581  * @cfg {Number} max_width set the max width of modal
2582  *
2583  *
2584  * @constructor
2585  * Create a new Modal Dialog
2586  * @param {Object} config The config object
2587  */
2588
2589 Roo.bootstrap.Modal = function(config){
2590     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2591     this.addEvents({
2592         // raw events
2593         /**
2594          * @event btnclick
2595          * The raw btnclick event for the button
2596          * @param {Roo.EventObject} e
2597          */
2598         "btnclick" : true,
2599         /**
2600          * @event resize
2601          * Fire when dialog resize
2602          * @param {Roo.bootstrap.Modal} this
2603          * @param {Roo.EventObject} e
2604          */
2605         "resize" : true
2606     });
2607     this.buttons = this.buttons || [];
2608
2609     if (this.tmpl) {
2610         this.tmpl = Roo.factory(this.tmpl);
2611     }
2612
2613 };
2614
2615 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2616
2617     title : 'test dialog',
2618
2619     buttons : false,
2620
2621     // set on load...
2622
2623     html: false,
2624
2625     tmp: false,
2626
2627     specificTitle: false,
2628
2629     buttonPosition: 'right',
2630
2631     allow_close : true,
2632
2633     animate : true,
2634
2635     fitwindow: false,
2636
2637
2638      // private
2639     dialogEl: false,
2640     bodyEl:  false,
2641     footerEl:  false,
2642     titleEl:  false,
2643     closeEl:  false,
2644
2645     size: '',
2646     
2647     max_width: 0,
2648
2649
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             
2834             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2835             
2836             // for fix height
2837             if(this.height) {
2838                 this.setSize(w, this.height);
2839                 return;
2840             }
2841             
2842             var body_childs = this.bodyEl.dom.childNodes;
2843             var full_height = this.headerEl.getHeight() + this.footerEl.getHeight();
2844             for(var i = 0; i < body_childs.length; i++) {
2845                 full_height += body_childs[i].offsetHeight;
2846             }
2847             
2848             this.setSize(w, Math.min(full_height, Roo.lib.Dom.getViewportHeight(true) - 60));
2849         }
2850         
2851     },
2852
2853     setSize : function(w,h)
2854     {
2855         if (!w && !h) {
2856             return;
2857         }
2858         this.resizeTo(w,h);
2859     },
2860
2861     show : function() {
2862
2863         if (!this.rendered) {
2864             this.render();
2865         }
2866
2867         //this.el.setStyle('display', 'block');
2868         this.el.removeClass('hideing');        
2869         this.el.addClass('show');
2870  
2871         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2872             var _this = this;
2873             (function(){
2874                 this.el.addClass('in');
2875             }).defer(50, this);
2876         }else{
2877             this.el.addClass('in');
2878         }
2879
2880         // not sure how we can show data in here..
2881         //if (this.tmpl) {
2882         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2883         //}
2884
2885         Roo.get(document.body).addClass("x-body-masked");
2886         
2887         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2888         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2889         this.maskEl.addClass('show');
2890         
2891         this.resize();
2892         
2893         this.fireEvent('show', this);
2894
2895         // set zindex here - otherwise it appears to be ignored...
2896         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2897
2898         (function () {
2899             this.items.forEach( function(e) {
2900                 e.layout ? e.layout() : false;
2901
2902             });
2903         }).defer(100,this);
2904
2905     },
2906     hide : function()
2907     {
2908         if(this.fireEvent("beforehide", this) !== false){
2909             this.maskEl.removeClass('show');
2910             Roo.get(document.body).removeClass("x-body-masked");
2911             this.el.removeClass('in');
2912             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2913
2914             if(this.animate){ // why
2915                 this.el.addClass('hideing');
2916                 (function(){
2917                     if (!this.el.hasClass('hideing')) {
2918                         return; // it's been shown again...
2919                     }
2920                     this.el.removeClass('show');
2921                     this.el.removeClass('hideing');
2922                 }).defer(150,this);
2923                 
2924             }else{
2925                  this.el.removeClass('show');
2926             }
2927             this.fireEvent('hide', this);
2928         }
2929     },
2930     isVisible : function()
2931     {
2932         
2933         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2934         
2935     },
2936
2937     addButton : function(str, cb)
2938     {
2939
2940
2941         var b = Roo.apply({}, { html : str } );
2942         b.xns = b.xns || Roo.bootstrap;
2943         b.xtype = b.xtype || 'Button';
2944         if (typeof(b.listeners) == 'undefined') {
2945             b.listeners = { click : cb.createDelegate(this)  };
2946         }
2947
2948         var btn = Roo.factory(b);
2949
2950         btn.render(this.el.select('.modal-footer div').first());
2951
2952         return btn;
2953
2954     },
2955
2956     setDefaultButton : function(btn)
2957     {
2958         //this.el.select('.modal-footer').()
2959     },
2960     diff : false,
2961
2962     resizeTo: function(w,h)
2963     {
2964         // skip.. ?? why??
2965
2966         this.dialogEl.setWidth(w);
2967         if (this.diff === false) {
2968             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2969         }
2970
2971         this.bodyEl.setHeight(h-this.diff);
2972
2973         this.fireEvent('resize', this);
2974
2975     },
2976     setContentSize  : function(w, h)
2977     {
2978
2979     },
2980     onButtonClick: function(btn,e)
2981     {
2982         //Roo.log([a,b,c]);
2983         this.fireEvent('btnclick', btn.name, e);
2984     },
2985      /**
2986      * Set the title of the Dialog
2987      * @param {String} str new Title
2988      */
2989     setTitle: function(str) {
2990         this.titleEl.dom.innerHTML = str;
2991     },
2992     /**
2993      * Set the body of the Dialog
2994      * @param {String} str new Title
2995      */
2996     setBody: function(str) {
2997         this.bodyEl.dom.innerHTML = str;
2998     },
2999     /**
3000      * Set the body of the Dialog using the template
3001      * @param {Obj} data - apply this data to the template and replace the body contents.
3002      */
3003     applyBody: function(obj)
3004     {
3005         if (!this.tmpl) {
3006             Roo.log("Error - using apply Body without a template");
3007             //code
3008         }
3009         this.tmpl.overwrite(this.bodyEl, obj);
3010     }
3011
3012 });
3013
3014
3015 Roo.apply(Roo.bootstrap.Modal,  {
3016     /**
3017          * Button config that displays a single OK button
3018          * @type Object
3019          */
3020         OK :  [{
3021             name : 'ok',
3022             weight : 'primary',
3023             html : 'OK'
3024         }],
3025         /**
3026          * Button config that displays Yes and No buttons
3027          * @type Object
3028          */
3029         YESNO : [
3030             {
3031                 name  : 'no',
3032                 html : 'No'
3033             },
3034             {
3035                 name  :'yes',
3036                 weight : 'primary',
3037                 html : 'Yes'
3038             }
3039         ],
3040
3041         /**
3042          * Button config that displays OK and Cancel buttons
3043          * @type Object
3044          */
3045         OKCANCEL : [
3046             {
3047                name : 'cancel',
3048                 html : 'Cancel'
3049             },
3050             {
3051                 name : 'ok',
3052                 weight : 'primary',
3053                 html : 'OK'
3054             }
3055         ],
3056         /**
3057          * Button config that displays Yes, No and Cancel buttons
3058          * @type Object
3059          */
3060         YESNOCANCEL : [
3061             {
3062                 name : 'yes',
3063                 weight : 'primary',
3064                 html : 'Yes'
3065             },
3066             {
3067                 name : 'no',
3068                 html : 'No'
3069             },
3070             {
3071                 name : 'cancel',
3072                 html : 'Cancel'
3073             }
3074         ],
3075         
3076         zIndex : 10001
3077 });
3078 /*
3079  * - LGPL
3080  *
3081  * messagebox - can be used as a replace
3082  * 
3083  */
3084 /**
3085  * @class Roo.MessageBox
3086  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3087  * Example usage:
3088  *<pre><code>
3089 // Basic alert:
3090 Roo.Msg.alert('Status', 'Changes saved successfully.');
3091
3092 // Prompt for user data:
3093 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3094     if (btn == 'ok'){
3095         // process text value...
3096     }
3097 });
3098
3099 // Show a dialog using config options:
3100 Roo.Msg.show({
3101    title:'Save Changes?',
3102    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3103    buttons: Roo.Msg.YESNOCANCEL,
3104    fn: processResult,
3105    animEl: 'elId'
3106 });
3107 </code></pre>
3108  * @singleton
3109  */
3110 Roo.bootstrap.MessageBox = function(){
3111     var dlg, opt, mask, waitTimer;
3112     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3113     var buttons, activeTextEl, bwidth;
3114
3115     
3116     // private
3117     var handleButton = function(button){
3118         dlg.hide();
3119         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3120     };
3121
3122     // private
3123     var handleHide = function(){
3124         if(opt && opt.cls){
3125             dlg.el.removeClass(opt.cls);
3126         }
3127         //if(waitTimer){
3128         //    Roo.TaskMgr.stop(waitTimer);
3129         //    waitTimer = null;
3130         //}
3131     };
3132
3133     // private
3134     var updateButtons = function(b){
3135         var width = 0;
3136         if(!b){
3137             buttons["ok"].hide();
3138             buttons["cancel"].hide();
3139             buttons["yes"].hide();
3140             buttons["no"].hide();
3141             //dlg.footer.dom.style.display = 'none';
3142             return width;
3143         }
3144         dlg.footerEl.dom.style.display = '';
3145         for(var k in buttons){
3146             if(typeof buttons[k] != "function"){
3147                 if(b[k]){
3148                     buttons[k].show();
3149                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3150                     width += buttons[k].el.getWidth()+15;
3151                 }else{
3152                     buttons[k].hide();
3153                 }
3154             }
3155         }
3156         return width;
3157     };
3158
3159     // private
3160     var handleEsc = function(d, k, e){
3161         if(opt && opt.closable !== false){
3162             dlg.hide();
3163         }
3164         if(e){
3165             e.stopEvent();
3166         }
3167     };
3168
3169     return {
3170         /**
3171          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3172          * @return {Roo.BasicDialog} The BasicDialog element
3173          */
3174         getDialog : function(){
3175            if(!dlg){
3176                 dlg = new Roo.bootstrap.Modal( {
3177                     //draggable: true,
3178                     //resizable:false,
3179                     //constraintoviewport:false,
3180                     //fixedcenter:true,
3181                     //collapsible : false,
3182                     //shim:true,
3183                     //modal: true,
3184                 //    width: 'auto',
3185                   //  height:100,
3186                     //buttonAlign:"center",
3187                     closeClick : function(){
3188                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3189                             handleButton("no");
3190                         }else{
3191                             handleButton("cancel");
3192                         }
3193                     }
3194                 });
3195                 dlg.render();
3196                 dlg.on("hide", handleHide);
3197                 mask = dlg.mask;
3198                 //dlg.addKeyListener(27, handleEsc);
3199                 buttons = {};
3200                 this.buttons = buttons;
3201                 var bt = this.buttonText;
3202                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3203                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3204                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3205                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3206                 //Roo.log(buttons);
3207                 bodyEl = dlg.bodyEl.createChild({
3208
3209                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3210                         '<textarea class="roo-mb-textarea"></textarea>' +
3211                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3212                 });
3213                 msgEl = bodyEl.dom.firstChild;
3214                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3215                 textboxEl.enableDisplayMode();
3216                 textboxEl.addKeyListener([10,13], function(){
3217                     if(dlg.isVisible() && opt && opt.buttons){
3218                         if(opt.buttons.ok){
3219                             handleButton("ok");
3220                         }else if(opt.buttons.yes){
3221                             handleButton("yes");
3222                         }
3223                     }
3224                 });
3225                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3226                 textareaEl.enableDisplayMode();
3227                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3228                 progressEl.enableDisplayMode();
3229                 
3230                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3231                 var pf = progressEl.dom.firstChild;
3232                 if (pf) {
3233                     pp = Roo.get(pf.firstChild);
3234                     pp.setHeight(pf.offsetHeight);
3235                 }
3236                 
3237             }
3238             return dlg;
3239         },
3240
3241         /**
3242          * Updates the message box body text
3243          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3244          * the XHTML-compliant non-breaking space character '&amp;#160;')
3245          * @return {Roo.MessageBox} This message box
3246          */
3247         updateText : function(text)
3248         {
3249             if(!dlg.isVisible() && !opt.width){
3250                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3251                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3252             }
3253             msgEl.innerHTML = text || '&#160;';
3254       
3255             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3256             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3257             var w = Math.max(
3258                     Math.min(opt.width || cw , this.maxWidth), 
3259                     Math.max(opt.minWidth || this.minWidth, bwidth)
3260             );
3261             if(opt.prompt){
3262                 activeTextEl.setWidth(w);
3263             }
3264             if(dlg.isVisible()){
3265                 dlg.fixedcenter = false;
3266             }
3267             // to big, make it scroll. = But as usual stupid IE does not support
3268             // !important..
3269             
3270             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3271                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3272                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3273             } else {
3274                 bodyEl.dom.style.height = '';
3275                 bodyEl.dom.style.overflowY = '';
3276             }
3277             if (cw > w) {
3278                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3279             } else {
3280                 bodyEl.dom.style.overflowX = '';
3281             }
3282             
3283             dlg.setContentSize(w, bodyEl.getHeight());
3284             if(dlg.isVisible()){
3285                 dlg.fixedcenter = true;
3286             }
3287             return this;
3288         },
3289
3290         /**
3291          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3292          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3293          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3294          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3295          * @return {Roo.MessageBox} This message box
3296          */
3297         updateProgress : function(value, text){
3298             if(text){
3299                 this.updateText(text);
3300             }
3301             
3302             if (pp) { // weird bug on my firefox - for some reason this is not defined
3303                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3304                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3305             }
3306             return this;
3307         },        
3308
3309         /**
3310          * Returns true if the message box is currently displayed
3311          * @return {Boolean} True if the message box is visible, else false
3312          */
3313         isVisible : function(){
3314             return dlg && dlg.isVisible();  
3315         },
3316
3317         /**
3318          * Hides the message box if it is displayed
3319          */
3320         hide : function(){
3321             if(this.isVisible()){
3322                 dlg.hide();
3323             }  
3324         },
3325
3326         /**
3327          * Displays a new message box, or reinitializes an existing message box, based on the config options
3328          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3329          * The following config object properties are supported:
3330          * <pre>
3331 Property    Type             Description
3332 ----------  ---------------  ------------------------------------------------------------------------------------
3333 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3334                                    closes (defaults to undefined)
3335 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3336                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3337 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3338                                    progress and wait dialogs will ignore this property and always hide the
3339                                    close button as they can only be closed programmatically.
3340 cls               String           A custom CSS class to apply to the message box element
3341 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3342                                    displayed (defaults to 75)
3343 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3344                                    function will be btn (the name of the button that was clicked, if applicable,
3345                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3346                                    Progress and wait dialogs will ignore this option since they do not respond to
3347                                    user actions and can only be closed programmatically, so any required function
3348                                    should be called by the same code after it closes the dialog.
3349 icon              String           A CSS class that provides a background image to be used as an icon for
3350                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3351 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3352 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3353 modal             Boolean          False to allow user interaction with the page while the message box is
3354                                    displayed (defaults to true)
3355 msg               String           A string that will replace the existing message box body text (defaults
3356                                    to the XHTML-compliant non-breaking space character '&#160;')
3357 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3358 progress          Boolean          True to display a progress bar (defaults to false)
3359 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3360 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3361 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3362 title             String           The title text
3363 value             String           The string value to set into the active textbox element if displayed
3364 wait              Boolean          True to display a progress bar (defaults to false)
3365 width             Number           The width of the dialog in pixels
3366 </pre>
3367          *
3368          * Example usage:
3369          * <pre><code>
3370 Roo.Msg.show({
3371    title: 'Address',
3372    msg: 'Please enter your address:',
3373    width: 300,
3374    buttons: Roo.MessageBox.OKCANCEL,
3375    multiline: true,
3376    fn: saveAddress,
3377    animEl: 'addAddressBtn'
3378 });
3379 </code></pre>
3380          * @param {Object} config Configuration options
3381          * @return {Roo.MessageBox} This message box
3382          */
3383         show : function(options)
3384         {
3385             
3386             // this causes nightmares if you show one dialog after another
3387             // especially on callbacks..
3388              
3389             if(this.isVisible()){
3390                 
3391                 this.hide();
3392                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3393                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3394                 Roo.log("New Dialog Message:" +  options.msg )
3395                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3396                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3397                 
3398             }
3399             var d = this.getDialog();
3400             opt = options;
3401             d.setTitle(opt.title || "&#160;");
3402             d.closeEl.setDisplayed(opt.closable !== false);
3403             activeTextEl = textboxEl;
3404             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3405             if(opt.prompt){
3406                 if(opt.multiline){
3407                     textboxEl.hide();
3408                     textareaEl.show();
3409                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3410                         opt.multiline : this.defaultTextHeight);
3411                     activeTextEl = textareaEl;
3412                 }else{
3413                     textboxEl.show();
3414                     textareaEl.hide();
3415                 }
3416             }else{
3417                 textboxEl.hide();
3418                 textareaEl.hide();
3419             }
3420             progressEl.setDisplayed(opt.progress === true);
3421             this.updateProgress(0);
3422             activeTextEl.dom.value = opt.value || "";
3423             if(opt.prompt){
3424                 dlg.setDefaultButton(activeTextEl);
3425             }else{
3426                 var bs = opt.buttons;
3427                 var db = null;
3428                 if(bs && bs.ok){
3429                     db = buttons["ok"];
3430                 }else if(bs && bs.yes){
3431                     db = buttons["yes"];
3432                 }
3433                 dlg.setDefaultButton(db);
3434             }
3435             bwidth = updateButtons(opt.buttons);
3436             this.updateText(opt.msg);
3437             if(opt.cls){
3438                 d.el.addClass(opt.cls);
3439             }
3440             d.proxyDrag = opt.proxyDrag === true;
3441             d.modal = opt.modal !== false;
3442             d.mask = opt.modal !== false ? mask : false;
3443             if(!d.isVisible()){
3444                 // force it to the end of the z-index stack so it gets a cursor in FF
3445                 document.body.appendChild(dlg.el.dom);
3446                 d.animateTarget = null;
3447                 d.show(options.animEl);
3448             }
3449             return this;
3450         },
3451
3452         /**
3453          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3454          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3455          * and closing the message box when the process is complete.
3456          * @param {String} title The title bar text
3457          * @param {String} msg The message box body text
3458          * @return {Roo.MessageBox} This message box
3459          */
3460         progress : function(title, msg){
3461             this.show({
3462                 title : title,
3463                 msg : msg,
3464                 buttons: false,
3465                 progress:true,
3466                 closable:false,
3467                 minWidth: this.minProgressWidth,
3468                 modal : true
3469             });
3470             return this;
3471         },
3472
3473         /**
3474          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3475          * If a callback function is passed it will be called after the user clicks the button, and the
3476          * id of the button that was clicked will be passed as the only parameter to the callback
3477          * (could also be the top-right close button).
3478          * @param {String} title The title bar text
3479          * @param {String} msg The message box body text
3480          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3481          * @param {Object} scope (optional) The scope of the callback function
3482          * @return {Roo.MessageBox} This message box
3483          */
3484         alert : function(title, msg, fn, scope)
3485         {
3486             this.show({
3487                 title : title,
3488                 msg : msg,
3489                 buttons: this.OK,
3490                 fn: fn,
3491                 closable : false,
3492                 scope : scope,
3493                 modal : true
3494             });
3495             return this;
3496         },
3497
3498         /**
3499          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3500          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3501          * You are responsible for closing the message box when the process is complete.
3502          * @param {String} msg The message box body text
3503          * @param {String} title (optional) The title bar text
3504          * @return {Roo.MessageBox} This message box
3505          */
3506         wait : function(msg, title){
3507             this.show({
3508                 title : title,
3509                 msg : msg,
3510                 buttons: false,
3511                 closable:false,
3512                 progress:true,
3513                 modal:true,
3514                 width:300,
3515                 wait:true
3516             });
3517             waitTimer = Roo.TaskMgr.start({
3518                 run: function(i){
3519                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3520                 },
3521                 interval: 1000
3522             });
3523             return this;
3524         },
3525
3526         /**
3527          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3528          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3529          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3530          * @param {String} title The title bar text
3531          * @param {String} msg The message box body text
3532          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3533          * @param {Object} scope (optional) The scope of the callback function
3534          * @return {Roo.MessageBox} This message box
3535          */
3536         confirm : function(title, msg, fn, scope){
3537             this.show({
3538                 title : title,
3539                 msg : msg,
3540                 buttons: this.YESNO,
3541                 fn: fn,
3542                 scope : scope,
3543                 modal : true
3544             });
3545             return this;
3546         },
3547
3548         /**
3549          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3550          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3551          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3552          * (could also be the top-right close button) and the text that was entered will be passed as the two
3553          * parameters to the callback.
3554          * @param {String} title The title bar text
3555          * @param {String} msg The message box body text
3556          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3557          * @param {Object} scope (optional) The scope of the callback function
3558          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3559          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3560          * @return {Roo.MessageBox} This message box
3561          */
3562         prompt : function(title, msg, fn, scope, multiline){
3563             this.show({
3564                 title : title,
3565                 msg : msg,
3566                 buttons: this.OKCANCEL,
3567                 fn: fn,
3568                 minWidth:250,
3569                 scope : scope,
3570                 prompt:true,
3571                 multiline: multiline,
3572                 modal : true
3573             });
3574             return this;
3575         },
3576
3577         /**
3578          * Button config that displays a single OK button
3579          * @type Object
3580          */
3581         OK : {ok:true},
3582         /**
3583          * Button config that displays Yes and No buttons
3584          * @type Object
3585          */
3586         YESNO : {yes:true, no:true},
3587         /**
3588          * Button config that displays OK and Cancel buttons
3589          * @type Object
3590          */
3591         OKCANCEL : {ok:true, cancel:true},
3592         /**
3593          * Button config that displays Yes, No and Cancel buttons
3594          * @type Object
3595          */
3596         YESNOCANCEL : {yes:true, no:true, cancel:true},
3597
3598         /**
3599          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3600          * @type Number
3601          */
3602         defaultTextHeight : 75,
3603         /**
3604          * The maximum width in pixels of the message box (defaults to 600)
3605          * @type Number
3606          */
3607         maxWidth : 600,
3608         /**
3609          * The minimum width in pixels of the message box (defaults to 100)
3610          * @type Number
3611          */
3612         minWidth : 100,
3613         /**
3614          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3615          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3616          * @type Number
3617          */
3618         minProgressWidth : 250,
3619         /**
3620          * An object containing the default button text strings that can be overriden for localized language support.
3621          * Supported properties are: ok, cancel, yes and no.
3622          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3623          * @type Object
3624          */
3625         buttonText : {
3626             ok : "OK",
3627             cancel : "Cancel",
3628             yes : "Yes",
3629             no : "No"
3630         }
3631     };
3632 }();
3633
3634 /**
3635  * Shorthand for {@link Roo.MessageBox}
3636  */
3637 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3638 Roo.Msg = Roo.Msg || Roo.MessageBox;
3639 /*
3640  * - LGPL
3641  *
3642  * navbar
3643  * 
3644  */
3645
3646 /**
3647  * @class Roo.bootstrap.Navbar
3648  * @extends Roo.bootstrap.Component
3649  * Bootstrap Navbar class
3650
3651  * @constructor
3652  * Create a new Navbar
3653  * @param {Object} config The config object
3654  */
3655
3656
3657 Roo.bootstrap.Navbar = function(config){
3658     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3659     this.addEvents({
3660         // raw events
3661         /**
3662          * @event beforetoggle
3663          * Fire before toggle the menu
3664          * @param {Roo.EventObject} e
3665          */
3666         "beforetoggle" : true
3667     });
3668 };
3669
3670 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3671     
3672     
3673    
3674     // private
3675     navItems : false,
3676     loadMask : false,
3677     
3678     
3679     getAutoCreate : function(){
3680         
3681         
3682         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3683         
3684     },
3685     
3686     initEvents :function ()
3687     {
3688         //Roo.log(this.el.select('.navbar-toggle',true));
3689         this.el.select('.navbar-toggle',true).on('click', function() {
3690             if(this.fireEvent('beforetoggle', this) !== false){
3691                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3692             }
3693             
3694         }, this);
3695         
3696         var mark = {
3697             tag: "div",
3698             cls:"x-dlg-mask"
3699         };
3700         
3701         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3702         
3703         var size = this.el.getSize();
3704         this.maskEl.setSize(size.width, size.height);
3705         this.maskEl.enableDisplayMode("block");
3706         this.maskEl.hide();
3707         
3708         if(this.loadMask){
3709             this.maskEl.show();
3710         }
3711     },
3712     
3713     
3714     getChildContainer : function()
3715     {
3716         if (this.el.select('.collapse').getCount()) {
3717             return this.el.select('.collapse',true).first();
3718         }
3719         
3720         return this.el;
3721     },
3722     
3723     mask : function()
3724     {
3725         this.maskEl.show();
3726     },
3727     
3728     unmask : function()
3729     {
3730         this.maskEl.hide();
3731     } 
3732     
3733     
3734     
3735     
3736 });
3737
3738
3739
3740  
3741
3742  /*
3743  * - LGPL
3744  *
3745  * navbar
3746  * 
3747  */
3748
3749 /**
3750  * @class Roo.bootstrap.NavSimplebar
3751  * @extends Roo.bootstrap.Navbar
3752  * Bootstrap Sidebar class
3753  *
3754  * @cfg {Boolean} inverse is inverted color
3755  * 
3756  * @cfg {String} type (nav | pills | tabs)
3757  * @cfg {Boolean} arrangement stacked | justified
3758  * @cfg {String} align (left | right) alignment
3759  * 
3760  * @cfg {Boolean} main (true|false) main nav bar? default false
3761  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3762  * 
3763  * @cfg {String} tag (header|footer|nav|div) default is nav 
3764
3765  * 
3766  * 
3767  * 
3768  * @constructor
3769  * Create a new Sidebar
3770  * @param {Object} config The config object
3771  */
3772
3773
3774 Roo.bootstrap.NavSimplebar = function(config){
3775     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3776 };
3777
3778 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3779     
3780     inverse: false,
3781     
3782     type: false,
3783     arrangement: '',
3784     align : false,
3785     
3786     
3787     
3788     main : false,
3789     
3790     
3791     tag : false,
3792     
3793     
3794     getAutoCreate : function(){
3795         
3796         
3797         var cfg = {
3798             tag : this.tag || 'div',
3799             cls : 'navbar'
3800         };
3801           
3802         
3803         cfg.cn = [
3804             {
3805                 cls: 'nav',
3806                 tag : 'ul'
3807             }
3808         ];
3809         
3810          
3811         this.type = this.type || 'nav';
3812         if (['tabs','pills'].indexOf(this.type)!==-1) {
3813             cfg.cn[0].cls += ' nav-' + this.type
3814         
3815         
3816         } else {
3817             if (this.type!=='nav') {
3818                 Roo.log('nav type must be nav/tabs/pills')
3819             }
3820             cfg.cn[0].cls += ' navbar-nav'
3821         }
3822         
3823         
3824         
3825         
3826         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3827             cfg.cn[0].cls += ' nav-' + this.arrangement;
3828         }
3829         
3830         
3831         if (this.align === 'right') {
3832             cfg.cn[0].cls += ' navbar-right';
3833         }
3834         
3835         if (this.inverse) {
3836             cfg.cls += ' navbar-inverse';
3837             
3838         }
3839         
3840         
3841         return cfg;
3842     
3843         
3844     }
3845     
3846     
3847     
3848 });
3849
3850
3851
3852  
3853
3854  
3855        /*
3856  * - LGPL
3857  *
3858  * navbar
3859  * 
3860  */
3861
3862 /**
3863  * @class Roo.bootstrap.NavHeaderbar
3864  * @extends Roo.bootstrap.NavSimplebar
3865  * Bootstrap Sidebar class
3866  *
3867  * @cfg {String} brand what is brand
3868  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3869  * @cfg {String} brand_href href of the brand
3870  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3871  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3872  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3873  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3874  * 
3875  * @constructor
3876  * Create a new Sidebar
3877  * @param {Object} config The config object
3878  */
3879
3880
3881 Roo.bootstrap.NavHeaderbar = function(config){
3882     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3883       
3884 };
3885
3886 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3887     
3888     position: '',
3889     brand: '',
3890     brand_href: false,
3891     srButton : true,
3892     autohide : false,
3893     desktopCenter : false,
3894    
3895     
3896     getAutoCreate : function(){
3897         
3898         var   cfg = {
3899             tag: this.nav || 'nav',
3900             cls: 'navbar',
3901             role: 'navigation',
3902             cn: []
3903         };
3904         
3905         var cn = cfg.cn;
3906         if (this.desktopCenter) {
3907             cn.push({cls : 'container', cn : []});
3908             cn = cn[0].cn;
3909         }
3910         
3911         if(this.srButton){
3912             cn.push({
3913                 tag: 'div',
3914                 cls: 'navbar-header',
3915                 cn: [
3916                     {
3917                         tag: 'button',
3918                         type: 'button',
3919                         cls: 'navbar-toggle',
3920                         'data-toggle': 'collapse',
3921                         cn: [
3922                             {
3923                                 tag: 'span',
3924                                 cls: 'sr-only',
3925                                 html: 'Toggle navigation'
3926                             },
3927                             {
3928                                 tag: 'span',
3929                                 cls: 'icon-bar'
3930                             },
3931                             {
3932                                 tag: 'span',
3933                                 cls: 'icon-bar'
3934                             },
3935                             {
3936                                 tag: 'span',
3937                                 cls: 'icon-bar'
3938                             }
3939                         ]
3940                     }
3941                 ]
3942             });
3943         }
3944         
3945         cn.push({
3946             tag: 'div',
3947             cls: 'collapse navbar-collapse',
3948             cn : []
3949         });
3950         
3951         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3952         
3953         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3954             cfg.cls += ' navbar-' + this.position;
3955             
3956             // tag can override this..
3957             
3958             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3959         }
3960         
3961         if (this.brand !== '') {
3962             cn[0].cn.push({
3963                 tag: 'a',
3964                 href: this.brand_href ? this.brand_href : '#',
3965                 cls: 'navbar-brand',
3966                 cn: [
3967                 this.brand
3968                 ]
3969             });
3970         }
3971         
3972         if(this.main){
3973             cfg.cls += ' main-nav';
3974         }
3975         
3976         
3977         return cfg;
3978
3979         
3980     },
3981     getHeaderChildContainer : function()
3982     {
3983         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3984             return this.el.select('.navbar-header',true).first();
3985         }
3986         
3987         return this.getChildContainer();
3988     },
3989     
3990     
3991     initEvents : function()
3992     {
3993         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3994         
3995         if (this.autohide) {
3996             
3997             var prevScroll = 0;
3998             var ft = this.el;
3999             
4000             Roo.get(document).on('scroll',function(e) {
4001                 var ns = Roo.get(document).getScroll().top;
4002                 var os = prevScroll;
4003                 prevScroll = ns;
4004                 
4005                 if(ns > os){
4006                     ft.removeClass('slideDown');
4007                     ft.addClass('slideUp');
4008                     return;
4009                 }
4010                 ft.removeClass('slideUp');
4011                 ft.addClass('slideDown');
4012                  
4013               
4014           },this);
4015         }
4016     }    
4017     
4018 });
4019
4020
4021
4022  
4023
4024  /*
4025  * - LGPL
4026  *
4027  * navbar
4028  * 
4029  */
4030
4031 /**
4032  * @class Roo.bootstrap.NavSidebar
4033  * @extends Roo.bootstrap.Navbar
4034  * Bootstrap Sidebar class
4035  * 
4036  * @constructor
4037  * Create a new Sidebar
4038  * @param {Object} config The config object
4039  */
4040
4041
4042 Roo.bootstrap.NavSidebar = function(config){
4043     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4044 };
4045
4046 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4047     
4048     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4049     
4050     getAutoCreate : function(){
4051         
4052         
4053         return  {
4054             tag: 'div',
4055             cls: 'sidebar sidebar-nav'
4056         };
4057     
4058         
4059     }
4060     
4061     
4062     
4063 });
4064
4065
4066
4067  
4068
4069  /*
4070  * - LGPL
4071  *
4072  * nav group
4073  * 
4074  */
4075
4076 /**
4077  * @class Roo.bootstrap.NavGroup
4078  * @extends Roo.bootstrap.Component
4079  * Bootstrap NavGroup class
4080  * @cfg {String} align (left|right)
4081  * @cfg {Boolean} inverse
4082  * @cfg {String} type (nav|pills|tab) default nav
4083  * @cfg {String} navId - reference Id for navbar.
4084
4085  * 
4086  * @constructor
4087  * Create a new nav group
4088  * @param {Object} config The config object
4089  */
4090
4091 Roo.bootstrap.NavGroup = function(config){
4092     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4093     this.navItems = [];
4094    
4095     Roo.bootstrap.NavGroup.register(this);
4096      this.addEvents({
4097         /**
4098              * @event changed
4099              * Fires when the active item changes
4100              * @param {Roo.bootstrap.NavGroup} this
4101              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4102              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4103          */
4104         'changed': true
4105      });
4106     
4107 };
4108
4109 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4110     
4111     align: '',
4112     inverse: false,
4113     form: false,
4114     type: 'nav',
4115     navId : '',
4116     // private
4117     
4118     navItems : false, 
4119     
4120     getAutoCreate : function()
4121     {
4122         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4123         
4124         cfg = {
4125             tag : 'ul',
4126             cls: 'nav' 
4127         };
4128         
4129         if (['tabs','pills'].indexOf(this.type)!==-1) {
4130             cfg.cls += ' nav-' + this.type
4131         } else {
4132             if (this.type!=='nav') {
4133                 Roo.log('nav type must be nav/tabs/pills')
4134             }
4135             cfg.cls += ' navbar-nav'
4136         }
4137         
4138         if (this.parent() && this.parent().sidebar) {
4139             cfg = {
4140                 tag: 'ul',
4141                 cls: 'dashboard-menu sidebar-menu'
4142             };
4143             
4144             return cfg;
4145         }
4146         
4147         if (this.form === true) {
4148             cfg = {
4149                 tag: 'form',
4150                 cls: 'navbar-form'
4151             };
4152             
4153             if (this.align === 'right') {
4154                 cfg.cls += ' navbar-right';
4155             } else {
4156                 cfg.cls += ' navbar-left';
4157             }
4158         }
4159         
4160         if (this.align === 'right') {
4161             cfg.cls += ' navbar-right';
4162         }
4163         
4164         if (this.inverse) {
4165             cfg.cls += ' navbar-inverse';
4166             
4167         }
4168         
4169         
4170         return cfg;
4171     },
4172     /**
4173     * sets the active Navigation item
4174     * @param {Roo.bootstrap.NavItem} the new current navitem
4175     */
4176     setActiveItem : function(item)
4177     {
4178         var prev = false;
4179         Roo.each(this.navItems, function(v){
4180             if (v == item) {
4181                 return ;
4182             }
4183             if (v.isActive()) {
4184                 v.setActive(false, true);
4185                 prev = v;
4186                 
4187             }
4188             
4189         });
4190
4191         item.setActive(true, true);
4192         this.fireEvent('changed', this, item, prev);
4193         
4194         
4195     },
4196     /**
4197     * gets the active Navigation item
4198     * @return {Roo.bootstrap.NavItem} the current navitem
4199     */
4200     getActive : function()
4201     {
4202         
4203         var prev = false;
4204         Roo.each(this.navItems, function(v){
4205             
4206             if (v.isActive()) {
4207                 prev = v;
4208                 
4209             }
4210             
4211         });
4212         return prev;
4213     },
4214     
4215     indexOfNav : function()
4216     {
4217         
4218         var prev = false;
4219         Roo.each(this.navItems, function(v,i){
4220             
4221             if (v.isActive()) {
4222                 prev = i;
4223                 
4224             }
4225             
4226         });
4227         return prev;
4228     },
4229     /**
4230     * adds a Navigation item
4231     * @param {Roo.bootstrap.NavItem} the navitem to add
4232     */
4233     addItem : function(cfg)
4234     {
4235         var cn = new Roo.bootstrap.NavItem(cfg);
4236         this.register(cn);
4237         cn.parentId = this.id;
4238         cn.onRender(this.el, null);
4239         return cn;
4240     },
4241     /**
4242     * register a Navigation item
4243     * @param {Roo.bootstrap.NavItem} the navitem to add
4244     */
4245     register : function(item)
4246     {
4247         this.navItems.push( item);
4248         item.navId = this.navId;
4249     
4250     },
4251     
4252     /**
4253     * clear all the Navigation item
4254     */
4255    
4256     clearAll : function()
4257     {
4258         this.navItems = [];
4259         this.el.dom.innerHTML = '';
4260     },
4261     
4262     getNavItem: function(tabId)
4263     {
4264         var ret = false;
4265         Roo.each(this.navItems, function(e) {
4266             if (e.tabId == tabId) {
4267                ret =  e;
4268                return false;
4269             }
4270             return true;
4271             
4272         });
4273         return ret;
4274     },
4275     
4276     setActiveNext : function()
4277     {
4278         var i = this.indexOfNav(this.getActive());
4279         if (i > this.navItems.length) {
4280             return;
4281         }
4282         this.setActiveItem(this.navItems[i+1]);
4283     },
4284     setActivePrev : function()
4285     {
4286         var i = this.indexOfNav(this.getActive());
4287         if (i  < 1) {
4288             return;
4289         }
4290         this.setActiveItem(this.navItems[i-1]);
4291     },
4292     clearWasActive : function(except) {
4293         Roo.each(this.navItems, function(e) {
4294             if (e.tabId != except.tabId && e.was_active) {
4295                e.was_active = false;
4296                return false;
4297             }
4298             return true;
4299             
4300         });
4301     },
4302     getWasActive : function ()
4303     {
4304         var r = false;
4305         Roo.each(this.navItems, function(e) {
4306             if (e.was_active) {
4307                r = e;
4308                return false;
4309             }
4310             return true;
4311             
4312         });
4313         return r;
4314     }
4315     
4316     
4317 });
4318
4319  
4320 Roo.apply(Roo.bootstrap.NavGroup, {
4321     
4322     groups: {},
4323      /**
4324     * register a Navigation Group
4325     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4326     */
4327     register : function(navgrp)
4328     {
4329         this.groups[navgrp.navId] = navgrp;
4330         
4331     },
4332     /**
4333     * fetch a Navigation Group based on the navigation ID
4334     * @param {string} the navgroup to add
4335     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4336     */
4337     get: function(navId) {
4338         if (typeof(this.groups[navId]) == 'undefined') {
4339             return false;
4340             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4341         }
4342         return this.groups[navId] ;
4343     }
4344     
4345     
4346     
4347 });
4348
4349  /*
4350  * - LGPL
4351  *
4352  * row
4353  * 
4354  */
4355
4356 /**
4357  * @class Roo.bootstrap.NavItem
4358  * @extends Roo.bootstrap.Component
4359  * Bootstrap Navbar.NavItem class
4360  * @cfg {String} href  link to
4361  * @cfg {String} html content of button
4362  * @cfg {String} badge text inside badge
4363  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4364  * @cfg {String} glyphicon name of glyphicon
4365  * @cfg {String} icon name of font awesome icon
4366  * @cfg {Boolean} active Is item active
4367  * @cfg {Boolean} disabled Is item disabled
4368  
4369  * @cfg {Boolean} preventDefault (true | false) default false
4370  * @cfg {String} tabId the tab that this item activates.
4371  * @cfg {String} tagtype (a|span) render as a href or span?
4372  * @cfg {Boolean} animateRef (true|false) link to element default false  
4373   
4374  * @constructor
4375  * Create a new Navbar Item
4376  * @param {Object} config The config object
4377  */
4378 Roo.bootstrap.NavItem = function(config){
4379     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4380     this.addEvents({
4381         // raw events
4382         /**
4383          * @event click
4384          * The raw click event for the entire grid.
4385          * @param {Roo.EventObject} e
4386          */
4387         "click" : true,
4388          /**
4389             * @event changed
4390             * Fires when the active item active state changes
4391             * @param {Roo.bootstrap.NavItem} this
4392             * @param {boolean} state the new state
4393              
4394          */
4395         'changed': true,
4396         /**
4397             * @event scrollto
4398             * Fires when scroll to element
4399             * @param {Roo.bootstrap.NavItem} this
4400             * @param {Object} options
4401             * @param {Roo.EventObject} e
4402              
4403          */
4404         'scrollto': true
4405     });
4406    
4407 };
4408
4409 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4410     
4411     href: false,
4412     html: '',
4413     badge: '',
4414     icon: false,
4415     glyphicon: false,
4416     active: false,
4417     preventDefault : false,
4418     tabId : false,
4419     tagtype : 'a',
4420     disabled : false,
4421     animateRef : false,
4422     was_active : false,
4423     
4424     getAutoCreate : function(){
4425          
4426         var cfg = {
4427             tag: 'li',
4428             cls: 'nav-item'
4429             
4430         };
4431         
4432         if (this.active) {
4433             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4434         }
4435         if (this.disabled) {
4436             cfg.cls += ' disabled';
4437         }
4438         
4439         if (this.href || this.html || this.glyphicon || this.icon) {
4440             cfg.cn = [
4441                 {
4442                     tag: this.tagtype,
4443                     href : this.href || "#",
4444                     html: this.html || ''
4445                 }
4446             ];
4447             
4448             if (this.icon) {
4449                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4450             }
4451
4452             if(this.glyphicon) {
4453                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4454             }
4455             
4456             if (this.menu) {
4457                 
4458                 cfg.cn[0].html += " <span class='caret'></span>";
4459              
4460             }
4461             
4462             if (this.badge !== '') {
4463                  
4464                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4465             }
4466         }
4467         
4468         
4469         
4470         return cfg;
4471     },
4472     initEvents: function() 
4473     {
4474         if (typeof (this.menu) != 'undefined') {
4475             this.menu.parentType = this.xtype;
4476             this.menu.triggerEl = this.el;
4477             this.menu = this.addxtype(Roo.apply({}, this.menu));
4478         }
4479         
4480         this.el.select('a',true).on('click', this.onClick, this);
4481         
4482         if(this.tagtype == 'span'){
4483             this.el.select('span',true).on('click', this.onClick, this);
4484         }
4485        
4486         // at this point parent should be available..
4487         this.parent().register(this);
4488     },
4489     
4490     onClick : function(e)
4491     {
4492         if (e.getTarget('.dropdown-menu-item')) {
4493             // did you click on a menu itemm.... - then don't trigger onclick..
4494             return;
4495         }
4496         
4497         if(
4498                 this.preventDefault || 
4499                 this.href == '#' 
4500         ){
4501             Roo.log("NavItem - prevent Default?");
4502             e.preventDefault();
4503         }
4504         
4505         if (this.disabled) {
4506             return;
4507         }
4508         
4509         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4510         if (tg && tg.transition) {
4511             Roo.log("waiting for the transitionend");
4512             return;
4513         }
4514         
4515         
4516         
4517         //Roo.log("fire event clicked");
4518         if(this.fireEvent('click', this, e) === false){
4519             return;
4520         };
4521         
4522         if(this.tagtype == 'span'){
4523             return;
4524         }
4525         
4526         //Roo.log(this.href);
4527         var ael = this.el.select('a',true).first();
4528         //Roo.log(ael);
4529         
4530         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4531             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4532             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4533                 return; // ignore... - it's a 'hash' to another page.
4534             }
4535             Roo.log("NavItem - prevent Default?");
4536             e.preventDefault();
4537             this.scrollToElement(e);
4538         }
4539         
4540         
4541         var p =  this.parent();
4542    
4543         if (['tabs','pills'].indexOf(p.type)!==-1) {
4544             if (typeof(p.setActiveItem) !== 'undefined') {
4545                 p.setActiveItem(this);
4546             }
4547         }
4548         
4549         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4550         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4551             // remove the collapsed menu expand...
4552             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4553         }
4554     },
4555     
4556     isActive: function () {
4557         return this.active
4558     },
4559     setActive : function(state, fire, is_was_active)
4560     {
4561         if (this.active && !state && this.navId) {
4562             this.was_active = true;
4563             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4564             if (nv) {
4565                 nv.clearWasActive(this);
4566             }
4567             
4568         }
4569         this.active = state;
4570         
4571         if (!state ) {
4572             this.el.removeClass('active');
4573         } else if (!this.el.hasClass('active')) {
4574             this.el.addClass('active');
4575         }
4576         if (fire) {
4577             this.fireEvent('changed', this, state);
4578         }
4579         
4580         // show a panel if it's registered and related..
4581         
4582         if (!this.navId || !this.tabId || !state || is_was_active) {
4583             return;
4584         }
4585         
4586         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4587         if (!tg) {
4588             return;
4589         }
4590         var pan = tg.getPanelByName(this.tabId);
4591         if (!pan) {
4592             return;
4593         }
4594         // if we can not flip to new panel - go back to old nav highlight..
4595         if (false == tg.showPanel(pan)) {
4596             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4597             if (nv) {
4598                 var onav = nv.getWasActive();
4599                 if (onav) {
4600                     onav.setActive(true, false, true);
4601                 }
4602             }
4603             
4604         }
4605         
4606         
4607         
4608     },
4609      // this should not be here...
4610     setDisabled : function(state)
4611     {
4612         this.disabled = state;
4613         if (!state ) {
4614             this.el.removeClass('disabled');
4615         } else if (!this.el.hasClass('disabled')) {
4616             this.el.addClass('disabled');
4617         }
4618         
4619     },
4620     
4621     /**
4622      * Fetch the element to display the tooltip on.
4623      * @return {Roo.Element} defaults to this.el
4624      */
4625     tooltipEl : function()
4626     {
4627         return this.el.select('' + this.tagtype + '', true).first();
4628     },
4629     
4630     scrollToElement : function(e)
4631     {
4632         var c = document.body;
4633         
4634         /*
4635          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4636          */
4637         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4638             c = document.documentElement;
4639         }
4640         
4641         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4642         
4643         if(!target){
4644             return;
4645         }
4646
4647         var o = target.calcOffsetsTo(c);
4648         
4649         var options = {
4650             target : target,
4651             value : o[1]
4652         };
4653         
4654         this.fireEvent('scrollto', this, options, e);
4655         
4656         Roo.get(c).scrollTo('top', options.value, true);
4657         
4658         return;
4659     }
4660 });
4661  
4662
4663  /*
4664  * - LGPL
4665  *
4666  * sidebar item
4667  *
4668  *  li
4669  *    <span> icon </span>
4670  *    <span> text </span>
4671  *    <span>badge </span>
4672  */
4673
4674 /**
4675  * @class Roo.bootstrap.NavSidebarItem
4676  * @extends Roo.bootstrap.NavItem
4677  * Bootstrap Navbar.NavSidebarItem class
4678  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4679  * {Boolean} open is the menu open
4680  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4681  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4682  * {String} buttonSize (sm|md|lg)the extra classes for the button
4683  * {Boolean} showArrow show arrow next to the text (default true)
4684  * @constructor
4685  * Create a new Navbar Button
4686  * @param {Object} config The config object
4687  */
4688 Roo.bootstrap.NavSidebarItem = function(config){
4689     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4690     this.addEvents({
4691         // raw events
4692         /**
4693          * @event click
4694          * The raw click event for the entire grid.
4695          * @param {Roo.EventObject} e
4696          */
4697         "click" : true,
4698          /**
4699             * @event changed
4700             * Fires when the active item active state changes
4701             * @param {Roo.bootstrap.NavSidebarItem} this
4702             * @param {boolean} state the new state
4703              
4704          */
4705         'changed': true
4706     });
4707    
4708 };
4709
4710 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4711     
4712     badgeWeight : 'default',
4713     
4714     open: false,
4715     
4716     buttonView : false,
4717     
4718     buttonWeight : 'default',
4719     
4720     buttonSize : 'md',
4721     
4722     showArrow : true,
4723     
4724     getAutoCreate : function(){
4725         
4726         
4727         var a = {
4728                 tag: 'a',
4729                 href : this.href || '#',
4730                 cls: '',
4731                 html : '',
4732                 cn : []
4733         };
4734         
4735         if(this.buttonView){
4736             a = {
4737                 tag: 'button',
4738                 href : this.href || '#',
4739                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4740                 html : this.html,
4741                 cn : []
4742             };
4743         }
4744         
4745         var cfg = {
4746             tag: 'li',
4747             cls: '',
4748             cn: [ a ]
4749         };
4750         
4751         if (this.active) {
4752             cfg.cls += ' active';
4753         }
4754         
4755         if (this.disabled) {
4756             cfg.cls += ' disabled';
4757         }
4758         if (this.open) {
4759             cfg.cls += ' open x-open';
4760         }
4761         // left icon..
4762         if (this.glyphicon || this.icon) {
4763             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4764             a.cn.push({ tag : 'i', cls : c }) ;
4765         }
4766         
4767         if(!this.buttonView){
4768             var span = {
4769                 tag: 'span',
4770                 html : this.html || ''
4771             };
4772
4773             a.cn.push(span);
4774             
4775         }
4776         
4777         if (this.badge !== '') {
4778             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4779         }
4780         
4781         if (this.menu) {
4782             
4783             if(this.showArrow){
4784                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4785             }
4786             
4787             a.cls += ' dropdown-toggle treeview' ;
4788         }
4789         
4790         return cfg;
4791     },
4792     
4793     initEvents : function()
4794     { 
4795         if (typeof (this.menu) != 'undefined') {
4796             this.menu.parentType = this.xtype;
4797             this.menu.triggerEl = this.el;
4798             this.menu = this.addxtype(Roo.apply({}, this.menu));
4799         }
4800         
4801         this.el.on('click', this.onClick, this);
4802         
4803         if(this.badge !== ''){
4804             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4805         }
4806         
4807     },
4808     
4809     onClick : function(e)
4810     {
4811         if(this.disabled){
4812             e.preventDefault();
4813             return;
4814         }
4815         
4816         if(this.preventDefault){
4817             e.preventDefault();
4818         }
4819         
4820         this.fireEvent('click', this);
4821     },
4822     
4823     disable : function()
4824     {
4825         this.setDisabled(true);
4826     },
4827     
4828     enable : function()
4829     {
4830         this.setDisabled(false);
4831     },
4832     
4833     setDisabled : function(state)
4834     {
4835         if(this.disabled == state){
4836             return;
4837         }
4838         
4839         this.disabled = state;
4840         
4841         if (state) {
4842             this.el.addClass('disabled');
4843             return;
4844         }
4845         
4846         this.el.removeClass('disabled');
4847         
4848         return;
4849     },
4850     
4851     setActive : function(state)
4852     {
4853         if(this.active == state){
4854             return;
4855         }
4856         
4857         this.active = state;
4858         
4859         if (state) {
4860             this.el.addClass('active');
4861             return;
4862         }
4863         
4864         this.el.removeClass('active');
4865         
4866         return;
4867     },
4868     
4869     isActive: function () 
4870     {
4871         return this.active;
4872     },
4873     
4874     setBadge : function(str)
4875     {
4876         if(!this.badgeEl){
4877             return;
4878         }
4879         
4880         this.badgeEl.dom.innerHTML = str;
4881     }
4882     
4883    
4884      
4885  
4886 });
4887  
4888
4889  /*
4890  * - LGPL
4891  *
4892  * row
4893  * 
4894  */
4895
4896 /**
4897  * @class Roo.bootstrap.Row
4898  * @extends Roo.bootstrap.Component
4899  * Bootstrap Row class (contains columns...)
4900  * 
4901  * @constructor
4902  * Create a new Row
4903  * @param {Object} config The config object
4904  */
4905
4906 Roo.bootstrap.Row = function(config){
4907     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4908 };
4909
4910 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4911     
4912     getAutoCreate : function(){
4913        return {
4914             cls: 'row clearfix'
4915        };
4916     }
4917     
4918     
4919 });
4920
4921  
4922
4923  /*
4924  * - LGPL
4925  *
4926  * element
4927  * 
4928  */
4929
4930 /**
4931  * @class Roo.bootstrap.Element
4932  * @extends Roo.bootstrap.Component
4933  * Bootstrap Element class
4934  * @cfg {String} html contents of the element
4935  * @cfg {String} tag tag of the element
4936  * @cfg {String} cls class of the element
4937  * @cfg {Boolean} preventDefault (true|false) default false
4938  * @cfg {Boolean} clickable (true|false) default false
4939  * 
4940  * @constructor
4941  * Create a new Element
4942  * @param {Object} config The config object
4943  */
4944
4945 Roo.bootstrap.Element = function(config){
4946     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4947     
4948     this.addEvents({
4949         // raw events
4950         /**
4951          * @event click
4952          * When a element is chick
4953          * @param {Roo.bootstrap.Element} this
4954          * @param {Roo.EventObject} e
4955          */
4956         "click" : true
4957     });
4958 };
4959
4960 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4961     
4962     tag: 'div',
4963     cls: '',
4964     html: '',
4965     preventDefault: false, 
4966     clickable: false,
4967     
4968     getAutoCreate : function(){
4969         
4970         var cfg = {
4971             tag: this.tag,
4972             // cls: this.cls, double assign in parent class Component.js :: onRender
4973             html: this.html
4974         };
4975         
4976         return cfg;
4977     },
4978     
4979     initEvents: function() 
4980     {
4981         Roo.bootstrap.Element.superclass.initEvents.call(this);
4982         
4983         if(this.clickable){
4984             this.el.on('click', this.onClick, this);
4985         }
4986         
4987     },
4988     
4989     onClick : function(e)
4990     {
4991         if(this.preventDefault){
4992             e.preventDefault();
4993         }
4994         
4995         this.fireEvent('click', this, e);
4996     },
4997     
4998     getValue : function()
4999     {
5000         return this.el.dom.innerHTML;
5001     },
5002     
5003     setValue : function(value)
5004     {
5005         this.el.dom.innerHTML = value;
5006     }
5007    
5008 });
5009
5010  
5011
5012  /*
5013  * - LGPL
5014  *
5015  * pagination
5016  * 
5017  */
5018
5019 /**
5020  * @class Roo.bootstrap.Pagination
5021  * @extends Roo.bootstrap.Component
5022  * Bootstrap Pagination class
5023  * @cfg {String} size xs | sm | md | lg
5024  * @cfg {Boolean} inverse false | true
5025  * 
5026  * @constructor
5027  * Create a new Pagination
5028  * @param {Object} config The config object
5029  */
5030
5031 Roo.bootstrap.Pagination = function(config){
5032     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5033 };
5034
5035 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5036     
5037     cls: false,
5038     size: false,
5039     inverse: false,
5040     
5041     getAutoCreate : function(){
5042         var cfg = {
5043             tag: 'ul',
5044                 cls: 'pagination'
5045         };
5046         if (this.inverse) {
5047             cfg.cls += ' inverse';
5048         }
5049         if (this.html) {
5050             cfg.html=this.html;
5051         }
5052         if (this.cls) {
5053             cfg.cls += " " + this.cls;
5054         }
5055         return cfg;
5056     }
5057    
5058 });
5059
5060  
5061
5062  /*
5063  * - LGPL
5064  *
5065  * Pagination item
5066  * 
5067  */
5068
5069
5070 /**
5071  * @class Roo.bootstrap.PaginationItem
5072  * @extends Roo.bootstrap.Component
5073  * Bootstrap PaginationItem class
5074  * @cfg {String} html text
5075  * @cfg {String} href the link
5076  * @cfg {Boolean} preventDefault (true | false) default true
5077  * @cfg {Boolean} active (true | false) default false
5078  * @cfg {Boolean} disabled default false
5079  * 
5080  * 
5081  * @constructor
5082  * Create a new PaginationItem
5083  * @param {Object} config The config object
5084  */
5085
5086
5087 Roo.bootstrap.PaginationItem = function(config){
5088     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5089     this.addEvents({
5090         // raw events
5091         /**
5092          * @event click
5093          * The raw click event for the entire grid.
5094          * @param {Roo.EventObject} e
5095          */
5096         "click" : true
5097     });
5098 };
5099
5100 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5101     
5102     href : false,
5103     html : false,
5104     preventDefault: true,
5105     active : false,
5106     cls : false,
5107     disabled: false,
5108     
5109     getAutoCreate : function(){
5110         var cfg= {
5111             tag: 'li',
5112             cn: [
5113                 {
5114                     tag : 'a',
5115                     href : this.href ? this.href : '#',
5116                     html : this.html ? this.html : ''
5117                 }
5118             ]
5119         };
5120         
5121         if(this.cls){
5122             cfg.cls = this.cls;
5123         }
5124         
5125         if(this.disabled){
5126             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5127         }
5128         
5129         if(this.active){
5130             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5131         }
5132         
5133         return cfg;
5134     },
5135     
5136     initEvents: function() {
5137         
5138         this.el.on('click', this.onClick, this);
5139         
5140     },
5141     onClick : function(e)
5142     {
5143         Roo.log('PaginationItem on click ');
5144         if(this.preventDefault){
5145             e.preventDefault();
5146         }
5147         
5148         if(this.disabled){
5149             return;
5150         }
5151         
5152         this.fireEvent('click', this, e);
5153     }
5154    
5155 });
5156
5157  
5158
5159  /*
5160  * - LGPL
5161  *
5162  * slider
5163  * 
5164  */
5165
5166
5167 /**
5168  * @class Roo.bootstrap.Slider
5169  * @extends Roo.bootstrap.Component
5170  * Bootstrap Slider class
5171  *    
5172  * @constructor
5173  * Create a new Slider
5174  * @param {Object} config The config object
5175  */
5176
5177 Roo.bootstrap.Slider = function(config){
5178     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5179 };
5180
5181 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5182     
5183     getAutoCreate : function(){
5184         
5185         var cfg = {
5186             tag: 'div',
5187             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5188             cn: [
5189                 {
5190                     tag: 'a',
5191                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5192                 }
5193             ]
5194         };
5195         
5196         return cfg;
5197     }
5198    
5199 });
5200
5201  /*
5202  * Based on:
5203  * Ext JS Library 1.1.1
5204  * Copyright(c) 2006-2007, Ext JS, LLC.
5205  *
5206  * Originally Released Under LGPL - original licence link has changed is not relivant.
5207  *
5208  * Fork - LGPL
5209  * <script type="text/javascript">
5210  */
5211  
5212
5213 /**
5214  * @class Roo.grid.ColumnModel
5215  * @extends Roo.util.Observable
5216  * This is the default implementation of a ColumnModel used by the Grid. It defines
5217  * the columns in the grid.
5218  * <br>Usage:<br>
5219  <pre><code>
5220  var colModel = new Roo.grid.ColumnModel([
5221         {header: "Ticker", width: 60, sortable: true, locked: true},
5222         {header: "Company Name", width: 150, sortable: true},
5223         {header: "Market Cap.", width: 100, sortable: true},
5224         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5225         {header: "Employees", width: 100, sortable: true, resizable: false}
5226  ]);
5227  </code></pre>
5228  * <p>
5229  
5230  * The config options listed for this class are options which may appear in each
5231  * individual column definition.
5232  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5233  * @constructor
5234  * @param {Object} config An Array of column config objects. See this class's
5235  * config objects for details.
5236 */
5237 Roo.grid.ColumnModel = function(config){
5238         /**
5239      * The config passed into the constructor
5240      */
5241     this.config = config;
5242     this.lookup = {};
5243
5244     // if no id, create one
5245     // if the column does not have a dataIndex mapping,
5246     // map it to the order it is in the config
5247     for(var i = 0, len = config.length; i < len; i++){
5248         var c = config[i];
5249         if(typeof c.dataIndex == "undefined"){
5250             c.dataIndex = i;
5251         }
5252         if(typeof c.renderer == "string"){
5253             c.renderer = Roo.util.Format[c.renderer];
5254         }
5255         if(typeof c.id == "undefined"){
5256             c.id = Roo.id();
5257         }
5258         if(c.editor && c.editor.xtype){
5259             c.editor  = Roo.factory(c.editor, Roo.grid);
5260         }
5261         if(c.editor && c.editor.isFormField){
5262             c.editor = new Roo.grid.GridEditor(c.editor);
5263         }
5264         this.lookup[c.id] = c;
5265     }
5266
5267     /**
5268      * The width of columns which have no width specified (defaults to 100)
5269      * @type Number
5270      */
5271     this.defaultWidth = 100;
5272
5273     /**
5274      * Default sortable of columns which have no sortable specified (defaults to false)
5275      * @type Boolean
5276      */
5277     this.defaultSortable = false;
5278
5279     this.addEvents({
5280         /**
5281              * @event widthchange
5282              * Fires when the width of a column changes.
5283              * @param {ColumnModel} this
5284              * @param {Number} columnIndex The column index
5285              * @param {Number} newWidth The new width
5286              */
5287             "widthchange": true,
5288         /**
5289              * @event headerchange
5290              * Fires when the text of a header changes.
5291              * @param {ColumnModel} this
5292              * @param {Number} columnIndex The column index
5293              * @param {Number} newText The new header text
5294              */
5295             "headerchange": true,
5296         /**
5297              * @event hiddenchange
5298              * Fires when a column is hidden or "unhidden".
5299              * @param {ColumnModel} this
5300              * @param {Number} columnIndex The column index
5301              * @param {Boolean} hidden true if hidden, false otherwise
5302              */
5303             "hiddenchange": true,
5304             /**
5305          * @event columnmoved
5306          * Fires when a column is moved.
5307          * @param {ColumnModel} this
5308          * @param {Number} oldIndex
5309          * @param {Number} newIndex
5310          */
5311         "columnmoved" : true,
5312         /**
5313          * @event columlockchange
5314          * Fires when a column's locked state is changed
5315          * @param {ColumnModel} this
5316          * @param {Number} colIndex
5317          * @param {Boolean} locked true if locked
5318          */
5319         "columnlockchange" : true
5320     });
5321     Roo.grid.ColumnModel.superclass.constructor.call(this);
5322 };
5323 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5324     /**
5325      * @cfg {String} header The header text to display in the Grid view.
5326      */
5327     /**
5328      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5329      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5330      * specified, the column's index is used as an index into the Record's data Array.
5331      */
5332     /**
5333      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5334      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5335      */
5336     /**
5337      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5338      * Defaults to the value of the {@link #defaultSortable} property.
5339      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5340      */
5341     /**
5342      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5343      */
5344     /**
5345      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5346      */
5347     /**
5348      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5349      */
5350     /**
5351      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5352      */
5353     /**
5354      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5355      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5356      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5357      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5358      */
5359        /**
5360      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5361      */
5362     /**
5363      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5364      */
5365     /**
5366      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5367      */
5368     /**
5369      * @cfg {String} cursor (Optional)
5370      */
5371     /**
5372      * @cfg {String} tooltip (Optional)
5373      */
5374     /**
5375      * @cfg {Number} xs (Optional)
5376      */
5377     /**
5378      * @cfg {Number} sm (Optional)
5379      */
5380     /**
5381      * @cfg {Number} md (Optional)
5382      */
5383     /**
5384      * @cfg {Number} lg (Optional)
5385      */
5386     /**
5387      * Returns the id of the column at the specified index.
5388      * @param {Number} index The column index
5389      * @return {String} the id
5390      */
5391     getColumnId : function(index){
5392         return this.config[index].id;
5393     },
5394
5395     /**
5396      * Returns the column for a specified id.
5397      * @param {String} id The column id
5398      * @return {Object} the column
5399      */
5400     getColumnById : function(id){
5401         return this.lookup[id];
5402     },
5403
5404     
5405     /**
5406      * Returns the column for a specified dataIndex.
5407      * @param {String} dataIndex The column dataIndex
5408      * @return {Object|Boolean} the column or false if not found
5409      */
5410     getColumnByDataIndex: function(dataIndex){
5411         var index = this.findColumnIndex(dataIndex);
5412         return index > -1 ? this.config[index] : false;
5413     },
5414     
5415     /**
5416      * Returns the index for a specified column id.
5417      * @param {String} id The column id
5418      * @return {Number} the index, or -1 if not found
5419      */
5420     getIndexById : function(id){
5421         for(var i = 0, len = this.config.length; i < len; i++){
5422             if(this.config[i].id == id){
5423                 return i;
5424             }
5425         }
5426         return -1;
5427     },
5428     
5429     /**
5430      * Returns the index for a specified column dataIndex.
5431      * @param {String} dataIndex The column dataIndex
5432      * @return {Number} the index, or -1 if not found
5433      */
5434     
5435     findColumnIndex : function(dataIndex){
5436         for(var i = 0, len = this.config.length; i < len; i++){
5437             if(this.config[i].dataIndex == dataIndex){
5438                 return i;
5439             }
5440         }
5441         return -1;
5442     },
5443     
5444     
5445     moveColumn : function(oldIndex, newIndex){
5446         var c = this.config[oldIndex];
5447         this.config.splice(oldIndex, 1);
5448         this.config.splice(newIndex, 0, c);
5449         this.dataMap = null;
5450         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5451     },
5452
5453     isLocked : function(colIndex){
5454         return this.config[colIndex].locked === true;
5455     },
5456
5457     setLocked : function(colIndex, value, suppressEvent){
5458         if(this.isLocked(colIndex) == value){
5459             return;
5460         }
5461         this.config[colIndex].locked = value;
5462         if(!suppressEvent){
5463             this.fireEvent("columnlockchange", this, colIndex, value);
5464         }
5465     },
5466
5467     getTotalLockedWidth : function(){
5468         var totalWidth = 0;
5469         for(var i = 0; i < this.config.length; i++){
5470             if(this.isLocked(i) && !this.isHidden(i)){
5471                 this.totalWidth += this.getColumnWidth(i);
5472             }
5473         }
5474         return totalWidth;
5475     },
5476
5477     getLockedCount : function(){
5478         for(var i = 0, len = this.config.length; i < len; i++){
5479             if(!this.isLocked(i)){
5480                 return i;
5481             }
5482         }
5483         
5484         return this.config.length;
5485     },
5486
5487     /**
5488      * Returns the number of columns.
5489      * @return {Number}
5490      */
5491     getColumnCount : function(visibleOnly){
5492         if(visibleOnly === true){
5493             var c = 0;
5494             for(var i = 0, len = this.config.length; i < len; i++){
5495                 if(!this.isHidden(i)){
5496                     c++;
5497                 }
5498             }
5499             return c;
5500         }
5501         return this.config.length;
5502     },
5503
5504     /**
5505      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5506      * @param {Function} fn
5507      * @param {Object} scope (optional)
5508      * @return {Array} result
5509      */
5510     getColumnsBy : function(fn, scope){
5511         var r = [];
5512         for(var i = 0, len = this.config.length; i < len; i++){
5513             var c = this.config[i];
5514             if(fn.call(scope||this, c, i) === true){
5515                 r[r.length] = c;
5516             }
5517         }
5518         return r;
5519     },
5520
5521     /**
5522      * Returns true if the specified column is sortable.
5523      * @param {Number} col The column index
5524      * @return {Boolean}
5525      */
5526     isSortable : function(col){
5527         if(typeof this.config[col].sortable == "undefined"){
5528             return this.defaultSortable;
5529         }
5530         return this.config[col].sortable;
5531     },
5532
5533     /**
5534      * Returns the rendering (formatting) function defined for the column.
5535      * @param {Number} col The column index.
5536      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5537      */
5538     getRenderer : function(col){
5539         if(!this.config[col].renderer){
5540             return Roo.grid.ColumnModel.defaultRenderer;
5541         }
5542         return this.config[col].renderer;
5543     },
5544
5545     /**
5546      * Sets the rendering (formatting) function for a column.
5547      * @param {Number} col The column index
5548      * @param {Function} fn The function to use to process the cell's raw data
5549      * to return HTML markup for the grid view. The render function is called with
5550      * the following parameters:<ul>
5551      * <li>Data value.</li>
5552      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5553      * <li>css A CSS style string to apply to the table cell.</li>
5554      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5555      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5556      * <li>Row index</li>
5557      * <li>Column index</li>
5558      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5559      */
5560     setRenderer : function(col, fn){
5561         this.config[col].renderer = fn;
5562     },
5563
5564     /**
5565      * Returns the width for the specified column.
5566      * @param {Number} col The column index
5567      * @return {Number}
5568      */
5569     getColumnWidth : function(col){
5570         return this.config[col].width * 1 || this.defaultWidth;
5571     },
5572
5573     /**
5574      * Sets the width for a column.
5575      * @param {Number} col The column index
5576      * @param {Number} width The new width
5577      */
5578     setColumnWidth : function(col, width, suppressEvent){
5579         this.config[col].width = width;
5580         this.totalWidth = null;
5581         if(!suppressEvent){
5582              this.fireEvent("widthchange", this, col, width);
5583         }
5584     },
5585
5586     /**
5587      * Returns the total width of all columns.
5588      * @param {Boolean} includeHidden True to include hidden column widths
5589      * @return {Number}
5590      */
5591     getTotalWidth : function(includeHidden){
5592         if(!this.totalWidth){
5593             this.totalWidth = 0;
5594             for(var i = 0, len = this.config.length; i < len; i++){
5595                 if(includeHidden || !this.isHidden(i)){
5596                     this.totalWidth += this.getColumnWidth(i);
5597                 }
5598             }
5599         }
5600         return this.totalWidth;
5601     },
5602
5603     /**
5604      * Returns the header for the specified column.
5605      * @param {Number} col The column index
5606      * @return {String}
5607      */
5608     getColumnHeader : function(col){
5609         return this.config[col].header;
5610     },
5611
5612     /**
5613      * Sets the header for a column.
5614      * @param {Number} col The column index
5615      * @param {String} header The new header
5616      */
5617     setColumnHeader : function(col, header){
5618         this.config[col].header = header;
5619         this.fireEvent("headerchange", this, col, header);
5620     },
5621
5622     /**
5623      * Returns the tooltip for the specified column.
5624      * @param {Number} col The column index
5625      * @return {String}
5626      */
5627     getColumnTooltip : function(col){
5628             return this.config[col].tooltip;
5629     },
5630     /**
5631      * Sets the tooltip for a column.
5632      * @param {Number} col The column index
5633      * @param {String} tooltip The new tooltip
5634      */
5635     setColumnTooltip : function(col, tooltip){
5636             this.config[col].tooltip = tooltip;
5637     },
5638
5639     /**
5640      * Returns the dataIndex for the specified column.
5641      * @param {Number} col The column index
5642      * @return {Number}
5643      */
5644     getDataIndex : function(col){
5645         return this.config[col].dataIndex;
5646     },
5647
5648     /**
5649      * Sets the dataIndex for a column.
5650      * @param {Number} col The column index
5651      * @param {Number} dataIndex The new dataIndex
5652      */
5653     setDataIndex : function(col, dataIndex){
5654         this.config[col].dataIndex = dataIndex;
5655     },
5656
5657     
5658     
5659     /**
5660      * Returns true if the cell is editable.
5661      * @param {Number} colIndex The column index
5662      * @param {Number} rowIndex The row index - this is nto actually used..?
5663      * @return {Boolean}
5664      */
5665     isCellEditable : function(colIndex, rowIndex){
5666         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5667     },
5668
5669     /**
5670      * Returns the editor defined for the cell/column.
5671      * return false or null to disable editing.
5672      * @param {Number} colIndex The column index
5673      * @param {Number} rowIndex The row index
5674      * @return {Object}
5675      */
5676     getCellEditor : function(colIndex, rowIndex){
5677         return this.config[colIndex].editor;
5678     },
5679
5680     /**
5681      * Sets if a column is editable.
5682      * @param {Number} col The column index
5683      * @param {Boolean} editable True if the column is editable
5684      */
5685     setEditable : function(col, editable){
5686         this.config[col].editable = editable;
5687     },
5688
5689
5690     /**
5691      * Returns true if the column is hidden.
5692      * @param {Number} colIndex The column index
5693      * @return {Boolean}
5694      */
5695     isHidden : function(colIndex){
5696         return this.config[colIndex].hidden;
5697     },
5698
5699
5700     /**
5701      * Returns true if the column width cannot be changed
5702      */
5703     isFixed : function(colIndex){
5704         return this.config[colIndex].fixed;
5705     },
5706
5707     /**
5708      * Returns true if the column can be resized
5709      * @return {Boolean}
5710      */
5711     isResizable : function(colIndex){
5712         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5713     },
5714     /**
5715      * Sets if a column is hidden.
5716      * @param {Number} colIndex The column index
5717      * @param {Boolean} hidden True if the column is hidden
5718      */
5719     setHidden : function(colIndex, hidden){
5720         this.config[colIndex].hidden = hidden;
5721         this.totalWidth = null;
5722         this.fireEvent("hiddenchange", this, colIndex, hidden);
5723     },
5724
5725     /**
5726      * Sets the editor for a column.
5727      * @param {Number} col The column index
5728      * @param {Object} editor The editor object
5729      */
5730     setEditor : function(col, editor){
5731         this.config[col].editor = editor;
5732     }
5733 });
5734
5735 Roo.grid.ColumnModel.defaultRenderer = function(value)
5736 {
5737     if(typeof value == "object") {
5738         return value;
5739     }
5740         if(typeof value == "string" && value.length < 1){
5741             return "&#160;";
5742         }
5743     
5744         return String.format("{0}", value);
5745 };
5746
5747 // Alias for backwards compatibility
5748 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5749 /*
5750  * Based on:
5751  * Ext JS Library 1.1.1
5752  * Copyright(c) 2006-2007, Ext JS, LLC.
5753  *
5754  * Originally Released Under LGPL - original licence link has changed is not relivant.
5755  *
5756  * Fork - LGPL
5757  * <script type="text/javascript">
5758  */
5759  
5760 /**
5761  * @class Roo.LoadMask
5762  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5763  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5764  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5765  * element's UpdateManager load indicator and will be destroyed after the initial load.
5766  * @constructor
5767  * Create a new LoadMask
5768  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5769  * @param {Object} config The config object
5770  */
5771 Roo.LoadMask = function(el, config){
5772     this.el = Roo.get(el);
5773     Roo.apply(this, config);
5774     if(this.store){
5775         this.store.on('beforeload', this.onBeforeLoad, this);
5776         this.store.on('load', this.onLoad, this);
5777         this.store.on('loadexception', this.onLoadException, this);
5778         this.removeMask = false;
5779     }else{
5780         var um = this.el.getUpdateManager();
5781         um.showLoadIndicator = false; // disable the default indicator
5782         um.on('beforeupdate', this.onBeforeLoad, this);
5783         um.on('update', this.onLoad, this);
5784         um.on('failure', this.onLoad, this);
5785         this.removeMask = true;
5786     }
5787 };
5788
5789 Roo.LoadMask.prototype = {
5790     /**
5791      * @cfg {Boolean} removeMask
5792      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5793      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5794      */
5795     /**
5796      * @cfg {String} msg
5797      * The text to display in a centered loading message box (defaults to 'Loading...')
5798      */
5799     msg : 'Loading...',
5800     /**
5801      * @cfg {String} msgCls
5802      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5803      */
5804     msgCls : 'x-mask-loading',
5805
5806     /**
5807      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5808      * @type Boolean
5809      */
5810     disabled: false,
5811
5812     /**
5813      * Disables the mask to prevent it from being displayed
5814      */
5815     disable : function(){
5816        this.disabled = true;
5817     },
5818
5819     /**
5820      * Enables the mask so that it can be displayed
5821      */
5822     enable : function(){
5823         this.disabled = false;
5824     },
5825     
5826     onLoadException : function()
5827     {
5828         Roo.log(arguments);
5829         
5830         if (typeof(arguments[3]) != 'undefined') {
5831             Roo.MessageBox.alert("Error loading",arguments[3]);
5832         } 
5833         /*
5834         try {
5835             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5836                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5837             }   
5838         } catch(e) {
5839             
5840         }
5841         */
5842     
5843         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5844     },
5845     // private
5846     onLoad : function()
5847     {
5848         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5849     },
5850
5851     // private
5852     onBeforeLoad : function(){
5853         if(!this.disabled){
5854             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5855         }
5856     },
5857
5858     // private
5859     destroy : function(){
5860         if(this.store){
5861             this.store.un('beforeload', this.onBeforeLoad, this);
5862             this.store.un('load', this.onLoad, this);
5863             this.store.un('loadexception', this.onLoadException, this);
5864         }else{
5865             var um = this.el.getUpdateManager();
5866             um.un('beforeupdate', this.onBeforeLoad, this);
5867             um.un('update', this.onLoad, this);
5868             um.un('failure', this.onLoad, this);
5869         }
5870     }
5871 };/*
5872  * - LGPL
5873  *
5874  * table
5875  * 
5876  */
5877
5878 /**
5879  * @class Roo.bootstrap.Table
5880  * @extends Roo.bootstrap.Component
5881  * Bootstrap Table class
5882  * @cfg {String} cls table class
5883  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5884  * @cfg {String} bgcolor Specifies the background color for a table
5885  * @cfg {Number} border Specifies whether the table cells should have borders or not
5886  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5887  * @cfg {Number} cellspacing Specifies the space between cells
5888  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5889  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5890  * @cfg {String} sortable Specifies that the table should be sortable
5891  * @cfg {String} summary Specifies a summary of the content of a table
5892  * @cfg {Number} width Specifies the width of a table
5893  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5894  * 
5895  * @cfg {boolean} striped Should the rows be alternative striped
5896  * @cfg {boolean} bordered Add borders to the table
5897  * @cfg {boolean} hover Add hover highlighting
5898  * @cfg {boolean} condensed Format condensed
5899  * @cfg {boolean} responsive Format condensed
5900  * @cfg {Boolean} loadMask (true|false) default false
5901  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5902  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5903  * @cfg {Boolean} rowSelection (true|false) default false
5904  * @cfg {Boolean} cellSelection (true|false) default false
5905  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5906  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5907  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5908  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
5909  
5910  * 
5911  * @constructor
5912  * Create a new Table
5913  * @param {Object} config The config object
5914  */
5915
5916 Roo.bootstrap.Table = function(config){
5917     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5918     
5919   
5920     
5921     // BC...
5922     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5923     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5924     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5925     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5926     
5927     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5928     if (this.sm) {
5929         this.sm.grid = this;
5930         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5931         this.sm = this.selModel;
5932         this.sm.xmodule = this.xmodule || false;
5933     }
5934     
5935     if (this.cm && typeof(this.cm.config) == 'undefined') {
5936         this.colModel = new Roo.grid.ColumnModel(this.cm);
5937         this.cm = this.colModel;
5938         this.cm.xmodule = this.xmodule || false;
5939     }
5940     if (this.store) {
5941         this.store= Roo.factory(this.store, Roo.data);
5942         this.ds = this.store;
5943         this.ds.xmodule = this.xmodule || false;
5944          
5945     }
5946     if (this.footer && this.store) {
5947         this.footer.dataSource = this.ds;
5948         this.footer = Roo.factory(this.footer);
5949     }
5950     
5951     /** @private */
5952     this.addEvents({
5953         /**
5954          * @event cellclick
5955          * Fires when a cell is clicked
5956          * @param {Roo.bootstrap.Table} this
5957          * @param {Roo.Element} el
5958          * @param {Number} rowIndex
5959          * @param {Number} columnIndex
5960          * @param {Roo.EventObject} e
5961          */
5962         "cellclick" : true,
5963         /**
5964          * @event celldblclick
5965          * Fires when a cell is double clicked
5966          * @param {Roo.bootstrap.Table} this
5967          * @param {Roo.Element} el
5968          * @param {Number} rowIndex
5969          * @param {Number} columnIndex
5970          * @param {Roo.EventObject} e
5971          */
5972         "celldblclick" : true,
5973         /**
5974          * @event rowclick
5975          * Fires when a row is clicked
5976          * @param {Roo.bootstrap.Table} this
5977          * @param {Roo.Element} el
5978          * @param {Number} rowIndex
5979          * @param {Roo.EventObject} e
5980          */
5981         "rowclick" : true,
5982         /**
5983          * @event rowdblclick
5984          * Fires when a row is double clicked
5985          * @param {Roo.bootstrap.Table} this
5986          * @param {Roo.Element} el
5987          * @param {Number} rowIndex
5988          * @param {Roo.EventObject} e
5989          */
5990         "rowdblclick" : true,
5991         /**
5992          * @event mouseover
5993          * Fires when a mouseover occur
5994          * @param {Roo.bootstrap.Table} this
5995          * @param {Roo.Element} el
5996          * @param {Number} rowIndex
5997          * @param {Number} columnIndex
5998          * @param {Roo.EventObject} e
5999          */
6000         "mouseover" : true,
6001         /**
6002          * @event mouseout
6003          * Fires when a mouseout occur
6004          * @param {Roo.bootstrap.Table} this
6005          * @param {Roo.Element} el
6006          * @param {Number} rowIndex
6007          * @param {Number} columnIndex
6008          * @param {Roo.EventObject} e
6009          */
6010         "mouseout" : true,
6011         /**
6012          * @event rowclass
6013          * Fires when a row is rendered, so you can change add a style to it.
6014          * @param {Roo.bootstrap.Table} this
6015          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6016          */
6017         'rowclass' : true,
6018           /**
6019          * @event rowsrendered
6020          * Fires when all the  rows have been rendered
6021          * @param {Roo.bootstrap.Table} this
6022          */
6023         'rowsrendered' : true,
6024         /**
6025          * @event contextmenu
6026          * The raw contextmenu event for the entire grid.
6027          * @param {Roo.EventObject} e
6028          */
6029         "contextmenu" : true,
6030         /**
6031          * @event rowcontextmenu
6032          * Fires when a row is right clicked
6033          * @param {Roo.bootstrap.Table} this
6034          * @param {Number} rowIndex
6035          * @param {Roo.EventObject} e
6036          */
6037         "rowcontextmenu" : true,
6038         /**
6039          * @event cellcontextmenu
6040          * Fires when a cell is right clicked
6041          * @param {Roo.bootstrap.Table} this
6042          * @param {Number} rowIndex
6043          * @param {Number} cellIndex
6044          * @param {Roo.EventObject} e
6045          */
6046          "cellcontextmenu" : true,
6047          /**
6048          * @event headercontextmenu
6049          * Fires when a header is right clicked
6050          * @param {Roo.bootstrap.Table} this
6051          * @param {Number} columnIndex
6052          * @param {Roo.EventObject} e
6053          */
6054         "headercontextmenu" : true
6055     });
6056 };
6057
6058 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6059     
6060     cls: false,
6061     align: false,
6062     bgcolor: false,
6063     border: false,
6064     cellpadding: false,
6065     cellspacing: false,
6066     frame: false,
6067     rules: false,
6068     sortable: false,
6069     summary: false,
6070     width: false,
6071     striped : false,
6072     scrollBody : false,
6073     bordered: false,
6074     hover:  false,
6075     condensed : false,
6076     responsive : false,
6077     sm : false,
6078     cm : false,
6079     store : false,
6080     loadMask : false,
6081     footerShow : true,
6082     headerShow : true,
6083   
6084     rowSelection : false,
6085     cellSelection : false,
6086     layout : false,
6087     
6088     // Roo.Element - the tbody
6089     mainBody: false,
6090     // Roo.Element - thead element
6091     mainHead: false,
6092     
6093     container: false, // used by gridpanel...
6094     
6095     lazyLoad : false,
6096     
6097     CSS : Roo.util.CSS,
6098     
6099     auto_hide_footer : false,
6100     
6101     getAutoCreate : function()
6102     {
6103         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6104         
6105         cfg = {
6106             tag: 'table',
6107             cls : 'table',
6108             cn : []
6109         };
6110         if (this.scrollBody) {
6111             cfg.cls += ' table-body-fixed';
6112         }    
6113         if (this.striped) {
6114             cfg.cls += ' table-striped';
6115         }
6116         
6117         if (this.hover) {
6118             cfg.cls += ' table-hover';
6119         }
6120         if (this.bordered) {
6121             cfg.cls += ' table-bordered';
6122         }
6123         if (this.condensed) {
6124             cfg.cls += ' table-condensed';
6125         }
6126         if (this.responsive) {
6127             cfg.cls += ' table-responsive';
6128         }
6129         
6130         if (this.cls) {
6131             cfg.cls+=  ' ' +this.cls;
6132         }
6133         
6134         // this lot should be simplifed...
6135         var _t = this;
6136         var cp = [
6137             'align',
6138             'bgcolor',
6139             'border',
6140             'cellpadding',
6141             'cellspacing',
6142             'frame',
6143             'rules',
6144             'sortable',
6145             'summary',
6146             'width'
6147         ].forEach(function(k) {
6148             if (_t[k]) {
6149                 cfg[k] = _t[k];
6150             }
6151         });
6152         
6153         
6154         if (this.layout) {
6155             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6156         }
6157         
6158         if(this.store || this.cm){
6159             if(this.headerShow){
6160                 cfg.cn.push(this.renderHeader());
6161             }
6162             
6163             cfg.cn.push(this.renderBody());
6164             
6165             if(this.footerShow){
6166                 cfg.cn.push(this.renderFooter());
6167             }
6168             // where does this come from?
6169             //cfg.cls+=  ' TableGrid';
6170         }
6171         
6172         return { cn : [ cfg ] };
6173     },
6174     
6175     initEvents : function()
6176     {   
6177         if(!this.store || !this.cm){
6178             return;
6179         }
6180         if (this.selModel) {
6181             this.selModel.initEvents();
6182         }
6183         
6184         
6185         //Roo.log('initEvents with ds!!!!');
6186         
6187         this.mainBody = this.el.select('tbody', true).first();
6188         this.mainHead = this.el.select('thead', true).first();
6189         this.mainFoot = this.el.select('tfoot', true).first();
6190         
6191         
6192         
6193         var _this = this;
6194         
6195         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6196             e.on('click', _this.sort, _this);
6197         });
6198         
6199         this.mainBody.on("click", this.onClick, this);
6200         this.mainBody.on("dblclick", this.onDblClick, this);
6201         
6202         // why is this done????? = it breaks dialogs??
6203         //this.parent().el.setStyle('position', 'relative');
6204         
6205         
6206         if (this.footer) {
6207             this.footer.parentId = this.id;
6208             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6209             
6210             if(this.lazyLoad){
6211                 this.el.select('tfoot tr td').first().addClass('hide');
6212             }
6213         } 
6214         
6215         if(this.loadMask) {
6216             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6217         }
6218         
6219         this.store.on('load', this.onLoad, this);
6220         this.store.on('beforeload', this.onBeforeLoad, this);
6221         this.store.on('update', this.onUpdate, this);
6222         this.store.on('add', this.onAdd, this);
6223         this.store.on("clear", this.clear, this);
6224         
6225         this.el.on("contextmenu", this.onContextMenu, this);
6226         
6227         this.mainBody.on('scroll', this.onBodyScroll, this);
6228         
6229         this.cm.on("headerchange", this.onHeaderChange, this);
6230         
6231         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6232         
6233     },
6234     
6235     onContextMenu : function(e, t)
6236     {
6237         this.processEvent("contextmenu", e);
6238     },
6239     
6240     processEvent : function(name, e)
6241     {
6242         if (name != 'touchstart' ) {
6243             this.fireEvent(name, e);    
6244         }
6245         
6246         var t = e.getTarget();
6247         
6248         var cell = Roo.get(t);
6249         
6250         if(!cell){
6251             return;
6252         }
6253         
6254         if(cell.findParent('tfoot', false, true)){
6255             return;
6256         }
6257         
6258         if(cell.findParent('thead', false, true)){
6259             
6260             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6261                 cell = Roo.get(t).findParent('th', false, true);
6262                 if (!cell) {
6263                     Roo.log("failed to find th in thead?");
6264                     Roo.log(e.getTarget());
6265                     return;
6266                 }
6267             }
6268             
6269             var cellIndex = cell.dom.cellIndex;
6270             
6271             var ename = name == 'touchstart' ? 'click' : name;
6272             this.fireEvent("header" + ename, this, cellIndex, e);
6273             
6274             return;
6275         }
6276         
6277         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6278             cell = Roo.get(t).findParent('td', false, true);
6279             if (!cell) {
6280                 Roo.log("failed to find th in tbody?");
6281                 Roo.log(e.getTarget());
6282                 return;
6283             }
6284         }
6285         
6286         var row = cell.findParent('tr', false, true);
6287         var cellIndex = cell.dom.cellIndex;
6288         var rowIndex = row.dom.rowIndex - 1;
6289         
6290         if(row !== false){
6291             
6292             this.fireEvent("row" + name, this, rowIndex, e);
6293             
6294             if(cell !== false){
6295             
6296                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6297             }
6298         }
6299         
6300     },
6301     
6302     onMouseover : function(e, el)
6303     {
6304         var cell = Roo.get(el);
6305         
6306         if(!cell){
6307             return;
6308         }
6309         
6310         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6311             cell = cell.findParent('td', false, true);
6312         }
6313         
6314         var row = cell.findParent('tr', false, true);
6315         var cellIndex = cell.dom.cellIndex;
6316         var rowIndex = row.dom.rowIndex - 1; // start from 0
6317         
6318         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6319         
6320     },
6321     
6322     onMouseout : function(e, el)
6323     {
6324         var cell = Roo.get(el);
6325         
6326         if(!cell){
6327             return;
6328         }
6329         
6330         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6331             cell = cell.findParent('td', false, true);
6332         }
6333         
6334         var row = cell.findParent('tr', false, true);
6335         var cellIndex = cell.dom.cellIndex;
6336         var rowIndex = row.dom.rowIndex - 1; // start from 0
6337         
6338         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6339         
6340     },
6341     
6342     onClick : function(e, el)
6343     {
6344         var cell = Roo.get(el);
6345         
6346         if(!cell || (!this.cellSelection && !this.rowSelection)){
6347             return;
6348         }
6349         
6350         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6351             cell = cell.findParent('td', false, true);
6352         }
6353         
6354         if(!cell || typeof(cell) == 'undefined'){
6355             return;
6356         }
6357         
6358         var row = cell.findParent('tr', false, true);
6359         
6360         if(!row || typeof(row) == 'undefined'){
6361             return;
6362         }
6363         
6364         var cellIndex = cell.dom.cellIndex;
6365         var rowIndex = this.getRowIndex(row);
6366         
6367         // why??? - should these not be based on SelectionModel?
6368         if(this.cellSelection){
6369             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6370         }
6371         
6372         if(this.rowSelection){
6373             this.fireEvent('rowclick', this, row, rowIndex, e);
6374         }
6375         
6376         
6377     },
6378         
6379     onDblClick : function(e,el)
6380     {
6381         var cell = Roo.get(el);
6382         
6383         if(!cell || (!this.cellSelection && !this.rowSelection)){
6384             return;
6385         }
6386         
6387         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6388             cell = cell.findParent('td', false, true);
6389         }
6390         
6391         if(!cell || typeof(cell) == 'undefined'){
6392             return;
6393         }
6394         
6395         var row = cell.findParent('tr', false, true);
6396         
6397         if(!row || typeof(row) == 'undefined'){
6398             return;
6399         }
6400         
6401         var cellIndex = cell.dom.cellIndex;
6402         var rowIndex = this.getRowIndex(row);
6403         
6404         if(this.cellSelection){
6405             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6406         }
6407         
6408         if(this.rowSelection){
6409             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6410         }
6411     },
6412     
6413     sort : function(e,el)
6414     {
6415         var col = Roo.get(el);
6416         
6417         if(!col.hasClass('sortable')){
6418             return;
6419         }
6420         
6421         var sort = col.attr('sort');
6422         var dir = 'ASC';
6423         
6424         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6425             dir = 'DESC';
6426         }
6427         
6428         this.store.sortInfo = {field : sort, direction : dir};
6429         
6430         if (this.footer) {
6431             Roo.log("calling footer first");
6432             this.footer.onClick('first');
6433         } else {
6434         
6435             this.store.load({ params : { start : 0 } });
6436         }
6437     },
6438     
6439     renderHeader : function()
6440     {
6441         var header = {
6442             tag: 'thead',
6443             cn : []
6444         };
6445         
6446         var cm = this.cm;
6447         this.totalWidth = 0;
6448         
6449         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6450             
6451             var config = cm.config[i];
6452             
6453             var c = {
6454                 tag: 'th',
6455                 cls : 'x-hcol-' + i,
6456                 style : '',
6457                 html: cm.getColumnHeader(i)
6458             };
6459             
6460             var hh = '';
6461             
6462             if(typeof(config.sortable) != 'undefined' && config.sortable){
6463                 c.cls = 'sortable';
6464                 c.html = '<i class="glyphicon"></i>' + c.html;
6465             }
6466             
6467             if(typeof(config.lgHeader) != 'undefined'){
6468                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6469             }
6470             
6471             if(typeof(config.mdHeader) != 'undefined'){
6472                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6473             }
6474             
6475             if(typeof(config.smHeader) != 'undefined'){
6476                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6477             }
6478             
6479             if(typeof(config.xsHeader) != 'undefined'){
6480                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6481             }
6482             
6483             if(hh.length){
6484                 c.html = hh;
6485             }
6486             
6487             if(typeof(config.tooltip) != 'undefined'){
6488                 c.tooltip = config.tooltip;
6489             }
6490             
6491             if(typeof(config.colspan) != 'undefined'){
6492                 c.colspan = config.colspan;
6493             }
6494             
6495             if(typeof(config.hidden) != 'undefined' && config.hidden){
6496                 c.style += ' display:none;';
6497             }
6498             
6499             if(typeof(config.dataIndex) != 'undefined'){
6500                 c.sort = config.dataIndex;
6501             }
6502             
6503            
6504             
6505             if(typeof(config.align) != 'undefined' && config.align.length){
6506                 c.style += ' text-align:' + config.align + ';';
6507             }
6508             
6509             if(typeof(config.width) != 'undefined'){
6510                 c.style += ' width:' + config.width + 'px;';
6511                 this.totalWidth += config.width;
6512             } else {
6513                 this.totalWidth += 100; // assume minimum of 100 per column?
6514             }
6515             
6516             if(typeof(config.cls) != 'undefined'){
6517                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6518             }
6519             
6520             ['xs','sm','md','lg'].map(function(size){
6521                 
6522                 if(typeof(config[size]) == 'undefined'){
6523                     return;
6524                 }
6525                 
6526                 if (!config[size]) { // 0 = hidden
6527                     c.cls += ' hidden-' + size;
6528                     return;
6529                 }
6530                 
6531                 c.cls += ' col-' + size + '-' + config[size];
6532
6533             });
6534             
6535             header.cn.push(c)
6536         }
6537         
6538         return header;
6539     },
6540     
6541     renderBody : function()
6542     {
6543         var body = {
6544             tag: 'tbody',
6545             cn : [
6546                 {
6547                     tag: 'tr',
6548                     cn : [
6549                         {
6550                             tag : 'td',
6551                             colspan :  this.cm.getColumnCount()
6552                         }
6553                     ]
6554                 }
6555             ]
6556         };
6557         
6558         return body;
6559     },
6560     
6561     renderFooter : function()
6562     {
6563         var footer = {
6564             tag: 'tfoot',
6565             cn : [
6566                 {
6567                     tag: 'tr',
6568                     cn : [
6569                         {
6570                             tag : 'td',
6571                             colspan :  this.cm.getColumnCount()
6572                         }
6573                     ]
6574                 }
6575             ]
6576         };
6577         
6578         return footer;
6579     },
6580     
6581     
6582     
6583     onLoad : function()
6584     {
6585 //        Roo.log('ds onload');
6586         this.clear();
6587         
6588         var _this = this;
6589         var cm = this.cm;
6590         var ds = this.store;
6591         
6592         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6593             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6594             if (_this.store.sortInfo) {
6595                     
6596                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6597                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6598                 }
6599                 
6600                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6601                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6602                 }
6603             }
6604         });
6605         
6606         var tbody =  this.mainBody;
6607               
6608         if(ds.getCount() > 0){
6609             ds.data.each(function(d,rowIndex){
6610                 var row =  this.renderRow(cm, ds, rowIndex);
6611                 
6612                 tbody.createChild(row);
6613                 
6614                 var _this = this;
6615                 
6616                 if(row.cellObjects.length){
6617                     Roo.each(row.cellObjects, function(r){
6618                         _this.renderCellObject(r);
6619                     })
6620                 }
6621                 
6622             }, this);
6623         }
6624         
6625         var tfoot = this.el.select('tfoot', true).first();
6626         
6627         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6628             
6629             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6630             
6631             var total = this.ds.getTotalCount();
6632             
6633             if(this.footer.pageSize < total){
6634                 this.mainFoot.show();
6635             }
6636         }
6637         
6638         Roo.each(this.el.select('tbody td', true).elements, function(e){
6639             e.on('mouseover', _this.onMouseover, _this);
6640         });
6641         
6642         Roo.each(this.el.select('tbody td', true).elements, function(e){
6643             e.on('mouseout', _this.onMouseout, _this);
6644         });
6645         this.fireEvent('rowsrendered', this);
6646         
6647         this.autoSize();
6648     },
6649     
6650     
6651     onUpdate : function(ds,record)
6652     {
6653         this.refreshRow(record);
6654         this.autoSize();
6655     },
6656     
6657     onRemove : function(ds, record, index, isUpdate){
6658         if(isUpdate !== true){
6659             this.fireEvent("beforerowremoved", this, index, record);
6660         }
6661         var bt = this.mainBody.dom;
6662         
6663         var rows = this.el.select('tbody > tr', true).elements;
6664         
6665         if(typeof(rows[index]) != 'undefined'){
6666             bt.removeChild(rows[index].dom);
6667         }
6668         
6669 //        if(bt.rows[index]){
6670 //            bt.removeChild(bt.rows[index]);
6671 //        }
6672         
6673         if(isUpdate !== true){
6674             //this.stripeRows(index);
6675             //this.syncRowHeights(index, index);
6676             //this.layout();
6677             this.fireEvent("rowremoved", this, index, record);
6678         }
6679     },
6680     
6681     onAdd : function(ds, records, rowIndex)
6682     {
6683         //Roo.log('on Add called');
6684         // - note this does not handle multiple adding very well..
6685         var bt = this.mainBody.dom;
6686         for (var i =0 ; i < records.length;i++) {
6687             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6688             //Roo.log(records[i]);
6689             //Roo.log(this.store.getAt(rowIndex+i));
6690             this.insertRow(this.store, rowIndex + i, false);
6691             return;
6692         }
6693         
6694     },
6695     
6696     
6697     refreshRow : function(record){
6698         var ds = this.store, index;
6699         if(typeof record == 'number'){
6700             index = record;
6701             record = ds.getAt(index);
6702         }else{
6703             index = ds.indexOf(record);
6704         }
6705         this.insertRow(ds, index, true);
6706         this.autoSize();
6707         this.onRemove(ds, record, index+1, true);
6708         this.autoSize();
6709         //this.syncRowHeights(index, index);
6710         //this.layout();
6711         this.fireEvent("rowupdated", this, index, record);
6712     },
6713     
6714     insertRow : function(dm, rowIndex, isUpdate){
6715         
6716         if(!isUpdate){
6717             this.fireEvent("beforerowsinserted", this, rowIndex);
6718         }
6719             //var s = this.getScrollState();
6720         var row = this.renderRow(this.cm, this.store, rowIndex);
6721         // insert before rowIndex..
6722         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6723         
6724         var _this = this;
6725                 
6726         if(row.cellObjects.length){
6727             Roo.each(row.cellObjects, function(r){
6728                 _this.renderCellObject(r);
6729             })
6730         }
6731             
6732         if(!isUpdate){
6733             this.fireEvent("rowsinserted", this, rowIndex);
6734             //this.syncRowHeights(firstRow, lastRow);
6735             //this.stripeRows(firstRow);
6736             //this.layout();
6737         }
6738         
6739     },
6740     
6741     
6742     getRowDom : function(rowIndex)
6743     {
6744         var rows = this.el.select('tbody > tr', true).elements;
6745         
6746         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6747         
6748     },
6749     // returns the object tree for a tr..
6750   
6751     
6752     renderRow : function(cm, ds, rowIndex) 
6753     {
6754         var d = ds.getAt(rowIndex);
6755         
6756         var row = {
6757             tag : 'tr',
6758             cls : 'x-row-' + rowIndex,
6759             cn : []
6760         };
6761             
6762         var cellObjects = [];
6763         
6764         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6765             var config = cm.config[i];
6766             
6767             var renderer = cm.getRenderer(i);
6768             var value = '';
6769             var id = false;
6770             
6771             if(typeof(renderer) !== 'undefined'){
6772                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6773             }
6774             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6775             // and are rendered into the cells after the row is rendered - using the id for the element.
6776             
6777             if(typeof(value) === 'object'){
6778                 id = Roo.id();
6779                 cellObjects.push({
6780                     container : id,
6781                     cfg : value 
6782                 })
6783             }
6784             
6785             var rowcfg = {
6786                 record: d,
6787                 rowIndex : rowIndex,
6788                 colIndex : i,
6789                 rowClass : ''
6790             };
6791
6792             this.fireEvent('rowclass', this, rowcfg);
6793             
6794             var td = {
6795                 tag: 'td',
6796                 cls : rowcfg.rowClass + ' x-col-' + i,
6797                 style: '',
6798                 html: (typeof(value) === 'object') ? '' : value
6799             };
6800             
6801             if (id) {
6802                 td.id = id;
6803             }
6804             
6805             if(typeof(config.colspan) != 'undefined'){
6806                 td.colspan = config.colspan;
6807             }
6808             
6809             if(typeof(config.hidden) != 'undefined' && config.hidden){
6810                 td.style += ' display:none;';
6811             }
6812             
6813             if(typeof(config.align) != 'undefined' && config.align.length){
6814                 td.style += ' text-align:' + config.align + ';';
6815             }
6816             if(typeof(config.valign) != 'undefined' && config.valign.length){
6817                 td.style += ' vertical-align:' + config.valign + ';';
6818             }
6819             
6820             if(typeof(config.width) != 'undefined'){
6821                 td.style += ' width:' +  config.width + 'px;';
6822             }
6823             
6824             if(typeof(config.cursor) != 'undefined'){
6825                 td.style += ' cursor:' +  config.cursor + ';';
6826             }
6827             
6828             if(typeof(config.cls) != 'undefined'){
6829                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6830             }
6831             
6832             ['xs','sm','md','lg'].map(function(size){
6833                 
6834                 if(typeof(config[size]) == 'undefined'){
6835                     return;
6836                 }
6837                 
6838                 if (!config[size]) { // 0 = hidden
6839                     td.cls += ' hidden-' + size;
6840                     return;
6841                 }
6842                 
6843                 td.cls += ' col-' + size + '-' + config[size];
6844
6845             });
6846             
6847             row.cn.push(td);
6848            
6849         }
6850         
6851         row.cellObjects = cellObjects;
6852         
6853         return row;
6854           
6855     },
6856     
6857     
6858     
6859     onBeforeLoad : function()
6860     {
6861         
6862     },
6863      /**
6864      * Remove all rows
6865      */
6866     clear : function()
6867     {
6868         this.el.select('tbody', true).first().dom.innerHTML = '';
6869     },
6870     /**
6871      * Show or hide a row.
6872      * @param {Number} rowIndex to show or hide
6873      * @param {Boolean} state hide
6874      */
6875     setRowVisibility : function(rowIndex, state)
6876     {
6877         var bt = this.mainBody.dom;
6878         
6879         var rows = this.el.select('tbody > tr', true).elements;
6880         
6881         if(typeof(rows[rowIndex]) == 'undefined'){
6882             return;
6883         }
6884         rows[rowIndex].dom.style.display = state ? '' : 'none';
6885     },
6886     
6887     
6888     getSelectionModel : function(){
6889         if(!this.selModel){
6890             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6891         }
6892         return this.selModel;
6893     },
6894     /*
6895      * Render the Roo.bootstrap object from renderder
6896      */
6897     renderCellObject : function(r)
6898     {
6899         var _this = this;
6900         
6901         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6902         
6903         var t = r.cfg.render(r.container);
6904         
6905         if(r.cfg.cn){
6906             Roo.each(r.cfg.cn, function(c){
6907                 var child = {
6908                     container: t.getChildContainer(),
6909                     cfg: c
6910                 };
6911                 _this.renderCellObject(child);
6912             })
6913         }
6914     },
6915     
6916     getRowIndex : function(row)
6917     {
6918         var rowIndex = -1;
6919         
6920         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6921             if(el != row){
6922                 return;
6923             }
6924             
6925             rowIndex = index;
6926         });
6927         
6928         return rowIndex;
6929     },
6930      /**
6931      * Returns the grid's underlying element = used by panel.Grid
6932      * @return {Element} The element
6933      */
6934     getGridEl : function(){
6935         return this.el;
6936     },
6937      /**
6938      * Forces a resize - used by panel.Grid
6939      * @return {Element} The element
6940      */
6941     autoSize : function()
6942     {
6943         //var ctr = Roo.get(this.container.dom.parentElement);
6944         var ctr = Roo.get(this.el.dom);
6945         
6946         var thd = this.getGridEl().select('thead',true).first();
6947         var tbd = this.getGridEl().select('tbody', true).first();
6948         var tfd = this.getGridEl().select('tfoot', true).first();
6949         
6950         var cw = ctr.getWidth();
6951         
6952         if (tbd) {
6953             
6954             tbd.setSize(ctr.getWidth(),
6955                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6956             );
6957             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6958             cw -= barsize;
6959         }
6960         cw = Math.max(cw, this.totalWidth);
6961         this.getGridEl().select('tr',true).setWidth(cw);
6962         // resize 'expandable coloumn?
6963         
6964         return; // we doe not have a view in this design..
6965         
6966     },
6967     onBodyScroll: function()
6968     {
6969         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6970         if(this.mainHead){
6971             this.mainHead.setStyle({
6972                 'position' : 'relative',
6973                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6974             });
6975         }
6976         
6977         if(this.lazyLoad){
6978             
6979             var scrollHeight = this.mainBody.dom.scrollHeight;
6980             
6981             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6982             
6983             var height = this.mainBody.getHeight();
6984             
6985             if(scrollHeight - height == scrollTop) {
6986                 
6987                 var total = this.ds.getTotalCount();
6988                 
6989                 if(this.footer.cursor + this.footer.pageSize < total){
6990                     
6991                     this.footer.ds.load({
6992                         params : {
6993                             start : this.footer.cursor + this.footer.pageSize,
6994                             limit : this.footer.pageSize
6995                         },
6996                         add : true
6997                     });
6998                 }
6999             }
7000             
7001         }
7002     },
7003     
7004     onHeaderChange : function()
7005     {
7006         var header = this.renderHeader();
7007         var table = this.el.select('table', true).first();
7008         
7009         this.mainHead.remove();
7010         this.mainHead = table.createChild(header, this.mainBody, false);
7011     },
7012     
7013     onHiddenChange : function(colModel, colIndex, hidden)
7014     {
7015         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7016         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7017         
7018         this.CSS.updateRule(thSelector, "display", "");
7019         this.CSS.updateRule(tdSelector, "display", "");
7020         
7021         if(hidden){
7022             this.CSS.updateRule(thSelector, "display", "none");
7023             this.CSS.updateRule(tdSelector, "display", "none");
7024         }
7025         
7026         this.onHeaderChange();
7027         this.onLoad();
7028         
7029     }
7030     
7031 });
7032
7033  
7034
7035  /*
7036  * - LGPL
7037  *
7038  * table cell
7039  * 
7040  */
7041
7042 /**
7043  * @class Roo.bootstrap.TableCell
7044  * @extends Roo.bootstrap.Component
7045  * Bootstrap TableCell class
7046  * @cfg {String} html cell contain text
7047  * @cfg {String} cls cell class
7048  * @cfg {String} tag cell tag (td|th) default td
7049  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7050  * @cfg {String} align Aligns the content in a cell
7051  * @cfg {String} axis Categorizes cells
7052  * @cfg {String} bgcolor Specifies the background color of a cell
7053  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7054  * @cfg {Number} colspan Specifies the number of columns a cell should span
7055  * @cfg {String} headers Specifies one or more header cells a cell is related to
7056  * @cfg {Number} height Sets the height of a cell
7057  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7058  * @cfg {Number} rowspan Sets the number of rows a cell should span
7059  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7060  * @cfg {String} valign Vertical aligns the content in a cell
7061  * @cfg {Number} width Specifies the width of a cell
7062  * 
7063  * @constructor
7064  * Create a new TableCell
7065  * @param {Object} config The config object
7066  */
7067
7068 Roo.bootstrap.TableCell = function(config){
7069     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7070 };
7071
7072 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7073     
7074     html: false,
7075     cls: false,
7076     tag: false,
7077     abbr: false,
7078     align: false,
7079     axis: false,
7080     bgcolor: false,
7081     charoff: false,
7082     colspan: false,
7083     headers: false,
7084     height: false,
7085     nowrap: false,
7086     rowspan: false,
7087     scope: false,
7088     valign: false,
7089     width: false,
7090     
7091     
7092     getAutoCreate : function(){
7093         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7094         
7095         cfg = {
7096             tag: 'td'
7097         };
7098         
7099         if(this.tag){
7100             cfg.tag = this.tag;
7101         }
7102         
7103         if (this.html) {
7104             cfg.html=this.html
7105         }
7106         if (this.cls) {
7107             cfg.cls=this.cls
7108         }
7109         if (this.abbr) {
7110             cfg.abbr=this.abbr
7111         }
7112         if (this.align) {
7113             cfg.align=this.align
7114         }
7115         if (this.axis) {
7116             cfg.axis=this.axis
7117         }
7118         if (this.bgcolor) {
7119             cfg.bgcolor=this.bgcolor
7120         }
7121         if (this.charoff) {
7122             cfg.charoff=this.charoff
7123         }
7124         if (this.colspan) {
7125             cfg.colspan=this.colspan
7126         }
7127         if (this.headers) {
7128             cfg.headers=this.headers
7129         }
7130         if (this.height) {
7131             cfg.height=this.height
7132         }
7133         if (this.nowrap) {
7134             cfg.nowrap=this.nowrap
7135         }
7136         if (this.rowspan) {
7137             cfg.rowspan=this.rowspan
7138         }
7139         if (this.scope) {
7140             cfg.scope=this.scope
7141         }
7142         if (this.valign) {
7143             cfg.valign=this.valign
7144         }
7145         if (this.width) {
7146             cfg.width=this.width
7147         }
7148         
7149         
7150         return cfg;
7151     }
7152    
7153 });
7154
7155  
7156
7157  /*
7158  * - LGPL
7159  *
7160  * table row
7161  * 
7162  */
7163
7164 /**
7165  * @class Roo.bootstrap.TableRow
7166  * @extends Roo.bootstrap.Component
7167  * Bootstrap TableRow class
7168  * @cfg {String} cls row class
7169  * @cfg {String} align Aligns the content in a table row
7170  * @cfg {String} bgcolor Specifies a background color for a table row
7171  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7172  * @cfg {String} valign Vertical aligns the content in a table row
7173  * 
7174  * @constructor
7175  * Create a new TableRow
7176  * @param {Object} config The config object
7177  */
7178
7179 Roo.bootstrap.TableRow = function(config){
7180     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7181 };
7182
7183 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7184     
7185     cls: false,
7186     align: false,
7187     bgcolor: false,
7188     charoff: false,
7189     valign: false,
7190     
7191     getAutoCreate : function(){
7192         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7193         
7194         cfg = {
7195             tag: 'tr'
7196         };
7197             
7198         if(this.cls){
7199             cfg.cls = this.cls;
7200         }
7201         if(this.align){
7202             cfg.align = this.align;
7203         }
7204         if(this.bgcolor){
7205             cfg.bgcolor = this.bgcolor;
7206         }
7207         if(this.charoff){
7208             cfg.charoff = this.charoff;
7209         }
7210         if(this.valign){
7211             cfg.valign = this.valign;
7212         }
7213         
7214         return cfg;
7215     }
7216    
7217 });
7218
7219  
7220
7221  /*
7222  * - LGPL
7223  *
7224  * table body
7225  * 
7226  */
7227
7228 /**
7229  * @class Roo.bootstrap.TableBody
7230  * @extends Roo.bootstrap.Component
7231  * Bootstrap TableBody class
7232  * @cfg {String} cls element class
7233  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7234  * @cfg {String} align Aligns the content inside the element
7235  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7236  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7237  * 
7238  * @constructor
7239  * Create a new TableBody
7240  * @param {Object} config The config object
7241  */
7242
7243 Roo.bootstrap.TableBody = function(config){
7244     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7245 };
7246
7247 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7248     
7249     cls: false,
7250     tag: false,
7251     align: false,
7252     charoff: false,
7253     valign: false,
7254     
7255     getAutoCreate : function(){
7256         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7257         
7258         cfg = {
7259             tag: 'tbody'
7260         };
7261             
7262         if (this.cls) {
7263             cfg.cls=this.cls
7264         }
7265         if(this.tag){
7266             cfg.tag = this.tag;
7267         }
7268         
7269         if(this.align){
7270             cfg.align = this.align;
7271         }
7272         if(this.charoff){
7273             cfg.charoff = this.charoff;
7274         }
7275         if(this.valign){
7276             cfg.valign = this.valign;
7277         }
7278         
7279         return cfg;
7280     }
7281     
7282     
7283 //    initEvents : function()
7284 //    {
7285 //        
7286 //        if(!this.store){
7287 //            return;
7288 //        }
7289 //        
7290 //        this.store = Roo.factory(this.store, Roo.data);
7291 //        this.store.on('load', this.onLoad, this);
7292 //        
7293 //        this.store.load();
7294 //        
7295 //    },
7296 //    
7297 //    onLoad: function () 
7298 //    {   
7299 //        this.fireEvent('load', this);
7300 //    }
7301 //    
7302 //   
7303 });
7304
7305  
7306
7307  /*
7308  * Based on:
7309  * Ext JS Library 1.1.1
7310  * Copyright(c) 2006-2007, Ext JS, LLC.
7311  *
7312  * Originally Released Under LGPL - original licence link has changed is not relivant.
7313  *
7314  * Fork - LGPL
7315  * <script type="text/javascript">
7316  */
7317
7318 // as we use this in bootstrap.
7319 Roo.namespace('Roo.form');
7320  /**
7321  * @class Roo.form.Action
7322  * Internal Class used to handle form actions
7323  * @constructor
7324  * @param {Roo.form.BasicForm} el The form element or its id
7325  * @param {Object} config Configuration options
7326  */
7327
7328  
7329  
7330 // define the action interface
7331 Roo.form.Action = function(form, options){
7332     this.form = form;
7333     this.options = options || {};
7334 };
7335 /**
7336  * Client Validation Failed
7337  * @const 
7338  */
7339 Roo.form.Action.CLIENT_INVALID = 'client';
7340 /**
7341  * Server Validation Failed
7342  * @const 
7343  */
7344 Roo.form.Action.SERVER_INVALID = 'server';
7345  /**
7346  * Connect to Server Failed
7347  * @const 
7348  */
7349 Roo.form.Action.CONNECT_FAILURE = 'connect';
7350 /**
7351  * Reading Data from Server Failed
7352  * @const 
7353  */
7354 Roo.form.Action.LOAD_FAILURE = 'load';
7355
7356 Roo.form.Action.prototype = {
7357     type : 'default',
7358     failureType : undefined,
7359     response : undefined,
7360     result : undefined,
7361
7362     // interface method
7363     run : function(options){
7364
7365     },
7366
7367     // interface method
7368     success : function(response){
7369
7370     },
7371
7372     // interface method
7373     handleResponse : function(response){
7374
7375     },
7376
7377     // default connection failure
7378     failure : function(response){
7379         
7380         this.response = response;
7381         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7382         this.form.afterAction(this, false);
7383     },
7384
7385     processResponse : function(response){
7386         this.response = response;
7387         if(!response.responseText){
7388             return true;
7389         }
7390         this.result = this.handleResponse(response);
7391         return this.result;
7392     },
7393
7394     // utility functions used internally
7395     getUrl : function(appendParams){
7396         var url = this.options.url || this.form.url || this.form.el.dom.action;
7397         if(appendParams){
7398             var p = this.getParams();
7399             if(p){
7400                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7401             }
7402         }
7403         return url;
7404     },
7405
7406     getMethod : function(){
7407         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7408     },
7409
7410     getParams : function(){
7411         var bp = this.form.baseParams;
7412         var p = this.options.params;
7413         if(p){
7414             if(typeof p == "object"){
7415                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7416             }else if(typeof p == 'string' && bp){
7417                 p += '&' + Roo.urlEncode(bp);
7418             }
7419         }else if(bp){
7420             p = Roo.urlEncode(bp);
7421         }
7422         return p;
7423     },
7424
7425     createCallback : function(){
7426         return {
7427             success: this.success,
7428             failure: this.failure,
7429             scope: this,
7430             timeout: (this.form.timeout*1000),
7431             upload: this.form.fileUpload ? this.success : undefined
7432         };
7433     }
7434 };
7435
7436 Roo.form.Action.Submit = function(form, options){
7437     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7438 };
7439
7440 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7441     type : 'submit',
7442
7443     haveProgress : false,
7444     uploadComplete : false,
7445     
7446     // uploadProgress indicator.
7447     uploadProgress : function()
7448     {
7449         if (!this.form.progressUrl) {
7450             return;
7451         }
7452         
7453         if (!this.haveProgress) {
7454             Roo.MessageBox.progress("Uploading", "Uploading");
7455         }
7456         if (this.uploadComplete) {
7457            Roo.MessageBox.hide();
7458            return;
7459         }
7460         
7461         this.haveProgress = true;
7462    
7463         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7464         
7465         var c = new Roo.data.Connection();
7466         c.request({
7467             url : this.form.progressUrl,
7468             params: {
7469                 id : uid
7470             },
7471             method: 'GET',
7472             success : function(req){
7473                //console.log(data);
7474                 var rdata = false;
7475                 var edata;
7476                 try  {
7477                    rdata = Roo.decode(req.responseText)
7478                 } catch (e) {
7479                     Roo.log("Invalid data from server..");
7480                     Roo.log(edata);
7481                     return;
7482                 }
7483                 if (!rdata || !rdata.success) {
7484                     Roo.log(rdata);
7485                     Roo.MessageBox.alert(Roo.encode(rdata));
7486                     return;
7487                 }
7488                 var data = rdata.data;
7489                 
7490                 if (this.uploadComplete) {
7491                    Roo.MessageBox.hide();
7492                    return;
7493                 }
7494                    
7495                 if (data){
7496                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7497                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7498                     );
7499                 }
7500                 this.uploadProgress.defer(2000,this);
7501             },
7502        
7503             failure: function(data) {
7504                 Roo.log('progress url failed ');
7505                 Roo.log(data);
7506             },
7507             scope : this
7508         });
7509            
7510     },
7511     
7512     
7513     run : function()
7514     {
7515         // run get Values on the form, so it syncs any secondary forms.
7516         this.form.getValues();
7517         
7518         var o = this.options;
7519         var method = this.getMethod();
7520         var isPost = method == 'POST';
7521         if(o.clientValidation === false || this.form.isValid()){
7522             
7523             if (this.form.progressUrl) {
7524                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7525                     (new Date() * 1) + '' + Math.random());
7526                     
7527             } 
7528             
7529             
7530             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7531                 form:this.form.el.dom,
7532                 url:this.getUrl(!isPost),
7533                 method: method,
7534                 params:isPost ? this.getParams() : null,
7535                 isUpload: this.form.fileUpload
7536             }));
7537             
7538             this.uploadProgress();
7539
7540         }else if (o.clientValidation !== false){ // client validation failed
7541             this.failureType = Roo.form.Action.CLIENT_INVALID;
7542             this.form.afterAction(this, false);
7543         }
7544     },
7545
7546     success : function(response)
7547     {
7548         this.uploadComplete= true;
7549         if (this.haveProgress) {
7550             Roo.MessageBox.hide();
7551         }
7552         
7553         
7554         var result = this.processResponse(response);
7555         if(result === true || result.success){
7556             this.form.afterAction(this, true);
7557             return;
7558         }
7559         if(result.errors){
7560             this.form.markInvalid(result.errors);
7561             this.failureType = Roo.form.Action.SERVER_INVALID;
7562         }
7563         this.form.afterAction(this, false);
7564     },
7565     failure : function(response)
7566     {
7567         this.uploadComplete= true;
7568         if (this.haveProgress) {
7569             Roo.MessageBox.hide();
7570         }
7571         
7572         this.response = response;
7573         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7574         this.form.afterAction(this, false);
7575     },
7576     
7577     handleResponse : function(response){
7578         if(this.form.errorReader){
7579             var rs = this.form.errorReader.read(response);
7580             var errors = [];
7581             if(rs.records){
7582                 for(var i = 0, len = rs.records.length; i < len; i++) {
7583                     var r = rs.records[i];
7584                     errors[i] = r.data;
7585                 }
7586             }
7587             if(errors.length < 1){
7588                 errors = null;
7589             }
7590             return {
7591                 success : rs.success,
7592                 errors : errors
7593             };
7594         }
7595         var ret = false;
7596         try {
7597             ret = Roo.decode(response.responseText);
7598         } catch (e) {
7599             ret = {
7600                 success: false,
7601                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7602                 errors : []
7603             };
7604         }
7605         return ret;
7606         
7607     }
7608 });
7609
7610
7611 Roo.form.Action.Load = function(form, options){
7612     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7613     this.reader = this.form.reader;
7614 };
7615
7616 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7617     type : 'load',
7618
7619     run : function(){
7620         
7621         Roo.Ajax.request(Roo.apply(
7622                 this.createCallback(), {
7623                     method:this.getMethod(),
7624                     url:this.getUrl(false),
7625                     params:this.getParams()
7626         }));
7627     },
7628
7629     success : function(response){
7630         
7631         var result = this.processResponse(response);
7632         if(result === true || !result.success || !result.data){
7633             this.failureType = Roo.form.Action.LOAD_FAILURE;
7634             this.form.afterAction(this, false);
7635             return;
7636         }
7637         this.form.clearInvalid();
7638         this.form.setValues(result.data);
7639         this.form.afterAction(this, true);
7640     },
7641
7642     handleResponse : function(response){
7643         if(this.form.reader){
7644             var rs = this.form.reader.read(response);
7645             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7646             return {
7647                 success : rs.success,
7648                 data : data
7649             };
7650         }
7651         return Roo.decode(response.responseText);
7652     }
7653 });
7654
7655 Roo.form.Action.ACTION_TYPES = {
7656     'load' : Roo.form.Action.Load,
7657     'submit' : Roo.form.Action.Submit
7658 };/*
7659  * - LGPL
7660  *
7661  * form
7662  *
7663  */
7664
7665 /**
7666  * @class Roo.bootstrap.Form
7667  * @extends Roo.bootstrap.Component
7668  * Bootstrap Form class
7669  * @cfg {String} method  GET | POST (default POST)
7670  * @cfg {String} labelAlign top | left (default top)
7671  * @cfg {String} align left  | right - for navbars
7672  * @cfg {Boolean} loadMask load mask when submit (default true)
7673
7674  *
7675  * @constructor
7676  * Create a new Form
7677  * @param {Object} config The config object
7678  */
7679
7680
7681 Roo.bootstrap.Form = function(config){
7682     
7683     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7684     
7685     Roo.bootstrap.Form.popover.apply();
7686     
7687     this.addEvents({
7688         /**
7689          * @event clientvalidation
7690          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7691          * @param {Form} this
7692          * @param {Boolean} valid true if the form has passed client-side validation
7693          */
7694         clientvalidation: true,
7695         /**
7696          * @event beforeaction
7697          * Fires before any action is performed. Return false to cancel the action.
7698          * @param {Form} this
7699          * @param {Action} action The action to be performed
7700          */
7701         beforeaction: true,
7702         /**
7703          * @event actionfailed
7704          * Fires when an action fails.
7705          * @param {Form} this
7706          * @param {Action} action The action that failed
7707          */
7708         actionfailed : true,
7709         /**
7710          * @event actioncomplete
7711          * Fires when an action is completed.
7712          * @param {Form} this
7713          * @param {Action} action The action that completed
7714          */
7715         actioncomplete : true
7716     });
7717 };
7718
7719 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7720
7721      /**
7722      * @cfg {String} method
7723      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7724      */
7725     method : 'POST',
7726     /**
7727      * @cfg {String} url
7728      * The URL to use for form actions if one isn't supplied in the action options.
7729      */
7730     /**
7731      * @cfg {Boolean} fileUpload
7732      * Set to true if this form is a file upload.
7733      */
7734
7735     /**
7736      * @cfg {Object} baseParams
7737      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7738      */
7739
7740     /**
7741      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7742      */
7743     timeout: 30,
7744     /**
7745      * @cfg {Sting} align (left|right) for navbar forms
7746      */
7747     align : 'left',
7748
7749     // private
7750     activeAction : null,
7751
7752     /**
7753      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7754      * element by passing it or its id or mask the form itself by passing in true.
7755      * @type Mixed
7756      */
7757     waitMsgTarget : false,
7758
7759     loadMask : true,
7760     
7761     /**
7762      * @cfg {Boolean} errorMask (true|false) default false
7763      */
7764     errorMask : false,
7765     
7766     /**
7767      * @cfg {Number} maskOffset Default 100
7768      */
7769     maskOffset : 100,
7770     
7771     /**
7772      * @cfg {Boolean} maskBody
7773      */
7774     maskBody : false,
7775
7776     getAutoCreate : function(){
7777
7778         var cfg = {
7779             tag: 'form',
7780             method : this.method || 'POST',
7781             id : this.id || Roo.id(),
7782             cls : ''
7783         };
7784         if (this.parent().xtype.match(/^Nav/)) {
7785             cfg.cls = 'navbar-form navbar-' + this.align;
7786
7787         }
7788
7789         if (this.labelAlign == 'left' ) {
7790             cfg.cls += ' form-horizontal';
7791         }
7792
7793
7794         return cfg;
7795     },
7796     initEvents : function()
7797     {
7798         this.el.on('submit', this.onSubmit, this);
7799         // this was added as random key presses on the form where triggering form submit.
7800         this.el.on('keypress', function(e) {
7801             if (e.getCharCode() != 13) {
7802                 return true;
7803             }
7804             // we might need to allow it for textareas.. and some other items.
7805             // check e.getTarget().
7806
7807             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7808                 return true;
7809             }
7810
7811             Roo.log("keypress blocked");
7812
7813             e.preventDefault();
7814             return false;
7815         });
7816         
7817     },
7818     // private
7819     onSubmit : function(e){
7820         e.stopEvent();
7821     },
7822
7823      /**
7824      * Returns true if client-side validation on the form is successful.
7825      * @return Boolean
7826      */
7827     isValid : function(){
7828         var items = this.getItems();
7829         var valid = true;
7830         var target = false;
7831         
7832         items.each(function(f){
7833             
7834             if(f.validate()){
7835                 return;
7836             }
7837             
7838             Roo.log('invalid field: ' + f.name);
7839             
7840             valid = false;
7841
7842             if(!target && f.el.isVisible(true)){
7843                 target = f;
7844             }
7845            
7846         });
7847         
7848         if(this.errorMask && !valid){
7849             Roo.bootstrap.Form.popover.mask(this, target);
7850         }
7851         
7852         return valid;
7853     },
7854     
7855     /**
7856      * Returns true if any fields in this form have changed since their original load.
7857      * @return Boolean
7858      */
7859     isDirty : function(){
7860         var dirty = false;
7861         var items = this.getItems();
7862         items.each(function(f){
7863            if(f.isDirty()){
7864                dirty = true;
7865                return false;
7866            }
7867            return true;
7868         });
7869         return dirty;
7870     },
7871      /**
7872      * Performs a predefined action (submit or load) or custom actions you define on this form.
7873      * @param {String} actionName The name of the action type
7874      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7875      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7876      * accept other config options):
7877      * <pre>
7878 Property          Type             Description
7879 ----------------  ---------------  ----------------------------------------------------------------------------------
7880 url               String           The url for the action (defaults to the form's url)
7881 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7882 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7883 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7884                                    validate the form on the client (defaults to false)
7885      * </pre>
7886      * @return {BasicForm} this
7887      */
7888     doAction : function(action, options){
7889         if(typeof action == 'string'){
7890             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7891         }
7892         if(this.fireEvent('beforeaction', this, action) !== false){
7893             this.beforeAction(action);
7894             action.run.defer(100, action);
7895         }
7896         return this;
7897     },
7898
7899     // private
7900     beforeAction : function(action){
7901         var o = action.options;
7902         
7903         if(this.loadMask){
7904             
7905             if(this.maskBody){
7906                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7907             } else {
7908                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7909             }
7910         }
7911         // not really supported yet.. ??
7912
7913         //if(this.waitMsgTarget === true){
7914         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7915         //}else if(this.waitMsgTarget){
7916         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7917         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7918         //}else {
7919         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7920        // }
7921
7922     },
7923
7924     // private
7925     afterAction : function(action, success){
7926         this.activeAction = null;
7927         var o = action.options;
7928
7929         if(this.loadMask){
7930             
7931             if(this.maskBody){
7932                 Roo.get(document.body).unmask();
7933             } else {
7934                 this.el.unmask();
7935             }
7936         }
7937         
7938         //if(this.waitMsgTarget === true){
7939 //            this.el.unmask();
7940         //}else if(this.waitMsgTarget){
7941         //    this.waitMsgTarget.unmask();
7942         //}else{
7943         //    Roo.MessageBox.updateProgress(1);
7944         //    Roo.MessageBox.hide();
7945        // }
7946         //
7947         if(success){
7948             if(o.reset){
7949                 this.reset();
7950             }
7951             Roo.callback(o.success, o.scope, [this, action]);
7952             this.fireEvent('actioncomplete', this, action);
7953
7954         }else{
7955
7956             // failure condition..
7957             // we have a scenario where updates need confirming.
7958             // eg. if a locking scenario exists..
7959             // we look for { errors : { needs_confirm : true }} in the response.
7960             if (
7961                 (typeof(action.result) != 'undefined')  &&
7962                 (typeof(action.result.errors) != 'undefined')  &&
7963                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7964            ){
7965                 var _t = this;
7966                 Roo.log("not supported yet");
7967                  /*
7968
7969                 Roo.MessageBox.confirm(
7970                     "Change requires confirmation",
7971                     action.result.errorMsg,
7972                     function(r) {
7973                         if (r != 'yes') {
7974                             return;
7975                         }
7976                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7977                     }
7978
7979                 );
7980                 */
7981
7982
7983                 return;
7984             }
7985
7986             Roo.callback(o.failure, o.scope, [this, action]);
7987             // show an error message if no failed handler is set..
7988             if (!this.hasListener('actionfailed')) {
7989                 Roo.log("need to add dialog support");
7990                 /*
7991                 Roo.MessageBox.alert("Error",
7992                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7993                         action.result.errorMsg :
7994                         "Saving Failed, please check your entries or try again"
7995                 );
7996                 */
7997             }
7998
7999             this.fireEvent('actionfailed', this, action);
8000         }
8001
8002     },
8003     /**
8004      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8005      * @param {String} id The value to search for
8006      * @return Field
8007      */
8008     findField : function(id){
8009         var items = this.getItems();
8010         var field = items.get(id);
8011         if(!field){
8012              items.each(function(f){
8013                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8014                     field = f;
8015                     return false;
8016                 }
8017                 return true;
8018             });
8019         }
8020         return field || null;
8021     },
8022      /**
8023      * Mark fields in this form invalid in bulk.
8024      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8025      * @return {BasicForm} this
8026      */
8027     markInvalid : function(errors){
8028         if(errors instanceof Array){
8029             for(var i = 0, len = errors.length; i < len; i++){
8030                 var fieldError = errors[i];
8031                 var f = this.findField(fieldError.id);
8032                 if(f){
8033                     f.markInvalid(fieldError.msg);
8034                 }
8035             }
8036         }else{
8037             var field, id;
8038             for(id in errors){
8039                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8040                     field.markInvalid(errors[id]);
8041                 }
8042             }
8043         }
8044         //Roo.each(this.childForms || [], function (f) {
8045         //    f.markInvalid(errors);
8046         //});
8047
8048         return this;
8049     },
8050
8051     /**
8052      * Set values for fields in this form in bulk.
8053      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8054      * @return {BasicForm} this
8055      */
8056     setValues : function(values){
8057         if(values instanceof Array){ // array of objects
8058             for(var i = 0, len = values.length; i < len; i++){
8059                 var v = values[i];
8060                 var f = this.findField(v.id);
8061                 if(f){
8062                     f.setValue(v.value);
8063                     if(this.trackResetOnLoad){
8064                         f.originalValue = f.getValue();
8065                     }
8066                 }
8067             }
8068         }else{ // object hash
8069             var field, id;
8070             for(id in values){
8071                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8072
8073                     if (field.setFromData &&
8074                         field.valueField &&
8075                         field.displayField &&
8076                         // combos' with local stores can
8077                         // be queried via setValue()
8078                         // to set their value..
8079                         (field.store && !field.store.isLocal)
8080                         ) {
8081                         // it's a combo
8082                         var sd = { };
8083                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8084                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8085                         field.setFromData(sd);
8086
8087                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8088                         
8089                         field.setFromData(values);
8090                         
8091                     } else {
8092                         field.setValue(values[id]);
8093                     }
8094
8095
8096                     if(this.trackResetOnLoad){
8097                         field.originalValue = field.getValue();
8098                     }
8099                 }
8100             }
8101         }
8102
8103         //Roo.each(this.childForms || [], function (f) {
8104         //    f.setValues(values);
8105         //});
8106
8107         return this;
8108     },
8109
8110     /**
8111      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8112      * they are returned as an array.
8113      * @param {Boolean} asString
8114      * @return {Object}
8115      */
8116     getValues : function(asString){
8117         //if (this.childForms) {
8118             // copy values from the child forms
8119         //    Roo.each(this.childForms, function (f) {
8120         //        this.setValues(f.getValues());
8121         //    }, this);
8122         //}
8123
8124
8125
8126         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8127         if(asString === true){
8128             return fs;
8129         }
8130         return Roo.urlDecode(fs);
8131     },
8132
8133     /**
8134      * Returns the fields in this form as an object with key/value pairs.
8135      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8136      * @return {Object}
8137      */
8138     getFieldValues : function(with_hidden)
8139     {
8140         var items = this.getItems();
8141         var ret = {};
8142         items.each(function(f){
8143             
8144             if (!f.getName()) {
8145                 return;
8146             }
8147             
8148             var v = f.getValue();
8149             
8150             if (f.inputType =='radio') {
8151                 if (typeof(ret[f.getName()]) == 'undefined') {
8152                     ret[f.getName()] = ''; // empty..
8153                 }
8154
8155                 if (!f.el.dom.checked) {
8156                     return;
8157
8158                 }
8159                 v = f.el.dom.value;
8160
8161             }
8162             
8163             if(f.xtype == 'MoneyField'){
8164                 ret[f.currencyName] = f.getCurrency();
8165             }
8166
8167             // not sure if this supported any more..
8168             if ((typeof(v) == 'object') && f.getRawValue) {
8169                 v = f.getRawValue() ; // dates..
8170             }
8171             // combo boxes where name != hiddenName...
8172             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8173                 ret[f.name] = f.getRawValue();
8174             }
8175             ret[f.getName()] = v;
8176         });
8177
8178         return ret;
8179     },
8180
8181     /**
8182      * Clears all invalid messages in this form.
8183      * @return {BasicForm} this
8184      */
8185     clearInvalid : function(){
8186         var items = this.getItems();
8187
8188         items.each(function(f){
8189            f.clearInvalid();
8190         });
8191
8192         return this;
8193     },
8194
8195     /**
8196      * Resets this form.
8197      * @return {BasicForm} this
8198      */
8199     reset : function(){
8200         var items = this.getItems();
8201         items.each(function(f){
8202             f.reset();
8203         });
8204
8205         Roo.each(this.childForms || [], function (f) {
8206             f.reset();
8207         });
8208
8209
8210         return this;
8211     },
8212     
8213     getItems : function()
8214     {
8215         var r=new Roo.util.MixedCollection(false, function(o){
8216             return o.id || (o.id = Roo.id());
8217         });
8218         var iter = function(el) {
8219             if (el.inputEl) {
8220                 r.add(el);
8221             }
8222             if (!el.items) {
8223                 return;
8224             }
8225             Roo.each(el.items,function(e) {
8226                 iter(e);
8227             });
8228         };
8229
8230         iter(this);
8231         return r;
8232     },
8233     
8234     hideFields : function(items)
8235     {
8236         Roo.each(items, function(i){
8237             
8238             var f = this.findField(i);
8239             
8240             if(!f){
8241                 return;
8242             }
8243             
8244             if(f.xtype == 'DateField'){
8245                 f.setVisible(false);
8246                 return;
8247             }
8248             
8249             f.hide();
8250             
8251         }, this);
8252     },
8253     
8254     showFields : function(items)
8255     {
8256         Roo.each(items, function(i){
8257             
8258             var f = this.findField(i);
8259             
8260             if(!f){
8261                 return;
8262             }
8263             
8264             if(f.xtype == 'DateField'){
8265                 f.setVisible(true);
8266                 return;
8267             }
8268             
8269             f.show();
8270             
8271         }, this);
8272     }
8273
8274 });
8275
8276 Roo.apply(Roo.bootstrap.Form, {
8277     
8278     popover : {
8279         
8280         padding : 5,
8281         
8282         isApplied : false,
8283         
8284         isMasked : false,
8285         
8286         form : false,
8287         
8288         target : false,
8289         
8290         toolTip : false,
8291         
8292         intervalID : false,
8293         
8294         maskEl : false,
8295         
8296         apply : function()
8297         {
8298             if(this.isApplied){
8299                 return;
8300             }
8301             
8302             this.maskEl = {
8303                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8304                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8305                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8306                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8307             };
8308             
8309             this.maskEl.top.enableDisplayMode("block");
8310             this.maskEl.left.enableDisplayMode("block");
8311             this.maskEl.bottom.enableDisplayMode("block");
8312             this.maskEl.right.enableDisplayMode("block");
8313             
8314             this.toolTip = new Roo.bootstrap.Tooltip({
8315                 cls : 'roo-form-error-popover',
8316                 alignment : {
8317                     'left' : ['r-l', [-2,0], 'right'],
8318                     'right' : ['l-r', [2,0], 'left'],
8319                     'bottom' : ['tl-bl', [0,2], 'top'],
8320                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8321                 }
8322             });
8323             
8324             this.toolTip.render(Roo.get(document.body));
8325
8326             this.toolTip.el.enableDisplayMode("block");
8327             
8328             Roo.get(document.body).on('click', function(){
8329                 this.unmask();
8330             }, this);
8331             
8332             Roo.get(document.body).on('touchstart', function(){
8333                 this.unmask();
8334             }, this);
8335             
8336             this.isApplied = true
8337         },
8338         
8339         mask : function(form, target)
8340         {
8341             this.form = form;
8342             
8343             this.target = target;
8344             
8345             if(!this.form.errorMask || !target.el){
8346                 return;
8347             }
8348             
8349             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8350             
8351             Roo.log(scrollable);
8352             
8353             var ot = this.target.el.calcOffsetsTo(scrollable);
8354             
8355             var scrollTo = ot[1] - this.form.maskOffset;
8356             
8357             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8358             
8359             scrollable.scrollTo('top', scrollTo);
8360             
8361             var box = this.target.el.getBox();
8362             Roo.log(box);
8363             var zIndex = Roo.bootstrap.Modal.zIndex++;
8364
8365             
8366             this.maskEl.top.setStyle('position', 'absolute');
8367             this.maskEl.top.setStyle('z-index', zIndex);
8368             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8369             this.maskEl.top.setLeft(0);
8370             this.maskEl.top.setTop(0);
8371             this.maskEl.top.show();
8372             
8373             this.maskEl.left.setStyle('position', 'absolute');
8374             this.maskEl.left.setStyle('z-index', zIndex);
8375             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8376             this.maskEl.left.setLeft(0);
8377             this.maskEl.left.setTop(box.y - this.padding);
8378             this.maskEl.left.show();
8379
8380             this.maskEl.bottom.setStyle('position', 'absolute');
8381             this.maskEl.bottom.setStyle('z-index', zIndex);
8382             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8383             this.maskEl.bottom.setLeft(0);
8384             this.maskEl.bottom.setTop(box.bottom + this.padding);
8385             this.maskEl.bottom.show();
8386
8387             this.maskEl.right.setStyle('position', 'absolute');
8388             this.maskEl.right.setStyle('z-index', zIndex);
8389             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8390             this.maskEl.right.setLeft(box.right + this.padding);
8391             this.maskEl.right.setTop(box.y - this.padding);
8392             this.maskEl.right.show();
8393
8394             this.toolTip.bindEl = this.target.el;
8395
8396             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8397
8398             var tip = this.target.blankText;
8399
8400             if(this.target.getValue() !== '' ) {
8401                 
8402                 if (this.target.invalidText.length) {
8403                     tip = this.target.invalidText;
8404                 } else if (this.target.regexText.length){
8405                     tip = this.target.regexText;
8406                 }
8407             }
8408
8409             this.toolTip.show(tip);
8410
8411             this.intervalID = window.setInterval(function() {
8412                 Roo.bootstrap.Form.popover.unmask();
8413             }, 10000);
8414
8415             window.onwheel = function(){ return false;};
8416             
8417             (function(){ this.isMasked = true; }).defer(500, this);
8418             
8419         },
8420         
8421         unmask : function()
8422         {
8423             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8424                 return;
8425             }
8426             
8427             this.maskEl.top.setStyle('position', 'absolute');
8428             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8429             this.maskEl.top.hide();
8430
8431             this.maskEl.left.setStyle('position', 'absolute');
8432             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8433             this.maskEl.left.hide();
8434
8435             this.maskEl.bottom.setStyle('position', 'absolute');
8436             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8437             this.maskEl.bottom.hide();
8438
8439             this.maskEl.right.setStyle('position', 'absolute');
8440             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8441             this.maskEl.right.hide();
8442             
8443             this.toolTip.hide();
8444             
8445             this.toolTip.el.hide();
8446             
8447             window.onwheel = function(){ return true;};
8448             
8449             if(this.intervalID){
8450                 window.clearInterval(this.intervalID);
8451                 this.intervalID = false;
8452             }
8453             
8454             this.isMasked = false;
8455             
8456         }
8457         
8458     }
8459     
8460 });
8461
8462 /*
8463  * Based on:
8464  * Ext JS Library 1.1.1
8465  * Copyright(c) 2006-2007, Ext JS, LLC.
8466  *
8467  * Originally Released Under LGPL - original licence link has changed is not relivant.
8468  *
8469  * Fork - LGPL
8470  * <script type="text/javascript">
8471  */
8472 /**
8473  * @class Roo.form.VTypes
8474  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8475  * @singleton
8476  */
8477 Roo.form.VTypes = function(){
8478     // closure these in so they are only created once.
8479     var alpha = /^[a-zA-Z_]+$/;
8480     var alphanum = /^[a-zA-Z0-9_]+$/;
8481     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8482     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8483
8484     // All these messages and functions are configurable
8485     return {
8486         /**
8487          * The function used to validate email addresses
8488          * @param {String} value The email address
8489          */
8490         'email' : function(v){
8491             return email.test(v);
8492         },
8493         /**
8494          * The error text to display when the email validation function returns false
8495          * @type String
8496          */
8497         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8498         /**
8499          * The keystroke filter mask to be applied on email input
8500          * @type RegExp
8501          */
8502         'emailMask' : /[a-z0-9_\.\-@]/i,
8503
8504         /**
8505          * The function used to validate URLs
8506          * @param {String} value The URL
8507          */
8508         'url' : function(v){
8509             return url.test(v);
8510         },
8511         /**
8512          * The error text to display when the url validation function returns false
8513          * @type String
8514          */
8515         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8516         
8517         /**
8518          * The function used to validate alpha values
8519          * @param {String} value The value
8520          */
8521         'alpha' : function(v){
8522             return alpha.test(v);
8523         },
8524         /**
8525          * The error text to display when the alpha validation function returns false
8526          * @type String
8527          */
8528         'alphaText' : 'This field should only contain letters and _',
8529         /**
8530          * The keystroke filter mask to be applied on alpha input
8531          * @type RegExp
8532          */
8533         'alphaMask' : /[a-z_]/i,
8534
8535         /**
8536          * The function used to validate alphanumeric values
8537          * @param {String} value The value
8538          */
8539         'alphanum' : function(v){
8540             return alphanum.test(v);
8541         },
8542         /**
8543          * The error text to display when the alphanumeric validation function returns false
8544          * @type String
8545          */
8546         'alphanumText' : 'This field should only contain letters, numbers and _',
8547         /**
8548          * The keystroke filter mask to be applied on alphanumeric input
8549          * @type RegExp
8550          */
8551         'alphanumMask' : /[a-z0-9_]/i
8552     };
8553 }();/*
8554  * - LGPL
8555  *
8556  * Input
8557  * 
8558  */
8559
8560 /**
8561  * @class Roo.bootstrap.Input
8562  * @extends Roo.bootstrap.Component
8563  * Bootstrap Input class
8564  * @cfg {Boolean} disabled is it disabled
8565  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8566  * @cfg {String} name name of the input
8567  * @cfg {string} fieldLabel - the label associated
8568  * @cfg {string} placeholder - placeholder to put in text.
8569  * @cfg {string}  before - input group add on before
8570  * @cfg {string} after - input group add on after
8571  * @cfg {string} size - (lg|sm) or leave empty..
8572  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8573  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8574  * @cfg {Number} md colspan out of 12 for computer-sized screens
8575  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8576  * @cfg {string} value default value of the input
8577  * @cfg {Number} labelWidth set the width of label 
8578  * @cfg {Number} labellg set the width of label (1-12)
8579  * @cfg {Number} labelmd set the width of label (1-12)
8580  * @cfg {Number} labelsm set the width of label (1-12)
8581  * @cfg {Number} labelxs set the width of label (1-12)
8582  * @cfg {String} labelAlign (top|left)
8583  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8584  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8585  * @cfg {String} indicatorpos (left|right) default left
8586  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8587  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8588
8589  * @cfg {String} align (left|center|right) Default left
8590  * @cfg {Boolean} forceFeedback (true|false) Default false
8591  * 
8592  * @constructor
8593  * Create a new Input
8594  * @param {Object} config The config object
8595  */
8596
8597 Roo.bootstrap.Input = function(config){
8598     
8599     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8600     
8601     this.addEvents({
8602         /**
8603          * @event focus
8604          * Fires when this field receives input focus.
8605          * @param {Roo.form.Field} this
8606          */
8607         focus : true,
8608         /**
8609          * @event blur
8610          * Fires when this field loses input focus.
8611          * @param {Roo.form.Field} this
8612          */
8613         blur : true,
8614         /**
8615          * @event specialkey
8616          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8617          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8618          * @param {Roo.form.Field} this
8619          * @param {Roo.EventObject} e The event object
8620          */
8621         specialkey : true,
8622         /**
8623          * @event change
8624          * Fires just before the field blurs if the field value has changed.
8625          * @param {Roo.form.Field} this
8626          * @param {Mixed} newValue The new value
8627          * @param {Mixed} oldValue The original value
8628          */
8629         change : true,
8630         /**
8631          * @event invalid
8632          * Fires after the field has been marked as invalid.
8633          * @param {Roo.form.Field} this
8634          * @param {String} msg The validation message
8635          */
8636         invalid : true,
8637         /**
8638          * @event valid
8639          * Fires after the field has been validated with no errors.
8640          * @param {Roo.form.Field} this
8641          */
8642         valid : true,
8643          /**
8644          * @event keyup
8645          * Fires after the key up
8646          * @param {Roo.form.Field} this
8647          * @param {Roo.EventObject}  e The event Object
8648          */
8649         keyup : true
8650     });
8651 };
8652
8653 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8654      /**
8655      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8656       automatic validation (defaults to "keyup").
8657      */
8658     validationEvent : "keyup",
8659      /**
8660      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8661      */
8662     validateOnBlur : true,
8663     /**
8664      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8665      */
8666     validationDelay : 250,
8667      /**
8668      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8669      */
8670     focusClass : "x-form-focus",  // not needed???
8671     
8672        
8673     /**
8674      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8675      */
8676     invalidClass : "has-warning",
8677     
8678     /**
8679      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8680      */
8681     validClass : "has-success",
8682     
8683     /**
8684      * @cfg {Boolean} hasFeedback (true|false) default true
8685      */
8686     hasFeedback : true,
8687     
8688     /**
8689      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8690      */
8691     invalidFeedbackClass : "glyphicon-warning-sign",
8692     
8693     /**
8694      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8695      */
8696     validFeedbackClass : "glyphicon-ok",
8697     
8698     /**
8699      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8700      */
8701     selectOnFocus : false,
8702     
8703      /**
8704      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8705      */
8706     maskRe : null,
8707        /**
8708      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8709      */
8710     vtype : null,
8711     
8712       /**
8713      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8714      */
8715     disableKeyFilter : false,
8716     
8717        /**
8718      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8719      */
8720     disabled : false,
8721      /**
8722      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8723      */
8724     allowBlank : true,
8725     /**
8726      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8727      */
8728     blankText : "Please complete this mandatory field",
8729     
8730      /**
8731      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8732      */
8733     minLength : 0,
8734     /**
8735      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8736      */
8737     maxLength : Number.MAX_VALUE,
8738     /**
8739      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8740      */
8741     minLengthText : "The minimum length for this field is {0}",
8742     /**
8743      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8744      */
8745     maxLengthText : "The maximum length for this field is {0}",
8746   
8747     
8748     /**
8749      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8750      * If available, this function will be called only after the basic validators all return true, and will be passed the
8751      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8752      */
8753     validator : null,
8754     /**
8755      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8756      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8757      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8758      */
8759     regex : null,
8760     /**
8761      * @cfg {String} regexText -- Depricated - use Invalid Text
8762      */
8763     regexText : "",
8764     
8765     /**
8766      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8767      */
8768     invalidText : "",
8769     
8770     
8771     
8772     autocomplete: false,
8773     
8774     
8775     fieldLabel : '',
8776     inputType : 'text',
8777     
8778     name : false,
8779     placeholder: false,
8780     before : false,
8781     after : false,
8782     size : false,
8783     hasFocus : false,
8784     preventMark: false,
8785     isFormField : true,
8786     value : '',
8787     labelWidth : 2,
8788     labelAlign : false,
8789     readOnly : false,
8790     align : false,
8791     formatedValue : false,
8792     forceFeedback : false,
8793     
8794     indicatorpos : 'left',
8795     
8796     labellg : 0,
8797     labelmd : 0,
8798     labelsm : 0,
8799     labelxs : 0,
8800     
8801     capture : '',
8802     accept : '',
8803     
8804     parentLabelAlign : function()
8805     {
8806         var parent = this;
8807         while (parent.parent()) {
8808             parent = parent.parent();
8809             if (typeof(parent.labelAlign) !='undefined') {
8810                 return parent.labelAlign;
8811             }
8812         }
8813         return 'left';
8814         
8815     },
8816     
8817     getAutoCreate : function()
8818     {
8819         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8820         
8821         var id = Roo.id();
8822         
8823         var cfg = {};
8824         
8825         if(this.inputType != 'hidden'){
8826             cfg.cls = 'form-group' //input-group
8827         }
8828         
8829         var input =  {
8830             tag: 'input',
8831             id : id,
8832             type : this.inputType,
8833             value : this.value,
8834             cls : 'form-control',
8835             placeholder : this.placeholder || '',
8836             autocomplete : this.autocomplete || 'new-password'
8837         };
8838         
8839         if(this.capture.length){
8840             input.capture = this.capture;
8841         }
8842         
8843         if(this.accept.length){
8844             input.accept = this.accept + "/*";
8845         }
8846         
8847         if(this.align){
8848             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8849         }
8850         
8851         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8852             input.maxLength = this.maxLength;
8853         }
8854         
8855         if (this.disabled) {
8856             input.disabled=true;
8857         }
8858         
8859         if (this.readOnly) {
8860             input.readonly=true;
8861         }
8862         
8863         if (this.name) {
8864             input.name = this.name;
8865         }
8866         
8867         if (this.size) {
8868             input.cls += ' input-' + this.size;
8869         }
8870         
8871         var settings=this;
8872         ['xs','sm','md','lg'].map(function(size){
8873             if (settings[size]) {
8874                 cfg.cls += ' col-' + size + '-' + settings[size];
8875             }
8876         });
8877         
8878         var inputblock = input;
8879         
8880         var feedback = {
8881             tag: 'span',
8882             cls: 'glyphicon form-control-feedback'
8883         };
8884             
8885         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8886             
8887             inputblock = {
8888                 cls : 'has-feedback',
8889                 cn :  [
8890                     input,
8891                     feedback
8892                 ] 
8893             };  
8894         }
8895         
8896         if (this.before || this.after) {
8897             
8898             inputblock = {
8899                 cls : 'input-group',
8900                 cn :  [] 
8901             };
8902             
8903             if (this.before && typeof(this.before) == 'string') {
8904                 
8905                 inputblock.cn.push({
8906                     tag :'span',
8907                     cls : 'roo-input-before input-group-addon',
8908                     html : this.before
8909                 });
8910             }
8911             if (this.before && typeof(this.before) == 'object') {
8912                 this.before = Roo.factory(this.before);
8913                 
8914                 inputblock.cn.push({
8915                     tag :'span',
8916                     cls : 'roo-input-before input-group-' +
8917                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8918                 });
8919             }
8920             
8921             inputblock.cn.push(input);
8922             
8923             if (this.after && typeof(this.after) == 'string') {
8924                 inputblock.cn.push({
8925                     tag :'span',
8926                     cls : 'roo-input-after input-group-addon',
8927                     html : this.after
8928                 });
8929             }
8930             if (this.after && typeof(this.after) == 'object') {
8931                 this.after = Roo.factory(this.after);
8932                 
8933                 inputblock.cn.push({
8934                     tag :'span',
8935                     cls : 'roo-input-after input-group-' +
8936                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8937                 });
8938             }
8939             
8940             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8941                 inputblock.cls += ' has-feedback';
8942                 inputblock.cn.push(feedback);
8943             }
8944         };
8945         
8946         if (align ==='left' && this.fieldLabel.length) {
8947             
8948             cfg.cls += ' roo-form-group-label-left';
8949             
8950             cfg.cn = [
8951                 {
8952                     tag : 'i',
8953                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8954                     tooltip : 'This field is required'
8955                 },
8956                 {
8957                     tag: 'label',
8958                     'for' :  id,
8959                     cls : 'control-label',
8960                     html : this.fieldLabel
8961
8962                 },
8963                 {
8964                     cls : "", 
8965                     cn: [
8966                         inputblock
8967                     ]
8968                 }
8969             ];
8970             
8971             var labelCfg = cfg.cn[1];
8972             var contentCfg = cfg.cn[2];
8973             
8974             if(this.indicatorpos == 'right'){
8975                 cfg.cn = [
8976                     {
8977                         tag: 'label',
8978                         'for' :  id,
8979                         cls : 'control-label',
8980                         cn : [
8981                             {
8982                                 tag : 'span',
8983                                 html : this.fieldLabel
8984                             },
8985                             {
8986                                 tag : 'i',
8987                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8988                                 tooltip : 'This field is required'
8989                             }
8990                         ]
8991                     },
8992                     {
8993                         cls : "",
8994                         cn: [
8995                             inputblock
8996                         ]
8997                     }
8998
8999                 ];
9000                 
9001                 labelCfg = cfg.cn[0];
9002                 contentCfg = cfg.cn[1];
9003             
9004             }
9005             
9006             if(this.labelWidth > 12){
9007                 labelCfg.style = "width: " + this.labelWidth + 'px';
9008             }
9009             
9010             if(this.labelWidth < 13 && this.labelmd == 0){
9011                 this.labelmd = this.labelWidth;
9012             }
9013             
9014             if(this.labellg > 0){
9015                 labelCfg.cls += ' col-lg-' + this.labellg;
9016                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9017             }
9018             
9019             if(this.labelmd > 0){
9020                 labelCfg.cls += ' col-md-' + this.labelmd;
9021                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9022             }
9023             
9024             if(this.labelsm > 0){
9025                 labelCfg.cls += ' col-sm-' + this.labelsm;
9026                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9027             }
9028             
9029             if(this.labelxs > 0){
9030                 labelCfg.cls += ' col-xs-' + this.labelxs;
9031                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9032             }
9033             
9034             
9035         } else if ( this.fieldLabel.length) {
9036                 
9037             cfg.cn = [
9038                 {
9039                     tag : 'i',
9040                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9041                     tooltip : 'This field is required'
9042                 },
9043                 {
9044                     tag: 'label',
9045                    //cls : 'input-group-addon',
9046                     html : this.fieldLabel
9047
9048                 },
9049
9050                inputblock
9051
9052            ];
9053            
9054            if(this.indicatorpos == 'right'){
9055                 
9056                 cfg.cn = [
9057                     {
9058                         tag: 'label',
9059                        //cls : 'input-group-addon',
9060                         html : this.fieldLabel
9061
9062                     },
9063                     {
9064                         tag : 'i',
9065                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9066                         tooltip : 'This field is required'
9067                     },
9068
9069                    inputblock
9070
9071                ];
9072
9073             }
9074
9075         } else {
9076             
9077             cfg.cn = [
9078
9079                     inputblock
9080
9081             ];
9082                 
9083                 
9084         };
9085         
9086         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9087            cfg.cls += ' navbar-form';
9088         }
9089         
9090         if (this.parentType === 'NavGroup') {
9091            cfg.cls += ' navbar-form';
9092            cfg.tag = 'li';
9093         }
9094         
9095         return cfg;
9096         
9097     },
9098     /**
9099      * return the real input element.
9100      */
9101     inputEl: function ()
9102     {
9103         return this.el.select('input.form-control',true).first();
9104     },
9105     
9106     tooltipEl : function()
9107     {
9108         return this.inputEl();
9109     },
9110     
9111     indicatorEl : function()
9112     {
9113         var indicator = this.el.select('i.roo-required-indicator',true).first();
9114         
9115         if(!indicator){
9116             return false;
9117         }
9118         
9119         return indicator;
9120         
9121     },
9122     
9123     setDisabled : function(v)
9124     {
9125         var i  = this.inputEl().dom;
9126         if (!v) {
9127             i.removeAttribute('disabled');
9128             return;
9129             
9130         }
9131         i.setAttribute('disabled','true');
9132     },
9133     initEvents : function()
9134     {
9135           
9136         this.inputEl().on("keydown" , this.fireKey,  this);
9137         this.inputEl().on("focus", this.onFocus,  this);
9138         this.inputEl().on("blur", this.onBlur,  this);
9139         
9140         this.inputEl().relayEvent('keyup', this);
9141         
9142         this.indicator = this.indicatorEl();
9143         
9144         if(this.indicator){
9145             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9146         }
9147  
9148         // reference to original value for reset
9149         this.originalValue = this.getValue();
9150         //Roo.form.TextField.superclass.initEvents.call(this);
9151         if(this.validationEvent == 'keyup'){
9152             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9153             this.inputEl().on('keyup', this.filterValidation, this);
9154         }
9155         else if(this.validationEvent !== false){
9156             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9157         }
9158         
9159         if(this.selectOnFocus){
9160             this.on("focus", this.preFocus, this);
9161             
9162         }
9163         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9164             this.inputEl().on("keypress", this.filterKeys, this);
9165         } else {
9166             this.inputEl().relayEvent('keypress', this);
9167         }
9168        /* if(this.grow){
9169             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9170             this.el.on("click", this.autoSize,  this);
9171         }
9172         */
9173         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9174             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9175         }
9176         
9177         if (typeof(this.before) == 'object') {
9178             this.before.render(this.el.select('.roo-input-before',true).first());
9179         }
9180         if (typeof(this.after) == 'object') {
9181             this.after.render(this.el.select('.roo-input-after',true).first());
9182         }
9183         
9184         this.inputEl().on('change', this.onChange, this);
9185         
9186     },
9187     filterValidation : function(e){
9188         if(!e.isNavKeyPress()){
9189             this.validationTask.delay(this.validationDelay);
9190         }
9191     },
9192      /**
9193      * Validates the field value
9194      * @return {Boolean} True if the value is valid, else false
9195      */
9196     validate : function(){
9197         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9198         if(this.disabled || this.validateValue(this.getRawValue())){
9199             this.markValid();
9200             return true;
9201         }
9202         
9203         this.markInvalid();
9204         return false;
9205     },
9206     
9207     
9208     /**
9209      * Validates a value according to the field's validation rules and marks the field as invalid
9210      * if the validation fails
9211      * @param {Mixed} value The value to validate
9212      * @return {Boolean} True if the value is valid, else false
9213      */
9214     validateValue : function(value)
9215     {
9216         if(this.getVisibilityEl().hasClass('hidden')){
9217             return true;
9218         }
9219         
9220         if(value.length < 1)  { // if it's blank
9221             if(this.allowBlank){
9222                 return true;
9223             }
9224             return false;
9225         }
9226         
9227         if(value.length < this.minLength){
9228             return false;
9229         }
9230         if(value.length > this.maxLength){
9231             return false;
9232         }
9233         if(this.vtype){
9234             var vt = Roo.form.VTypes;
9235             if(!vt[this.vtype](value, this)){
9236                 return false;
9237             }
9238         }
9239         if(typeof this.validator == "function"){
9240             var msg = this.validator(value);
9241             if(msg !== true){
9242                 return false;
9243             }
9244             if (typeof(msg) == 'string') {
9245                 this.invalidText = msg;
9246             }
9247         }
9248         
9249         if(this.regex && !this.regex.test(value)){
9250             return false;
9251         }
9252         
9253         return true;
9254     },
9255     
9256      // private
9257     fireKey : function(e){
9258         //Roo.log('field ' + e.getKey());
9259         if(e.isNavKeyPress()){
9260             this.fireEvent("specialkey", this, e);
9261         }
9262     },
9263     focus : function (selectText){
9264         if(this.rendered){
9265             this.inputEl().focus();
9266             if(selectText === true){
9267                 this.inputEl().dom.select();
9268             }
9269         }
9270         return this;
9271     } ,
9272     
9273     onFocus : function(){
9274         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9275            // this.el.addClass(this.focusClass);
9276         }
9277         if(!this.hasFocus){
9278             this.hasFocus = true;
9279             this.startValue = this.getValue();
9280             this.fireEvent("focus", this);
9281         }
9282     },
9283     
9284     beforeBlur : Roo.emptyFn,
9285
9286     
9287     // private
9288     onBlur : function(){
9289         this.beforeBlur();
9290         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9291             //this.el.removeClass(this.focusClass);
9292         }
9293         this.hasFocus = false;
9294         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9295             this.validate();
9296         }
9297         var v = this.getValue();
9298         if(String(v) !== String(this.startValue)){
9299             this.fireEvent('change', this, v, this.startValue);
9300         }
9301         this.fireEvent("blur", this);
9302     },
9303     
9304     onChange : function(e)
9305     {
9306         var v = this.getValue();
9307         if(String(v) !== String(this.startValue)){
9308             this.fireEvent('change', this, v, this.startValue);
9309         }
9310         
9311     },
9312     
9313     /**
9314      * Resets the current field value to the originally loaded value and clears any validation messages
9315      */
9316     reset : function(){
9317         this.setValue(this.originalValue);
9318         this.validate();
9319     },
9320      /**
9321      * Returns the name of the field
9322      * @return {Mixed} name The name field
9323      */
9324     getName: function(){
9325         return this.name;
9326     },
9327      /**
9328      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9329      * @return {Mixed} value The field value
9330      */
9331     getValue : function(){
9332         
9333         var v = this.inputEl().getValue();
9334         
9335         return v;
9336     },
9337     /**
9338      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9339      * @return {Mixed} value The field value
9340      */
9341     getRawValue : function(){
9342         var v = this.inputEl().getValue();
9343         
9344         return v;
9345     },
9346     
9347     /**
9348      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9349      * @param {Mixed} value The value to set
9350      */
9351     setRawValue : function(v){
9352         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9353     },
9354     
9355     selectText : function(start, end){
9356         var v = this.getRawValue();
9357         if(v.length > 0){
9358             start = start === undefined ? 0 : start;
9359             end = end === undefined ? v.length : end;
9360             var d = this.inputEl().dom;
9361             if(d.setSelectionRange){
9362                 d.setSelectionRange(start, end);
9363             }else if(d.createTextRange){
9364                 var range = d.createTextRange();
9365                 range.moveStart("character", start);
9366                 range.moveEnd("character", v.length-end);
9367                 range.select();
9368             }
9369         }
9370     },
9371     
9372     /**
9373      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9374      * @param {Mixed} value The value to set
9375      */
9376     setValue : function(v){
9377         this.value = v;
9378         if(this.rendered){
9379             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9380             this.validate();
9381         }
9382     },
9383     
9384     /*
9385     processValue : function(value){
9386         if(this.stripCharsRe){
9387             var newValue = value.replace(this.stripCharsRe, '');
9388             if(newValue !== value){
9389                 this.setRawValue(newValue);
9390                 return newValue;
9391             }
9392         }
9393         return value;
9394     },
9395   */
9396     preFocus : function(){
9397         
9398         if(this.selectOnFocus){
9399             this.inputEl().dom.select();
9400         }
9401     },
9402     filterKeys : function(e){
9403         var k = e.getKey();
9404         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9405             return;
9406         }
9407         var c = e.getCharCode(), cc = String.fromCharCode(c);
9408         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9409             return;
9410         }
9411         if(!this.maskRe.test(cc)){
9412             e.stopEvent();
9413         }
9414     },
9415      /**
9416      * Clear any invalid styles/messages for this field
9417      */
9418     clearInvalid : function(){
9419         
9420         if(!this.el || this.preventMark){ // not rendered
9421             return;
9422         }
9423         
9424      
9425         this.el.removeClass(this.invalidClass);
9426         
9427         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9428             
9429             var feedback = this.el.select('.form-control-feedback', true).first();
9430             
9431             if(feedback){
9432                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9433             }
9434             
9435         }
9436         
9437         if(this.indicator){
9438             this.indicator.removeClass('visible');
9439             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9440         }
9441         
9442         this.fireEvent('valid', this);
9443     },
9444     
9445      /**
9446      * Mark this field as valid
9447      */
9448     markValid : function()
9449     {
9450         if(!this.el  || this.preventMark){ // not rendered...
9451             return;
9452         }
9453         
9454         this.el.removeClass([this.invalidClass, this.validClass]);
9455         
9456         var feedback = this.el.select('.form-control-feedback', true).first();
9457             
9458         if(feedback){
9459             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9460         }
9461         
9462         if(this.indicator){
9463             this.indicator.removeClass('visible');
9464             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9465         }
9466         
9467         if(this.disabled){
9468             return;
9469         }
9470         
9471         if(this.allowBlank && !this.getRawValue().length){
9472             return;
9473         }
9474         
9475         this.el.addClass(this.validClass);
9476         
9477         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9478             
9479             var feedback = this.el.select('.form-control-feedback', true).first();
9480             
9481             if(feedback){
9482                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9483                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9484             }
9485             
9486         }
9487         
9488         this.fireEvent('valid', this);
9489     },
9490     
9491      /**
9492      * Mark this field as invalid
9493      * @param {String} msg The validation message
9494      */
9495     markInvalid : function(msg)
9496     {
9497         if(!this.el  || this.preventMark){ // not rendered
9498             return;
9499         }
9500         
9501         this.el.removeClass([this.invalidClass, this.validClass]);
9502         
9503         var feedback = this.el.select('.form-control-feedback', true).first();
9504             
9505         if(feedback){
9506             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9507         }
9508
9509         if(this.disabled){
9510             return;
9511         }
9512         
9513         if(this.allowBlank && !this.getRawValue().length){
9514             return;
9515         }
9516         
9517         if(this.indicator){
9518             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9519             this.indicator.addClass('visible');
9520         }
9521         
9522         this.el.addClass(this.invalidClass);
9523         
9524         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9525             
9526             var feedback = this.el.select('.form-control-feedback', true).first();
9527             
9528             if(feedback){
9529                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9530                 
9531                 if(this.getValue().length || this.forceFeedback){
9532                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9533                 }
9534                 
9535             }
9536             
9537         }
9538         
9539         this.fireEvent('invalid', this, msg);
9540     },
9541     // private
9542     SafariOnKeyDown : function(event)
9543     {
9544         // this is a workaround for a password hang bug on chrome/ webkit.
9545         if (this.inputEl().dom.type != 'password') {
9546             return;
9547         }
9548         
9549         var isSelectAll = false;
9550         
9551         if(this.inputEl().dom.selectionEnd > 0){
9552             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9553         }
9554         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9555             event.preventDefault();
9556             this.setValue('');
9557             return;
9558         }
9559         
9560         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9561             
9562             event.preventDefault();
9563             // this is very hacky as keydown always get's upper case.
9564             //
9565             var cc = String.fromCharCode(event.getCharCode());
9566             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9567             
9568         }
9569     },
9570     adjustWidth : function(tag, w){
9571         tag = tag.toLowerCase();
9572         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9573             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9574                 if(tag == 'input'){
9575                     return w + 2;
9576                 }
9577                 if(tag == 'textarea'){
9578                     return w-2;
9579                 }
9580             }else if(Roo.isOpera){
9581                 if(tag == 'input'){
9582                     return w + 2;
9583                 }
9584                 if(tag == 'textarea'){
9585                     return w-2;
9586                 }
9587             }
9588         }
9589         return w;
9590     },
9591     
9592     setFieldLabel : function(v)
9593     {
9594         if(!this.rendered){
9595             return;
9596         }
9597         
9598         if(this.indicator){
9599             var ar = this.el.select('label > span',true);
9600             
9601             if (ar.elements.length) {
9602                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9603                 this.fieldLabel = v;
9604                 return;
9605             }
9606             
9607             var br = this.el.select('label',true);
9608             
9609             if(br.elements.length) {
9610                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9611                 this.fieldLabel = v;
9612                 return;
9613             }
9614             
9615             Roo.log('Cannot Found any of label > span || label in input');
9616             return;
9617         }
9618         
9619         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9620         this.fieldLabel = v;
9621         
9622         
9623     }
9624 });
9625
9626  
9627 /*
9628  * - LGPL
9629  *
9630  * Input
9631  * 
9632  */
9633
9634 /**
9635  * @class Roo.bootstrap.TextArea
9636  * @extends Roo.bootstrap.Input
9637  * Bootstrap TextArea class
9638  * @cfg {Number} cols Specifies the visible width of a text area
9639  * @cfg {Number} rows Specifies the visible number of lines in a text area
9640  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9641  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9642  * @cfg {string} html text
9643  * 
9644  * @constructor
9645  * Create a new TextArea
9646  * @param {Object} config The config object
9647  */
9648
9649 Roo.bootstrap.TextArea = function(config){
9650     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9651    
9652 };
9653
9654 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9655      
9656     cols : false,
9657     rows : 5,
9658     readOnly : false,
9659     warp : 'soft',
9660     resize : false,
9661     value: false,
9662     html: false,
9663     
9664     getAutoCreate : function(){
9665         
9666         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9667         
9668         var id = Roo.id();
9669         
9670         var cfg = {};
9671         
9672         if(this.inputType != 'hidden'){
9673             cfg.cls = 'form-group' //input-group
9674         }
9675         
9676         var input =  {
9677             tag: 'textarea',
9678             id : id,
9679             warp : this.warp,
9680             rows : this.rows,
9681             value : this.value || '',
9682             html: this.html || '',
9683             cls : 'form-control',
9684             placeholder : this.placeholder || '' 
9685             
9686         };
9687         
9688         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9689             input.maxLength = this.maxLength;
9690         }
9691         
9692         if(this.resize){
9693             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9694         }
9695         
9696         if(this.cols){
9697             input.cols = this.cols;
9698         }
9699         
9700         if (this.readOnly) {
9701             input.readonly = true;
9702         }
9703         
9704         if (this.name) {
9705             input.name = this.name;
9706         }
9707         
9708         if (this.size) {
9709             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9710         }
9711         
9712         var settings=this;
9713         ['xs','sm','md','lg'].map(function(size){
9714             if (settings[size]) {
9715                 cfg.cls += ' col-' + size + '-' + settings[size];
9716             }
9717         });
9718         
9719         var inputblock = input;
9720         
9721         if(this.hasFeedback && !this.allowBlank){
9722             
9723             var feedback = {
9724                 tag: 'span',
9725                 cls: 'glyphicon form-control-feedback'
9726             };
9727
9728             inputblock = {
9729                 cls : 'has-feedback',
9730                 cn :  [
9731                     input,
9732                     feedback
9733                 ] 
9734             };  
9735         }
9736         
9737         
9738         if (this.before || this.after) {
9739             
9740             inputblock = {
9741                 cls : 'input-group',
9742                 cn :  [] 
9743             };
9744             if (this.before) {
9745                 inputblock.cn.push({
9746                     tag :'span',
9747                     cls : 'input-group-addon',
9748                     html : this.before
9749                 });
9750             }
9751             
9752             inputblock.cn.push(input);
9753             
9754             if(this.hasFeedback && !this.allowBlank){
9755                 inputblock.cls += ' has-feedback';
9756                 inputblock.cn.push(feedback);
9757             }
9758             
9759             if (this.after) {
9760                 inputblock.cn.push({
9761                     tag :'span',
9762                     cls : 'input-group-addon',
9763                     html : this.after
9764                 });
9765             }
9766             
9767         }
9768         
9769         if (align ==='left' && this.fieldLabel.length) {
9770             cfg.cn = [
9771                 {
9772                     tag: 'label',
9773                     'for' :  id,
9774                     cls : 'control-label',
9775                     html : this.fieldLabel
9776                 },
9777                 {
9778                     cls : "",
9779                     cn: [
9780                         inputblock
9781                     ]
9782                 }
9783
9784             ];
9785             
9786             if(this.labelWidth > 12){
9787                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9788             }
9789
9790             if(this.labelWidth < 13 && this.labelmd == 0){
9791                 this.labelmd = this.labelWidth;
9792             }
9793
9794             if(this.labellg > 0){
9795                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9796                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9797             }
9798
9799             if(this.labelmd > 0){
9800                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9801                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9802             }
9803
9804             if(this.labelsm > 0){
9805                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9806                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9807             }
9808
9809             if(this.labelxs > 0){
9810                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9811                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9812             }
9813             
9814         } else if ( this.fieldLabel.length) {
9815             cfg.cn = [
9816
9817                {
9818                    tag: 'label',
9819                    //cls : 'input-group-addon',
9820                    html : this.fieldLabel
9821
9822                },
9823
9824                inputblock
9825
9826            ];
9827
9828         } else {
9829
9830             cfg.cn = [
9831
9832                 inputblock
9833
9834             ];
9835                 
9836         }
9837         
9838         if (this.disabled) {
9839             input.disabled=true;
9840         }
9841         
9842         return cfg;
9843         
9844     },
9845     /**
9846      * return the real textarea element.
9847      */
9848     inputEl: function ()
9849     {
9850         return this.el.select('textarea.form-control',true).first();
9851     },
9852     
9853     /**
9854      * Clear any invalid styles/messages for this field
9855      */
9856     clearInvalid : function()
9857     {
9858         
9859         if(!this.el || this.preventMark){ // not rendered
9860             return;
9861         }
9862         
9863         var label = this.el.select('label', true).first();
9864         var icon = this.el.select('i.fa-star', true).first();
9865         
9866         if(label && icon){
9867             icon.remove();
9868         }
9869         
9870         this.el.removeClass(this.invalidClass);
9871         
9872         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9873             
9874             var feedback = this.el.select('.form-control-feedback', true).first();
9875             
9876             if(feedback){
9877                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9878             }
9879             
9880         }
9881         
9882         this.fireEvent('valid', this);
9883     },
9884     
9885      /**
9886      * Mark this field as valid
9887      */
9888     markValid : function()
9889     {
9890         if(!this.el  || this.preventMark){ // not rendered
9891             return;
9892         }
9893         
9894         this.el.removeClass([this.invalidClass, this.validClass]);
9895         
9896         var feedback = this.el.select('.form-control-feedback', true).first();
9897             
9898         if(feedback){
9899             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9900         }
9901
9902         if(this.disabled || this.allowBlank){
9903             return;
9904         }
9905         
9906         var label = this.el.select('label', true).first();
9907         var icon = this.el.select('i.fa-star', true).first();
9908         
9909         if(label && icon){
9910             icon.remove();
9911         }
9912         
9913         this.el.addClass(this.validClass);
9914         
9915         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9916             
9917             var feedback = this.el.select('.form-control-feedback', true).first();
9918             
9919             if(feedback){
9920                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9921                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9922             }
9923             
9924         }
9925         
9926         this.fireEvent('valid', this);
9927     },
9928     
9929      /**
9930      * Mark this field as invalid
9931      * @param {String} msg The validation message
9932      */
9933     markInvalid : function(msg)
9934     {
9935         if(!this.el  || this.preventMark){ // not rendered
9936             return;
9937         }
9938         
9939         this.el.removeClass([this.invalidClass, this.validClass]);
9940         
9941         var feedback = this.el.select('.form-control-feedback', true).first();
9942             
9943         if(feedback){
9944             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9945         }
9946
9947         if(this.disabled || this.allowBlank){
9948             return;
9949         }
9950         
9951         var label = this.el.select('label', true).first();
9952         var icon = this.el.select('i.fa-star', true).first();
9953         
9954         if(!this.getValue().length && label && !icon){
9955             this.el.createChild({
9956                 tag : 'i',
9957                 cls : 'text-danger fa fa-lg fa-star',
9958                 tooltip : 'This field is required',
9959                 style : 'margin-right:5px;'
9960             }, label, true);
9961         }
9962
9963         this.el.addClass(this.invalidClass);
9964         
9965         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9966             
9967             var feedback = this.el.select('.form-control-feedback', true).first();
9968             
9969             if(feedback){
9970                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9971                 
9972                 if(this.getValue().length || this.forceFeedback){
9973                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9974                 }
9975                 
9976             }
9977             
9978         }
9979         
9980         this.fireEvent('invalid', this, msg);
9981     }
9982 });
9983
9984  
9985 /*
9986  * - LGPL
9987  *
9988  * trigger field - base class for combo..
9989  * 
9990  */
9991  
9992 /**
9993  * @class Roo.bootstrap.TriggerField
9994  * @extends Roo.bootstrap.Input
9995  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9996  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9997  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9998  * for which you can provide a custom implementation.  For example:
9999  * <pre><code>
10000 var trigger = new Roo.bootstrap.TriggerField();
10001 trigger.onTriggerClick = myTriggerFn;
10002 trigger.applyTo('my-field');
10003 </code></pre>
10004  *
10005  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10006  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10007  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10008  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10009  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10010
10011  * @constructor
10012  * Create a new TriggerField.
10013  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10014  * to the base TextField)
10015  */
10016 Roo.bootstrap.TriggerField = function(config){
10017     this.mimicing = false;
10018     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10019 };
10020
10021 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10022     /**
10023      * @cfg {String} triggerClass A CSS class to apply to the trigger
10024      */
10025      /**
10026      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10027      */
10028     hideTrigger:false,
10029
10030     /**
10031      * @cfg {Boolean} removable (true|false) special filter default false
10032      */
10033     removable : false,
10034     
10035     /** @cfg {Boolean} grow @hide */
10036     /** @cfg {Number} growMin @hide */
10037     /** @cfg {Number} growMax @hide */
10038
10039     /**
10040      * @hide 
10041      * @method
10042      */
10043     autoSize: Roo.emptyFn,
10044     // private
10045     monitorTab : true,
10046     // private
10047     deferHeight : true,
10048
10049     
10050     actionMode : 'wrap',
10051     
10052     caret : false,
10053     
10054     
10055     getAutoCreate : function(){
10056        
10057         var align = this.labelAlign || this.parentLabelAlign();
10058         
10059         var id = Roo.id();
10060         
10061         var cfg = {
10062             cls: 'form-group' //input-group
10063         };
10064         
10065         
10066         var input =  {
10067             tag: 'input',
10068             id : id,
10069             type : this.inputType,
10070             cls : 'form-control',
10071             autocomplete: 'new-password',
10072             placeholder : this.placeholder || '' 
10073             
10074         };
10075         if (this.name) {
10076             input.name = this.name;
10077         }
10078         if (this.size) {
10079             input.cls += ' input-' + this.size;
10080         }
10081         
10082         if (this.disabled) {
10083             input.disabled=true;
10084         }
10085         
10086         var inputblock = input;
10087         
10088         if(this.hasFeedback && !this.allowBlank){
10089             
10090             var feedback = {
10091                 tag: 'span',
10092                 cls: 'glyphicon form-control-feedback'
10093             };
10094             
10095             if(this.removable && !this.editable && !this.tickable){
10096                 inputblock = {
10097                     cls : 'has-feedback',
10098                     cn :  [
10099                         inputblock,
10100                         {
10101                             tag: 'button',
10102                             html : 'x',
10103                             cls : 'roo-combo-removable-btn close'
10104                         },
10105                         feedback
10106                     ] 
10107                 };
10108             } else {
10109                 inputblock = {
10110                     cls : 'has-feedback',
10111                     cn :  [
10112                         inputblock,
10113                         feedback
10114                     ] 
10115                 };
10116             }
10117
10118         } else {
10119             if(this.removable && !this.editable && !this.tickable){
10120                 inputblock = {
10121                     cls : 'roo-removable',
10122                     cn :  [
10123                         inputblock,
10124                         {
10125                             tag: 'button',
10126                             html : 'x',
10127                             cls : 'roo-combo-removable-btn close'
10128                         }
10129                     ] 
10130                 };
10131             }
10132         }
10133         
10134         if (this.before || this.after) {
10135             
10136             inputblock = {
10137                 cls : 'input-group',
10138                 cn :  [] 
10139             };
10140             if (this.before) {
10141                 inputblock.cn.push({
10142                     tag :'span',
10143                     cls : 'input-group-addon',
10144                     html : this.before
10145                 });
10146             }
10147             
10148             inputblock.cn.push(input);
10149             
10150             if(this.hasFeedback && !this.allowBlank){
10151                 inputblock.cls += ' has-feedback';
10152                 inputblock.cn.push(feedback);
10153             }
10154             
10155             if (this.after) {
10156                 inputblock.cn.push({
10157                     tag :'span',
10158                     cls : 'input-group-addon',
10159                     html : this.after
10160                 });
10161             }
10162             
10163         };
10164         
10165         var box = {
10166             tag: 'div',
10167             cn: [
10168                 {
10169                     tag: 'input',
10170                     type : 'hidden',
10171                     cls: 'form-hidden-field'
10172                 },
10173                 inputblock
10174             ]
10175             
10176         };
10177         
10178         if(this.multiple){
10179             box = {
10180                 tag: 'div',
10181                 cn: [
10182                     {
10183                         tag: 'input',
10184                         type : 'hidden',
10185                         cls: 'form-hidden-field'
10186                     },
10187                     {
10188                         tag: 'ul',
10189                         cls: 'roo-select2-choices',
10190                         cn:[
10191                             {
10192                                 tag: 'li',
10193                                 cls: 'roo-select2-search-field',
10194                                 cn: [
10195
10196                                     inputblock
10197                                 ]
10198                             }
10199                         ]
10200                     }
10201                 ]
10202             }
10203         };
10204         
10205         var combobox = {
10206             cls: 'roo-select2-container input-group',
10207             cn: [
10208                 box
10209 //                {
10210 //                    tag: 'ul',
10211 //                    cls: 'typeahead typeahead-long dropdown-menu',
10212 //                    style: 'display:none'
10213 //                }
10214             ]
10215         };
10216         
10217         if(!this.multiple && this.showToggleBtn){
10218             
10219             var caret = {
10220                         tag: 'span',
10221                         cls: 'caret'
10222              };
10223             if (this.caret != false) {
10224                 caret = {
10225                      tag: 'i',
10226                      cls: 'fa fa-' + this.caret
10227                 };
10228                 
10229             }
10230             
10231             combobox.cn.push({
10232                 tag :'span',
10233                 cls : 'input-group-addon btn dropdown-toggle',
10234                 cn : [
10235                     caret,
10236                     {
10237                         tag: 'span',
10238                         cls: 'combobox-clear',
10239                         cn  : [
10240                             {
10241                                 tag : 'i',
10242                                 cls: 'icon-remove'
10243                             }
10244                         ]
10245                     }
10246                 ]
10247
10248             })
10249         }
10250         
10251         if(this.multiple){
10252             combobox.cls += ' roo-select2-container-multi';
10253         }
10254         
10255         if (align ==='left' && this.fieldLabel.length) {
10256             
10257             cfg.cls += ' roo-form-group-label-left';
10258
10259             cfg.cn = [
10260                 {
10261                     tag : 'i',
10262                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10263                     tooltip : 'This field is required'
10264                 },
10265                 {
10266                     tag: 'label',
10267                     'for' :  id,
10268                     cls : 'control-label',
10269                     html : this.fieldLabel
10270
10271                 },
10272                 {
10273                     cls : "", 
10274                     cn: [
10275                         combobox
10276                     ]
10277                 }
10278
10279             ];
10280             
10281             var labelCfg = cfg.cn[1];
10282             var contentCfg = cfg.cn[2];
10283             
10284             if(this.indicatorpos == 'right'){
10285                 cfg.cn = [
10286                     {
10287                         tag: 'label',
10288                         'for' :  id,
10289                         cls : 'control-label',
10290                         cn : [
10291                             {
10292                                 tag : 'span',
10293                                 html : this.fieldLabel
10294                             },
10295                             {
10296                                 tag : 'i',
10297                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10298                                 tooltip : 'This field is required'
10299                             }
10300                         ]
10301                     },
10302                     {
10303                         cls : "", 
10304                         cn: [
10305                             combobox
10306                         ]
10307                     }
10308
10309                 ];
10310                 
10311                 labelCfg = cfg.cn[0];
10312                 contentCfg = cfg.cn[1];
10313             }
10314             
10315             if(this.labelWidth > 12){
10316                 labelCfg.style = "width: " + this.labelWidth + 'px';
10317             }
10318             
10319             if(this.labelWidth < 13 && this.labelmd == 0){
10320                 this.labelmd = this.labelWidth;
10321             }
10322             
10323             if(this.labellg > 0){
10324                 labelCfg.cls += ' col-lg-' + this.labellg;
10325                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10326             }
10327             
10328             if(this.labelmd > 0){
10329                 labelCfg.cls += ' col-md-' + this.labelmd;
10330                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10331             }
10332             
10333             if(this.labelsm > 0){
10334                 labelCfg.cls += ' col-sm-' + this.labelsm;
10335                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10336             }
10337             
10338             if(this.labelxs > 0){
10339                 labelCfg.cls += ' col-xs-' + this.labelxs;
10340                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10341             }
10342             
10343         } else if ( this.fieldLabel.length) {
10344 //                Roo.log(" label");
10345             cfg.cn = [
10346                 {
10347                    tag : 'i',
10348                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10349                    tooltip : 'This field is required'
10350                },
10351                {
10352                    tag: 'label',
10353                    //cls : 'input-group-addon',
10354                    html : this.fieldLabel
10355
10356                },
10357
10358                combobox
10359
10360             ];
10361             
10362             if(this.indicatorpos == 'right'){
10363                 
10364                 cfg.cn = [
10365                     {
10366                        tag: 'label',
10367                        cn : [
10368                            {
10369                                tag : 'span',
10370                                html : this.fieldLabel
10371                            },
10372                            {
10373                               tag : 'i',
10374                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10375                               tooltip : 'This field is required'
10376                            }
10377                        ]
10378
10379                     },
10380                     combobox
10381
10382                 ];
10383
10384             }
10385
10386         } else {
10387             
10388 //                Roo.log(" no label && no align");
10389                 cfg = combobox
10390                      
10391                 
10392         }
10393         
10394         var settings=this;
10395         ['xs','sm','md','lg'].map(function(size){
10396             if (settings[size]) {
10397                 cfg.cls += ' col-' + size + '-' + settings[size];
10398             }
10399         });
10400         
10401         return cfg;
10402         
10403     },
10404     
10405     
10406     
10407     // private
10408     onResize : function(w, h){
10409 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10410 //        if(typeof w == 'number'){
10411 //            var x = w - this.trigger.getWidth();
10412 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10413 //            this.trigger.setStyle('left', x+'px');
10414 //        }
10415     },
10416
10417     // private
10418     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10419
10420     // private
10421     getResizeEl : function(){
10422         return this.inputEl();
10423     },
10424
10425     // private
10426     getPositionEl : function(){
10427         return this.inputEl();
10428     },
10429
10430     // private
10431     alignErrorIcon : function(){
10432         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10433     },
10434
10435     // private
10436     initEvents : function(){
10437         
10438         this.createList();
10439         
10440         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10441         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10442         if(!this.multiple && this.showToggleBtn){
10443             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10444             if(this.hideTrigger){
10445                 this.trigger.setDisplayed(false);
10446             }
10447             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10448         }
10449         
10450         if(this.multiple){
10451             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10452         }
10453         
10454         if(this.removable && !this.editable && !this.tickable){
10455             var close = this.closeTriggerEl();
10456             
10457             if(close){
10458                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10459                 close.on('click', this.removeBtnClick, this, close);
10460             }
10461         }
10462         
10463         //this.trigger.addClassOnOver('x-form-trigger-over');
10464         //this.trigger.addClassOnClick('x-form-trigger-click');
10465         
10466         //if(!this.width){
10467         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10468         //}
10469     },
10470     
10471     closeTriggerEl : function()
10472     {
10473         var close = this.el.select('.roo-combo-removable-btn', true).first();
10474         return close ? close : false;
10475     },
10476     
10477     removeBtnClick : function(e, h, el)
10478     {
10479         e.preventDefault();
10480         
10481         if(this.fireEvent("remove", this) !== false){
10482             this.reset();
10483             this.fireEvent("afterremove", this)
10484         }
10485     },
10486     
10487     createList : function()
10488     {
10489         this.list = Roo.get(document.body).createChild({
10490             tag: 'ul',
10491             cls: 'typeahead typeahead-long dropdown-menu',
10492             style: 'display:none'
10493         });
10494         
10495         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10496         
10497     },
10498
10499     // private
10500     initTrigger : function(){
10501        
10502     },
10503
10504     // private
10505     onDestroy : function(){
10506         if(this.trigger){
10507             this.trigger.removeAllListeners();
10508           //  this.trigger.remove();
10509         }
10510         //if(this.wrap){
10511         //    this.wrap.remove();
10512         //}
10513         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10514     },
10515
10516     // private
10517     onFocus : function(){
10518         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10519         /*
10520         if(!this.mimicing){
10521             this.wrap.addClass('x-trigger-wrap-focus');
10522             this.mimicing = true;
10523             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10524             if(this.monitorTab){
10525                 this.el.on("keydown", this.checkTab, this);
10526             }
10527         }
10528         */
10529     },
10530
10531     // private
10532     checkTab : function(e){
10533         if(e.getKey() == e.TAB){
10534             this.triggerBlur();
10535         }
10536     },
10537
10538     // private
10539     onBlur : function(){
10540         // do nothing
10541     },
10542
10543     // private
10544     mimicBlur : function(e, t){
10545         /*
10546         if(!this.wrap.contains(t) && this.validateBlur()){
10547             this.triggerBlur();
10548         }
10549         */
10550     },
10551
10552     // private
10553     triggerBlur : function(){
10554         this.mimicing = false;
10555         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10556         if(this.monitorTab){
10557             this.el.un("keydown", this.checkTab, this);
10558         }
10559         //this.wrap.removeClass('x-trigger-wrap-focus');
10560         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10561     },
10562
10563     // private
10564     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10565     validateBlur : function(e, t){
10566         return true;
10567     },
10568
10569     // private
10570     onDisable : function(){
10571         this.inputEl().dom.disabled = true;
10572         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10573         //if(this.wrap){
10574         //    this.wrap.addClass('x-item-disabled');
10575         //}
10576     },
10577
10578     // private
10579     onEnable : function(){
10580         this.inputEl().dom.disabled = false;
10581         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10582         //if(this.wrap){
10583         //    this.el.removeClass('x-item-disabled');
10584         //}
10585     },
10586
10587     // private
10588     onShow : function(){
10589         var ae = this.getActionEl();
10590         
10591         if(ae){
10592             ae.dom.style.display = '';
10593             ae.dom.style.visibility = 'visible';
10594         }
10595     },
10596
10597     // private
10598     
10599     onHide : function(){
10600         var ae = this.getActionEl();
10601         ae.dom.style.display = 'none';
10602     },
10603
10604     /**
10605      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10606      * by an implementing function.
10607      * @method
10608      * @param {EventObject} e
10609      */
10610     onTriggerClick : Roo.emptyFn
10611 });
10612  /*
10613  * Based on:
10614  * Ext JS Library 1.1.1
10615  * Copyright(c) 2006-2007, Ext JS, LLC.
10616  *
10617  * Originally Released Under LGPL - original licence link has changed is not relivant.
10618  *
10619  * Fork - LGPL
10620  * <script type="text/javascript">
10621  */
10622
10623
10624 /**
10625  * @class Roo.data.SortTypes
10626  * @singleton
10627  * Defines the default sorting (casting?) comparison functions used when sorting data.
10628  */
10629 Roo.data.SortTypes = {
10630     /**
10631      * Default sort that does nothing
10632      * @param {Mixed} s The value being converted
10633      * @return {Mixed} The comparison value
10634      */
10635     none : function(s){
10636         return s;
10637     },
10638     
10639     /**
10640      * The regular expression used to strip tags
10641      * @type {RegExp}
10642      * @property
10643      */
10644     stripTagsRE : /<\/?[^>]+>/gi,
10645     
10646     /**
10647      * Strips all HTML tags to sort on text only
10648      * @param {Mixed} s The value being converted
10649      * @return {String} The comparison value
10650      */
10651     asText : function(s){
10652         return String(s).replace(this.stripTagsRE, "");
10653     },
10654     
10655     /**
10656      * Strips all HTML tags to sort on text only - Case insensitive
10657      * @param {Mixed} s The value being converted
10658      * @return {String} The comparison value
10659      */
10660     asUCText : function(s){
10661         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10662     },
10663     
10664     /**
10665      * Case insensitive string
10666      * @param {Mixed} s The value being converted
10667      * @return {String} The comparison value
10668      */
10669     asUCString : function(s) {
10670         return String(s).toUpperCase();
10671     },
10672     
10673     /**
10674      * Date sorting
10675      * @param {Mixed} s The value being converted
10676      * @return {Number} The comparison value
10677      */
10678     asDate : function(s) {
10679         if(!s){
10680             return 0;
10681         }
10682         if(s instanceof Date){
10683             return s.getTime();
10684         }
10685         return Date.parse(String(s));
10686     },
10687     
10688     /**
10689      * Float sorting
10690      * @param {Mixed} s The value being converted
10691      * @return {Float} The comparison value
10692      */
10693     asFloat : function(s) {
10694         var val = parseFloat(String(s).replace(/,/g, ""));
10695         if(isNaN(val)) {
10696             val = 0;
10697         }
10698         return val;
10699     },
10700     
10701     /**
10702      * Integer sorting
10703      * @param {Mixed} s The value being converted
10704      * @return {Number} The comparison value
10705      */
10706     asInt : function(s) {
10707         var val = parseInt(String(s).replace(/,/g, ""));
10708         if(isNaN(val)) {
10709             val = 0;
10710         }
10711         return val;
10712     }
10713 };/*
10714  * Based on:
10715  * Ext JS Library 1.1.1
10716  * Copyright(c) 2006-2007, Ext JS, LLC.
10717  *
10718  * Originally Released Under LGPL - original licence link has changed is not relivant.
10719  *
10720  * Fork - LGPL
10721  * <script type="text/javascript">
10722  */
10723
10724 /**
10725 * @class Roo.data.Record
10726  * Instances of this class encapsulate both record <em>definition</em> information, and record
10727  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10728  * to access Records cached in an {@link Roo.data.Store} object.<br>
10729  * <p>
10730  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10731  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10732  * objects.<br>
10733  * <p>
10734  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10735  * @constructor
10736  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10737  * {@link #create}. The parameters are the same.
10738  * @param {Array} data An associative Array of data values keyed by the field name.
10739  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10740  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10741  * not specified an integer id is generated.
10742  */
10743 Roo.data.Record = function(data, id){
10744     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10745     this.data = data;
10746 };
10747
10748 /**
10749  * Generate a constructor for a specific record layout.
10750  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10751  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10752  * Each field definition object may contain the following properties: <ul>
10753  * <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,
10754  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10755  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10756  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10757  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10758  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10759  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10760  * this may be omitted.</p></li>
10761  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10762  * <ul><li>auto (Default, implies no conversion)</li>
10763  * <li>string</li>
10764  * <li>int</li>
10765  * <li>float</li>
10766  * <li>boolean</li>
10767  * <li>date</li></ul></p></li>
10768  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10769  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10770  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10771  * by the Reader into an object that will be stored in the Record. It is passed the
10772  * following parameters:<ul>
10773  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10774  * </ul></p></li>
10775  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10776  * </ul>
10777  * <br>usage:<br><pre><code>
10778 var TopicRecord = Roo.data.Record.create(
10779     {name: 'title', mapping: 'topic_title'},
10780     {name: 'author', mapping: 'username'},
10781     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10782     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10783     {name: 'lastPoster', mapping: 'user2'},
10784     {name: 'excerpt', mapping: 'post_text'}
10785 );
10786
10787 var myNewRecord = new TopicRecord({
10788     title: 'Do my job please',
10789     author: 'noobie',
10790     totalPosts: 1,
10791     lastPost: new Date(),
10792     lastPoster: 'Animal',
10793     excerpt: 'No way dude!'
10794 });
10795 myStore.add(myNewRecord);
10796 </code></pre>
10797  * @method create
10798  * @static
10799  */
10800 Roo.data.Record.create = function(o){
10801     var f = function(){
10802         f.superclass.constructor.apply(this, arguments);
10803     };
10804     Roo.extend(f, Roo.data.Record);
10805     var p = f.prototype;
10806     p.fields = new Roo.util.MixedCollection(false, function(field){
10807         return field.name;
10808     });
10809     for(var i = 0, len = o.length; i < len; i++){
10810         p.fields.add(new Roo.data.Field(o[i]));
10811     }
10812     f.getField = function(name){
10813         return p.fields.get(name);  
10814     };
10815     return f;
10816 };
10817
10818 Roo.data.Record.AUTO_ID = 1000;
10819 Roo.data.Record.EDIT = 'edit';
10820 Roo.data.Record.REJECT = 'reject';
10821 Roo.data.Record.COMMIT = 'commit';
10822
10823 Roo.data.Record.prototype = {
10824     /**
10825      * Readonly flag - true if this record has been modified.
10826      * @type Boolean
10827      */
10828     dirty : false,
10829     editing : false,
10830     error: null,
10831     modified: null,
10832
10833     // private
10834     join : function(store){
10835         this.store = store;
10836     },
10837
10838     /**
10839      * Set the named field to the specified value.
10840      * @param {String} name The name of the field to set.
10841      * @param {Object} value The value to set the field to.
10842      */
10843     set : function(name, value){
10844         if(this.data[name] == value){
10845             return;
10846         }
10847         this.dirty = true;
10848         if(!this.modified){
10849             this.modified = {};
10850         }
10851         if(typeof this.modified[name] == 'undefined'){
10852             this.modified[name] = this.data[name];
10853         }
10854         this.data[name] = value;
10855         if(!this.editing && this.store){
10856             this.store.afterEdit(this);
10857         }       
10858     },
10859
10860     /**
10861      * Get the value of the named field.
10862      * @param {String} name The name of the field to get the value of.
10863      * @return {Object} The value of the field.
10864      */
10865     get : function(name){
10866         return this.data[name]; 
10867     },
10868
10869     // private
10870     beginEdit : function(){
10871         this.editing = true;
10872         this.modified = {}; 
10873     },
10874
10875     // private
10876     cancelEdit : function(){
10877         this.editing = false;
10878         delete this.modified;
10879     },
10880
10881     // private
10882     endEdit : function(){
10883         this.editing = false;
10884         if(this.dirty && this.store){
10885             this.store.afterEdit(this);
10886         }
10887     },
10888
10889     /**
10890      * Usually called by the {@link Roo.data.Store} which owns the Record.
10891      * Rejects all changes made to the Record since either creation, or the last commit operation.
10892      * Modified fields are reverted to their original values.
10893      * <p>
10894      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10895      * of reject operations.
10896      */
10897     reject : function(){
10898         var m = this.modified;
10899         for(var n in m){
10900             if(typeof m[n] != "function"){
10901                 this.data[n] = m[n];
10902             }
10903         }
10904         this.dirty = false;
10905         delete this.modified;
10906         this.editing = false;
10907         if(this.store){
10908             this.store.afterReject(this);
10909         }
10910     },
10911
10912     /**
10913      * Usually called by the {@link Roo.data.Store} which owns the Record.
10914      * Commits all changes made to the Record since either creation, or the last commit operation.
10915      * <p>
10916      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10917      * of commit operations.
10918      */
10919     commit : function(){
10920         this.dirty = false;
10921         delete this.modified;
10922         this.editing = false;
10923         if(this.store){
10924             this.store.afterCommit(this);
10925         }
10926     },
10927
10928     // private
10929     hasError : function(){
10930         return this.error != null;
10931     },
10932
10933     // private
10934     clearError : function(){
10935         this.error = null;
10936     },
10937
10938     /**
10939      * Creates a copy of this record.
10940      * @param {String} id (optional) A new record id if you don't want to use this record's id
10941      * @return {Record}
10942      */
10943     copy : function(newId) {
10944         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10945     }
10946 };/*
10947  * Based on:
10948  * Ext JS Library 1.1.1
10949  * Copyright(c) 2006-2007, Ext JS, LLC.
10950  *
10951  * Originally Released Under LGPL - original licence link has changed is not relivant.
10952  *
10953  * Fork - LGPL
10954  * <script type="text/javascript">
10955  */
10956
10957
10958
10959 /**
10960  * @class Roo.data.Store
10961  * @extends Roo.util.Observable
10962  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10963  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10964  * <p>
10965  * 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
10966  * has no knowledge of the format of the data returned by the Proxy.<br>
10967  * <p>
10968  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10969  * instances from the data object. These records are cached and made available through accessor functions.
10970  * @constructor
10971  * Creates a new Store.
10972  * @param {Object} config A config object containing the objects needed for the Store to access data,
10973  * and read the data into Records.
10974  */
10975 Roo.data.Store = function(config){
10976     this.data = new Roo.util.MixedCollection(false);
10977     this.data.getKey = function(o){
10978         return o.id;
10979     };
10980     this.baseParams = {};
10981     // private
10982     this.paramNames = {
10983         "start" : "start",
10984         "limit" : "limit",
10985         "sort" : "sort",
10986         "dir" : "dir",
10987         "multisort" : "_multisort"
10988     };
10989
10990     if(config && config.data){
10991         this.inlineData = config.data;
10992         delete config.data;
10993     }
10994
10995     Roo.apply(this, config);
10996     
10997     if(this.reader){ // reader passed
10998         this.reader = Roo.factory(this.reader, Roo.data);
10999         this.reader.xmodule = this.xmodule || false;
11000         if(!this.recordType){
11001             this.recordType = this.reader.recordType;
11002         }
11003         if(this.reader.onMetaChange){
11004             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11005         }
11006     }
11007
11008     if(this.recordType){
11009         this.fields = this.recordType.prototype.fields;
11010     }
11011     this.modified = [];
11012
11013     this.addEvents({
11014         /**
11015          * @event datachanged
11016          * Fires when the data cache has changed, and a widget which is using this Store
11017          * as a Record cache should refresh its view.
11018          * @param {Store} this
11019          */
11020         datachanged : true,
11021         /**
11022          * @event metachange
11023          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11024          * @param {Store} this
11025          * @param {Object} meta The JSON metadata
11026          */
11027         metachange : true,
11028         /**
11029          * @event add
11030          * Fires when Records have been added to the Store
11031          * @param {Store} this
11032          * @param {Roo.data.Record[]} records The array of Records added
11033          * @param {Number} index The index at which the record(s) were added
11034          */
11035         add : true,
11036         /**
11037          * @event remove
11038          * Fires when a Record has been removed from the Store
11039          * @param {Store} this
11040          * @param {Roo.data.Record} record The Record that was removed
11041          * @param {Number} index The index at which the record was removed
11042          */
11043         remove : true,
11044         /**
11045          * @event update
11046          * Fires when a Record has been updated
11047          * @param {Store} this
11048          * @param {Roo.data.Record} record The Record that was updated
11049          * @param {String} operation The update operation being performed.  Value may be one of:
11050          * <pre><code>
11051  Roo.data.Record.EDIT
11052  Roo.data.Record.REJECT
11053  Roo.data.Record.COMMIT
11054          * </code></pre>
11055          */
11056         update : true,
11057         /**
11058          * @event clear
11059          * Fires when the data cache has been cleared.
11060          * @param {Store} this
11061          */
11062         clear : true,
11063         /**
11064          * @event beforeload
11065          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11066          * the load action will be canceled.
11067          * @param {Store} this
11068          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11069          */
11070         beforeload : true,
11071         /**
11072          * @event beforeloadadd
11073          * Fires after a new set of Records has been loaded.
11074          * @param {Store} this
11075          * @param {Roo.data.Record[]} records The Records that were loaded
11076          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11077          */
11078         beforeloadadd : true,
11079         /**
11080          * @event load
11081          * Fires after a new set of Records has been loaded, before they are added to the store.
11082          * @param {Store} this
11083          * @param {Roo.data.Record[]} records The Records that were loaded
11084          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11085          * @params {Object} return from reader
11086          */
11087         load : true,
11088         /**
11089          * @event loadexception
11090          * Fires if an exception occurs in the Proxy during loading.
11091          * Called with the signature of the Proxy's "loadexception" event.
11092          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11093          * 
11094          * @param {Proxy} 
11095          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11096          * @param {Object} load options 
11097          * @param {Object} jsonData from your request (normally this contains the Exception)
11098          */
11099         loadexception : true
11100     });
11101     
11102     if(this.proxy){
11103         this.proxy = Roo.factory(this.proxy, Roo.data);
11104         this.proxy.xmodule = this.xmodule || false;
11105         this.relayEvents(this.proxy,  ["loadexception"]);
11106     }
11107     this.sortToggle = {};
11108     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11109
11110     Roo.data.Store.superclass.constructor.call(this);
11111
11112     if(this.inlineData){
11113         this.loadData(this.inlineData);
11114         delete this.inlineData;
11115     }
11116 };
11117
11118 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11119      /**
11120     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11121     * without a remote query - used by combo/forms at present.
11122     */
11123     
11124     /**
11125     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11126     */
11127     /**
11128     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11129     */
11130     /**
11131     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11132     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11133     */
11134     /**
11135     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11136     * on any HTTP request
11137     */
11138     /**
11139     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11140     */
11141     /**
11142     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11143     */
11144     multiSort: false,
11145     /**
11146     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11147     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11148     */
11149     remoteSort : false,
11150
11151     /**
11152     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11153      * loaded or when a record is removed. (defaults to false).
11154     */
11155     pruneModifiedRecords : false,
11156
11157     // private
11158     lastOptions : null,
11159
11160     /**
11161      * Add Records to the Store and fires the add event.
11162      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11163      */
11164     add : function(records){
11165         records = [].concat(records);
11166         for(var i = 0, len = records.length; i < len; i++){
11167             records[i].join(this);
11168         }
11169         var index = this.data.length;
11170         this.data.addAll(records);
11171         this.fireEvent("add", this, records, index);
11172     },
11173
11174     /**
11175      * Remove a Record from the Store and fires the remove event.
11176      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11177      */
11178     remove : function(record){
11179         var index = this.data.indexOf(record);
11180         this.data.removeAt(index);
11181  
11182         if(this.pruneModifiedRecords){
11183             this.modified.remove(record);
11184         }
11185         this.fireEvent("remove", this, record, index);
11186     },
11187
11188     /**
11189      * Remove all Records from the Store and fires the clear event.
11190      */
11191     removeAll : function(){
11192         this.data.clear();
11193         if(this.pruneModifiedRecords){
11194             this.modified = [];
11195         }
11196         this.fireEvent("clear", this);
11197     },
11198
11199     /**
11200      * Inserts Records to the Store at the given index and fires the add event.
11201      * @param {Number} index The start index at which to insert the passed Records.
11202      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11203      */
11204     insert : function(index, records){
11205         records = [].concat(records);
11206         for(var i = 0, len = records.length; i < len; i++){
11207             this.data.insert(index, records[i]);
11208             records[i].join(this);
11209         }
11210         this.fireEvent("add", this, records, index);
11211     },
11212
11213     /**
11214      * Get the index within the cache of the passed Record.
11215      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11216      * @return {Number} The index of the passed Record. Returns -1 if not found.
11217      */
11218     indexOf : function(record){
11219         return this.data.indexOf(record);
11220     },
11221
11222     /**
11223      * Get the index within the cache of the Record with the passed id.
11224      * @param {String} id The id of the Record to find.
11225      * @return {Number} The index of the Record. Returns -1 if not found.
11226      */
11227     indexOfId : function(id){
11228         return this.data.indexOfKey(id);
11229     },
11230
11231     /**
11232      * Get the Record with the specified id.
11233      * @param {String} id The id of the Record to find.
11234      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11235      */
11236     getById : function(id){
11237         return this.data.key(id);
11238     },
11239
11240     /**
11241      * Get the Record at the specified index.
11242      * @param {Number} index The index of the Record to find.
11243      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11244      */
11245     getAt : function(index){
11246         return this.data.itemAt(index);
11247     },
11248
11249     /**
11250      * Returns a range of Records between specified indices.
11251      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11252      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11253      * @return {Roo.data.Record[]} An array of Records
11254      */
11255     getRange : function(start, end){
11256         return this.data.getRange(start, end);
11257     },
11258
11259     // private
11260     storeOptions : function(o){
11261         o = Roo.apply({}, o);
11262         delete o.callback;
11263         delete o.scope;
11264         this.lastOptions = o;
11265     },
11266
11267     /**
11268      * Loads the Record cache from the configured Proxy using the configured Reader.
11269      * <p>
11270      * If using remote paging, then the first load call must specify the <em>start</em>
11271      * and <em>limit</em> properties in the options.params property to establish the initial
11272      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11273      * <p>
11274      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11275      * and this call will return before the new data has been loaded. Perform any post-processing
11276      * in a callback function, or in a "load" event handler.</strong>
11277      * <p>
11278      * @param {Object} options An object containing properties which control loading options:<ul>
11279      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11280      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11281      * passed the following arguments:<ul>
11282      * <li>r : Roo.data.Record[]</li>
11283      * <li>options: Options object from the load call</li>
11284      * <li>success: Boolean success indicator</li></ul></li>
11285      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11286      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11287      * </ul>
11288      */
11289     load : function(options){
11290         options = options || {};
11291         if(this.fireEvent("beforeload", this, options) !== false){
11292             this.storeOptions(options);
11293             var p = Roo.apply(options.params || {}, this.baseParams);
11294             // if meta was not loaded from remote source.. try requesting it.
11295             if (!this.reader.metaFromRemote) {
11296                 p._requestMeta = 1;
11297             }
11298             if(this.sortInfo && this.remoteSort){
11299                 var pn = this.paramNames;
11300                 p[pn["sort"]] = this.sortInfo.field;
11301                 p[pn["dir"]] = this.sortInfo.direction;
11302             }
11303             if (this.multiSort) {
11304                 var pn = this.paramNames;
11305                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11306             }
11307             
11308             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11309         }
11310     },
11311
11312     /**
11313      * Reloads the Record cache from the configured Proxy using the configured Reader and
11314      * the options from the last load operation performed.
11315      * @param {Object} options (optional) An object containing properties which may override the options
11316      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11317      * the most recently used options are reused).
11318      */
11319     reload : function(options){
11320         this.load(Roo.applyIf(options||{}, this.lastOptions));
11321     },
11322
11323     // private
11324     // Called as a callback by the Reader during a load operation.
11325     loadRecords : function(o, options, success){
11326         if(!o || success === false){
11327             if(success !== false){
11328                 this.fireEvent("load", this, [], options, o);
11329             }
11330             if(options.callback){
11331                 options.callback.call(options.scope || this, [], options, false);
11332             }
11333             return;
11334         }
11335         // if data returned failure - throw an exception.
11336         if (o.success === false) {
11337             // show a message if no listener is registered.
11338             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11339                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11340             }
11341             // loadmask wil be hooked into this..
11342             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11343             return;
11344         }
11345         var r = o.records, t = o.totalRecords || r.length;
11346         
11347         this.fireEvent("beforeloadadd", this, r, options, o);
11348         
11349         if(!options || options.add !== true){
11350             if(this.pruneModifiedRecords){
11351                 this.modified = [];
11352             }
11353             for(var i = 0, len = r.length; i < len; i++){
11354                 r[i].join(this);
11355             }
11356             if(this.snapshot){
11357                 this.data = this.snapshot;
11358                 delete this.snapshot;
11359             }
11360             this.data.clear();
11361             this.data.addAll(r);
11362             this.totalLength = t;
11363             this.applySort();
11364             this.fireEvent("datachanged", this);
11365         }else{
11366             this.totalLength = Math.max(t, this.data.length+r.length);
11367             this.add(r);
11368         }
11369         
11370         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11371                 
11372             var e = new Roo.data.Record({});
11373
11374             e.set(this.parent.displayField, this.parent.emptyTitle);
11375             e.set(this.parent.valueField, '');
11376
11377             this.insert(0, e);
11378         }
11379             
11380         this.fireEvent("load", this, r, options, o);
11381         if(options.callback){
11382             options.callback.call(options.scope || this, r, options, true);
11383         }
11384     },
11385
11386
11387     /**
11388      * Loads data from a passed data block. A Reader which understands the format of the data
11389      * must have been configured in the constructor.
11390      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11391      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11392      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11393      */
11394     loadData : function(o, append){
11395         var r = this.reader.readRecords(o);
11396         this.loadRecords(r, {add: append}, true);
11397     },
11398
11399     /**
11400      * Gets the number of cached records.
11401      * <p>
11402      * <em>If using paging, this may not be the total size of the dataset. If the data object
11403      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11404      * the data set size</em>
11405      */
11406     getCount : function(){
11407         return this.data.length || 0;
11408     },
11409
11410     /**
11411      * Gets the total number of records in the dataset as returned by the server.
11412      * <p>
11413      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11414      * the dataset size</em>
11415      */
11416     getTotalCount : function(){
11417         return this.totalLength || 0;
11418     },
11419
11420     /**
11421      * Returns the sort state of the Store as an object with two properties:
11422      * <pre><code>
11423  field {String} The name of the field by which the Records are sorted
11424  direction {String} The sort order, "ASC" or "DESC"
11425      * </code></pre>
11426      */
11427     getSortState : function(){
11428         return this.sortInfo;
11429     },
11430
11431     // private
11432     applySort : function(){
11433         if(this.sortInfo && !this.remoteSort){
11434             var s = this.sortInfo, f = s.field;
11435             var st = this.fields.get(f).sortType;
11436             var fn = function(r1, r2){
11437                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11438                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11439             };
11440             this.data.sort(s.direction, fn);
11441             if(this.snapshot && this.snapshot != this.data){
11442                 this.snapshot.sort(s.direction, fn);
11443             }
11444         }
11445     },
11446
11447     /**
11448      * Sets the default sort column and order to be used by the next load operation.
11449      * @param {String} fieldName The name of the field to sort by.
11450      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11451      */
11452     setDefaultSort : function(field, dir){
11453         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11454     },
11455
11456     /**
11457      * Sort the Records.
11458      * If remote sorting is used, the sort is performed on the server, and the cache is
11459      * reloaded. If local sorting is used, the cache is sorted internally.
11460      * @param {String} fieldName The name of the field to sort by.
11461      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11462      */
11463     sort : function(fieldName, dir){
11464         var f = this.fields.get(fieldName);
11465         if(!dir){
11466             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11467             
11468             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11469                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11470             }else{
11471                 dir = f.sortDir;
11472             }
11473         }
11474         this.sortToggle[f.name] = dir;
11475         this.sortInfo = {field: f.name, direction: dir};
11476         if(!this.remoteSort){
11477             this.applySort();
11478             this.fireEvent("datachanged", this);
11479         }else{
11480             this.load(this.lastOptions);
11481         }
11482     },
11483
11484     /**
11485      * Calls the specified function for each of the Records in the cache.
11486      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11487      * Returning <em>false</em> aborts and exits the iteration.
11488      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11489      */
11490     each : function(fn, scope){
11491         this.data.each(fn, scope);
11492     },
11493
11494     /**
11495      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11496      * (e.g., during paging).
11497      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11498      */
11499     getModifiedRecords : function(){
11500         return this.modified;
11501     },
11502
11503     // private
11504     createFilterFn : function(property, value, anyMatch){
11505         if(!value.exec){ // not a regex
11506             value = String(value);
11507             if(value.length == 0){
11508                 return false;
11509             }
11510             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11511         }
11512         return function(r){
11513             return value.test(r.data[property]);
11514         };
11515     },
11516
11517     /**
11518      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11519      * @param {String} property A field on your records
11520      * @param {Number} start The record index to start at (defaults to 0)
11521      * @param {Number} end The last record index to include (defaults to length - 1)
11522      * @return {Number} The sum
11523      */
11524     sum : function(property, start, end){
11525         var rs = this.data.items, v = 0;
11526         start = start || 0;
11527         end = (end || end === 0) ? end : rs.length-1;
11528
11529         for(var i = start; i <= end; i++){
11530             v += (rs[i].data[property] || 0);
11531         }
11532         return v;
11533     },
11534
11535     /**
11536      * Filter the records by a specified property.
11537      * @param {String} field A field on your records
11538      * @param {String/RegExp} value Either a string that the field
11539      * should start with or a RegExp to test against the field
11540      * @param {Boolean} anyMatch True to match any part not just the beginning
11541      */
11542     filter : function(property, value, anyMatch){
11543         var fn = this.createFilterFn(property, value, anyMatch);
11544         return fn ? this.filterBy(fn) : this.clearFilter();
11545     },
11546
11547     /**
11548      * Filter by a function. The specified function will be called with each
11549      * record in this data source. If the function returns true the record is included,
11550      * otherwise it is filtered.
11551      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11552      * @param {Object} scope (optional) The scope of the function (defaults to this)
11553      */
11554     filterBy : function(fn, scope){
11555         this.snapshot = this.snapshot || this.data;
11556         this.data = this.queryBy(fn, scope||this);
11557         this.fireEvent("datachanged", this);
11558     },
11559
11560     /**
11561      * Query the records by a specified property.
11562      * @param {String} field A field on your records
11563      * @param {String/RegExp} value Either a string that the field
11564      * should start with or a RegExp to test against the field
11565      * @param {Boolean} anyMatch True to match any part not just the beginning
11566      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11567      */
11568     query : function(property, value, anyMatch){
11569         var fn = this.createFilterFn(property, value, anyMatch);
11570         return fn ? this.queryBy(fn) : this.data.clone();
11571     },
11572
11573     /**
11574      * Query by a function. The specified function will be called with each
11575      * record in this data source. If the function returns true the record is included
11576      * in the results.
11577      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11578      * @param {Object} scope (optional) The scope of the function (defaults to this)
11579       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11580      **/
11581     queryBy : function(fn, scope){
11582         var data = this.snapshot || this.data;
11583         return data.filterBy(fn, scope||this);
11584     },
11585
11586     /**
11587      * Collects unique values for a particular dataIndex from this store.
11588      * @param {String} dataIndex The property to collect
11589      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11590      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11591      * @return {Array} An array of the unique values
11592      **/
11593     collect : function(dataIndex, allowNull, bypassFilter){
11594         var d = (bypassFilter === true && this.snapshot) ?
11595                 this.snapshot.items : this.data.items;
11596         var v, sv, r = [], l = {};
11597         for(var i = 0, len = d.length; i < len; i++){
11598             v = d[i].data[dataIndex];
11599             sv = String(v);
11600             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11601                 l[sv] = true;
11602                 r[r.length] = v;
11603             }
11604         }
11605         return r;
11606     },
11607
11608     /**
11609      * Revert to a view of the Record cache with no filtering applied.
11610      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11611      */
11612     clearFilter : function(suppressEvent){
11613         if(this.snapshot && this.snapshot != this.data){
11614             this.data = this.snapshot;
11615             delete this.snapshot;
11616             if(suppressEvent !== true){
11617                 this.fireEvent("datachanged", this);
11618             }
11619         }
11620     },
11621
11622     // private
11623     afterEdit : function(record){
11624         if(this.modified.indexOf(record) == -1){
11625             this.modified.push(record);
11626         }
11627         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11628     },
11629     
11630     // private
11631     afterReject : function(record){
11632         this.modified.remove(record);
11633         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11634     },
11635
11636     // private
11637     afterCommit : function(record){
11638         this.modified.remove(record);
11639         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11640     },
11641
11642     /**
11643      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11644      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11645      */
11646     commitChanges : function(){
11647         var m = this.modified.slice(0);
11648         this.modified = [];
11649         for(var i = 0, len = m.length; i < len; i++){
11650             m[i].commit();
11651         }
11652     },
11653
11654     /**
11655      * Cancel outstanding changes on all changed records.
11656      */
11657     rejectChanges : function(){
11658         var m = this.modified.slice(0);
11659         this.modified = [];
11660         for(var i = 0, len = m.length; i < len; i++){
11661             m[i].reject();
11662         }
11663     },
11664
11665     onMetaChange : function(meta, rtype, o){
11666         this.recordType = rtype;
11667         this.fields = rtype.prototype.fields;
11668         delete this.snapshot;
11669         this.sortInfo = meta.sortInfo || this.sortInfo;
11670         this.modified = [];
11671         this.fireEvent('metachange', this, this.reader.meta);
11672     },
11673     
11674     moveIndex : function(data, type)
11675     {
11676         var index = this.indexOf(data);
11677         
11678         var newIndex = index + type;
11679         
11680         this.remove(data);
11681         
11682         this.insert(newIndex, data);
11683         
11684     }
11685 });/*
11686  * Based on:
11687  * Ext JS Library 1.1.1
11688  * Copyright(c) 2006-2007, Ext JS, LLC.
11689  *
11690  * Originally Released Under LGPL - original licence link has changed is not relivant.
11691  *
11692  * Fork - LGPL
11693  * <script type="text/javascript">
11694  */
11695
11696 /**
11697  * @class Roo.data.SimpleStore
11698  * @extends Roo.data.Store
11699  * Small helper class to make creating Stores from Array data easier.
11700  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11701  * @cfg {Array} fields An array of field definition objects, or field name strings.
11702  * @cfg {Array} data The multi-dimensional array of data
11703  * @constructor
11704  * @param {Object} config
11705  */
11706 Roo.data.SimpleStore = function(config){
11707     Roo.data.SimpleStore.superclass.constructor.call(this, {
11708         isLocal : true,
11709         reader: new Roo.data.ArrayReader({
11710                 id: config.id
11711             },
11712             Roo.data.Record.create(config.fields)
11713         ),
11714         proxy : new Roo.data.MemoryProxy(config.data)
11715     });
11716     this.load();
11717 };
11718 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11719  * Based on:
11720  * Ext JS Library 1.1.1
11721  * Copyright(c) 2006-2007, Ext JS, LLC.
11722  *
11723  * Originally Released Under LGPL - original licence link has changed is not relivant.
11724  *
11725  * Fork - LGPL
11726  * <script type="text/javascript">
11727  */
11728
11729 /**
11730 /**
11731  * @extends Roo.data.Store
11732  * @class Roo.data.JsonStore
11733  * Small helper class to make creating Stores for JSON data easier. <br/>
11734 <pre><code>
11735 var store = new Roo.data.JsonStore({
11736     url: 'get-images.php',
11737     root: 'images',
11738     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11739 });
11740 </code></pre>
11741  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11742  * JsonReader and HttpProxy (unless inline data is provided).</b>
11743  * @cfg {Array} fields An array of field definition objects, or field name strings.
11744  * @constructor
11745  * @param {Object} config
11746  */
11747 Roo.data.JsonStore = function(c){
11748     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11749         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11750         reader: new Roo.data.JsonReader(c, c.fields)
11751     }));
11752 };
11753 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11754  * Based on:
11755  * Ext JS Library 1.1.1
11756  * Copyright(c) 2006-2007, Ext JS, LLC.
11757  *
11758  * Originally Released Under LGPL - original licence link has changed is not relivant.
11759  *
11760  * Fork - LGPL
11761  * <script type="text/javascript">
11762  */
11763
11764  
11765 Roo.data.Field = function(config){
11766     if(typeof config == "string"){
11767         config = {name: config};
11768     }
11769     Roo.apply(this, config);
11770     
11771     if(!this.type){
11772         this.type = "auto";
11773     }
11774     
11775     var st = Roo.data.SortTypes;
11776     // named sortTypes are supported, here we look them up
11777     if(typeof this.sortType == "string"){
11778         this.sortType = st[this.sortType];
11779     }
11780     
11781     // set default sortType for strings and dates
11782     if(!this.sortType){
11783         switch(this.type){
11784             case "string":
11785                 this.sortType = st.asUCString;
11786                 break;
11787             case "date":
11788                 this.sortType = st.asDate;
11789                 break;
11790             default:
11791                 this.sortType = st.none;
11792         }
11793     }
11794
11795     // define once
11796     var stripRe = /[\$,%]/g;
11797
11798     // prebuilt conversion function for this field, instead of
11799     // switching every time we're reading a value
11800     if(!this.convert){
11801         var cv, dateFormat = this.dateFormat;
11802         switch(this.type){
11803             case "":
11804             case "auto":
11805             case undefined:
11806                 cv = function(v){ return v; };
11807                 break;
11808             case "string":
11809                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11810                 break;
11811             case "int":
11812                 cv = function(v){
11813                     return v !== undefined && v !== null && v !== '' ?
11814                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11815                     };
11816                 break;
11817             case "float":
11818                 cv = function(v){
11819                     return v !== undefined && v !== null && v !== '' ?
11820                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11821                     };
11822                 break;
11823             case "bool":
11824             case "boolean":
11825                 cv = function(v){ return v === true || v === "true" || v == 1; };
11826                 break;
11827             case "date":
11828                 cv = function(v){
11829                     if(!v){
11830                         return '';
11831                     }
11832                     if(v instanceof Date){
11833                         return v;
11834                     }
11835                     if(dateFormat){
11836                         if(dateFormat == "timestamp"){
11837                             return new Date(v*1000);
11838                         }
11839                         return Date.parseDate(v, dateFormat);
11840                     }
11841                     var parsed = Date.parse(v);
11842                     return parsed ? new Date(parsed) : null;
11843                 };
11844              break;
11845             
11846         }
11847         this.convert = cv;
11848     }
11849 };
11850
11851 Roo.data.Field.prototype = {
11852     dateFormat: null,
11853     defaultValue: "",
11854     mapping: null,
11855     sortType : null,
11856     sortDir : "ASC"
11857 };/*
11858  * Based on:
11859  * Ext JS Library 1.1.1
11860  * Copyright(c) 2006-2007, Ext JS, LLC.
11861  *
11862  * Originally Released Under LGPL - original licence link has changed is not relivant.
11863  *
11864  * Fork - LGPL
11865  * <script type="text/javascript">
11866  */
11867  
11868 // Base class for reading structured data from a data source.  This class is intended to be
11869 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11870
11871 /**
11872  * @class Roo.data.DataReader
11873  * Base class for reading structured data from a data source.  This class is intended to be
11874  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11875  */
11876
11877 Roo.data.DataReader = function(meta, recordType){
11878     
11879     this.meta = meta;
11880     
11881     this.recordType = recordType instanceof Array ? 
11882         Roo.data.Record.create(recordType) : recordType;
11883 };
11884
11885 Roo.data.DataReader.prototype = {
11886      /**
11887      * Create an empty record
11888      * @param {Object} data (optional) - overlay some values
11889      * @return {Roo.data.Record} record created.
11890      */
11891     newRow :  function(d) {
11892         var da =  {};
11893         this.recordType.prototype.fields.each(function(c) {
11894             switch( c.type) {
11895                 case 'int' : da[c.name] = 0; break;
11896                 case 'date' : da[c.name] = new Date(); break;
11897                 case 'float' : da[c.name] = 0.0; break;
11898                 case 'boolean' : da[c.name] = false; break;
11899                 default : da[c.name] = ""; break;
11900             }
11901             
11902         });
11903         return new this.recordType(Roo.apply(da, d));
11904     }
11905     
11906 };/*
11907  * Based on:
11908  * Ext JS Library 1.1.1
11909  * Copyright(c) 2006-2007, Ext JS, LLC.
11910  *
11911  * Originally Released Under LGPL - original licence link has changed is not relivant.
11912  *
11913  * Fork - LGPL
11914  * <script type="text/javascript">
11915  */
11916
11917 /**
11918  * @class Roo.data.DataProxy
11919  * @extends Roo.data.Observable
11920  * This class is an abstract base class for implementations which provide retrieval of
11921  * unformatted data objects.<br>
11922  * <p>
11923  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11924  * (of the appropriate type which knows how to parse the data object) to provide a block of
11925  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11926  * <p>
11927  * Custom implementations must implement the load method as described in
11928  * {@link Roo.data.HttpProxy#load}.
11929  */
11930 Roo.data.DataProxy = function(){
11931     this.addEvents({
11932         /**
11933          * @event beforeload
11934          * Fires before a network request is made to retrieve a data object.
11935          * @param {Object} This DataProxy object.
11936          * @param {Object} params The params parameter to the load function.
11937          */
11938         beforeload : true,
11939         /**
11940          * @event load
11941          * Fires before the load method's callback is called.
11942          * @param {Object} This DataProxy object.
11943          * @param {Object} o The data object.
11944          * @param {Object} arg The callback argument object passed to the load function.
11945          */
11946         load : true,
11947         /**
11948          * @event loadexception
11949          * Fires if an Exception occurs during data retrieval.
11950          * @param {Object} This DataProxy object.
11951          * @param {Object} o The data object.
11952          * @param {Object} arg The callback argument object passed to the load function.
11953          * @param {Object} e The Exception.
11954          */
11955         loadexception : true
11956     });
11957     Roo.data.DataProxy.superclass.constructor.call(this);
11958 };
11959
11960 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11961
11962     /**
11963      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11964      */
11965 /*
11966  * Based on:
11967  * Ext JS Library 1.1.1
11968  * Copyright(c) 2006-2007, Ext JS, LLC.
11969  *
11970  * Originally Released Under LGPL - original licence link has changed is not relivant.
11971  *
11972  * Fork - LGPL
11973  * <script type="text/javascript">
11974  */
11975 /**
11976  * @class Roo.data.MemoryProxy
11977  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11978  * to the Reader when its load method is called.
11979  * @constructor
11980  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11981  */
11982 Roo.data.MemoryProxy = function(data){
11983     if (data.data) {
11984         data = data.data;
11985     }
11986     Roo.data.MemoryProxy.superclass.constructor.call(this);
11987     this.data = data;
11988 };
11989
11990 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11991     
11992     /**
11993      * Load data from the requested source (in this case an in-memory
11994      * data object passed to the constructor), read the data object into
11995      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11996      * process that block using the passed callback.
11997      * @param {Object} params This parameter is not used by the MemoryProxy class.
11998      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11999      * object into a block of Roo.data.Records.
12000      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12001      * The function must be passed <ul>
12002      * <li>The Record block object</li>
12003      * <li>The "arg" argument from the load function</li>
12004      * <li>A boolean success indicator</li>
12005      * </ul>
12006      * @param {Object} scope The scope in which to call the callback
12007      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12008      */
12009     load : function(params, reader, callback, scope, arg){
12010         params = params || {};
12011         var result;
12012         try {
12013             result = reader.readRecords(this.data);
12014         }catch(e){
12015             this.fireEvent("loadexception", this, arg, null, e);
12016             callback.call(scope, null, arg, false);
12017             return;
12018         }
12019         callback.call(scope, result, arg, true);
12020     },
12021     
12022     // private
12023     update : function(params, records){
12024         
12025     }
12026 });/*
12027  * Based on:
12028  * Ext JS Library 1.1.1
12029  * Copyright(c) 2006-2007, Ext JS, LLC.
12030  *
12031  * Originally Released Under LGPL - original licence link has changed is not relivant.
12032  *
12033  * Fork - LGPL
12034  * <script type="text/javascript">
12035  */
12036 /**
12037  * @class Roo.data.HttpProxy
12038  * @extends Roo.data.DataProxy
12039  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12040  * configured to reference a certain URL.<br><br>
12041  * <p>
12042  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12043  * from which the running page was served.<br><br>
12044  * <p>
12045  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12046  * <p>
12047  * Be aware that to enable the browser to parse an XML document, the server must set
12048  * the Content-Type header in the HTTP response to "text/xml".
12049  * @constructor
12050  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12051  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12052  * will be used to make the request.
12053  */
12054 Roo.data.HttpProxy = function(conn){
12055     Roo.data.HttpProxy.superclass.constructor.call(this);
12056     // is conn a conn config or a real conn?
12057     this.conn = conn;
12058     this.useAjax = !conn || !conn.events;
12059   
12060 };
12061
12062 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12063     // thse are take from connection...
12064     
12065     /**
12066      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12067      */
12068     /**
12069      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12070      * extra parameters to each request made by this object. (defaults to undefined)
12071      */
12072     /**
12073      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12074      *  to each request made by this object. (defaults to undefined)
12075      */
12076     /**
12077      * @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)
12078      */
12079     /**
12080      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12081      */
12082      /**
12083      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12084      * @type Boolean
12085      */
12086   
12087
12088     /**
12089      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12090      * @type Boolean
12091      */
12092     /**
12093      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12094      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12095      * a finer-grained basis than the DataProxy events.
12096      */
12097     getConnection : function(){
12098         return this.useAjax ? Roo.Ajax : this.conn;
12099     },
12100
12101     /**
12102      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12103      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12104      * process that block using the passed callback.
12105      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12106      * for the request to the remote server.
12107      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12108      * object into a block of Roo.data.Records.
12109      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12110      * The function must be passed <ul>
12111      * <li>The Record block object</li>
12112      * <li>The "arg" argument from the load function</li>
12113      * <li>A boolean success indicator</li>
12114      * </ul>
12115      * @param {Object} scope The scope in which to call the callback
12116      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12117      */
12118     load : function(params, reader, callback, scope, arg){
12119         if(this.fireEvent("beforeload", this, params) !== false){
12120             var  o = {
12121                 params : params || {},
12122                 request: {
12123                     callback : callback,
12124                     scope : scope,
12125                     arg : arg
12126                 },
12127                 reader: reader,
12128                 callback : this.loadResponse,
12129                 scope: this
12130             };
12131             if(this.useAjax){
12132                 Roo.applyIf(o, this.conn);
12133                 if(this.activeRequest){
12134                     Roo.Ajax.abort(this.activeRequest);
12135                 }
12136                 this.activeRequest = Roo.Ajax.request(o);
12137             }else{
12138                 this.conn.request(o);
12139             }
12140         }else{
12141             callback.call(scope||this, null, arg, false);
12142         }
12143     },
12144
12145     // private
12146     loadResponse : function(o, success, response){
12147         delete this.activeRequest;
12148         if(!success){
12149             this.fireEvent("loadexception", this, o, response);
12150             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12151             return;
12152         }
12153         var result;
12154         try {
12155             result = o.reader.read(response);
12156         }catch(e){
12157             this.fireEvent("loadexception", this, o, response, e);
12158             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12159             return;
12160         }
12161         
12162         this.fireEvent("load", this, o, o.request.arg);
12163         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12164     },
12165
12166     // private
12167     update : function(dataSet){
12168
12169     },
12170
12171     // private
12172     updateResponse : function(dataSet){
12173
12174     }
12175 });/*
12176  * Based on:
12177  * Ext JS Library 1.1.1
12178  * Copyright(c) 2006-2007, Ext JS, LLC.
12179  *
12180  * Originally Released Under LGPL - original licence link has changed is not relivant.
12181  *
12182  * Fork - LGPL
12183  * <script type="text/javascript">
12184  */
12185
12186 /**
12187  * @class Roo.data.ScriptTagProxy
12188  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12189  * other than the originating domain of the running page.<br><br>
12190  * <p>
12191  * <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
12192  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12193  * <p>
12194  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12195  * source code that is used as the source inside a &lt;script> tag.<br><br>
12196  * <p>
12197  * In order for the browser to process the returned data, the server must wrap the data object
12198  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12199  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12200  * depending on whether the callback name was passed:
12201  * <p>
12202  * <pre><code>
12203 boolean scriptTag = false;
12204 String cb = request.getParameter("callback");
12205 if (cb != null) {
12206     scriptTag = true;
12207     response.setContentType("text/javascript");
12208 } else {
12209     response.setContentType("application/x-json");
12210 }
12211 Writer out = response.getWriter();
12212 if (scriptTag) {
12213     out.write(cb + "(");
12214 }
12215 out.print(dataBlock.toJsonString());
12216 if (scriptTag) {
12217     out.write(");");
12218 }
12219 </pre></code>
12220  *
12221  * @constructor
12222  * @param {Object} config A configuration object.
12223  */
12224 Roo.data.ScriptTagProxy = function(config){
12225     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12226     Roo.apply(this, config);
12227     this.head = document.getElementsByTagName("head")[0];
12228 };
12229
12230 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12231
12232 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12233     /**
12234      * @cfg {String} url The URL from which to request the data object.
12235      */
12236     /**
12237      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12238      */
12239     timeout : 30000,
12240     /**
12241      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12242      * the server the name of the callback function set up by the load call to process the returned data object.
12243      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12244      * javascript output which calls this named function passing the data object as its only parameter.
12245      */
12246     callbackParam : "callback",
12247     /**
12248      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12249      * name to the request.
12250      */
12251     nocache : true,
12252
12253     /**
12254      * Load data from the configured URL, read the data object into
12255      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12256      * process that block using the passed callback.
12257      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12258      * for the request to the remote server.
12259      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12260      * object into a block of Roo.data.Records.
12261      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12262      * The function must be passed <ul>
12263      * <li>The Record block object</li>
12264      * <li>The "arg" argument from the load function</li>
12265      * <li>A boolean success indicator</li>
12266      * </ul>
12267      * @param {Object} scope The scope in which to call the callback
12268      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12269      */
12270     load : function(params, reader, callback, scope, arg){
12271         if(this.fireEvent("beforeload", this, params) !== false){
12272
12273             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12274
12275             var url = this.url;
12276             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12277             if(this.nocache){
12278                 url += "&_dc=" + (new Date().getTime());
12279             }
12280             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12281             var trans = {
12282                 id : transId,
12283                 cb : "stcCallback"+transId,
12284                 scriptId : "stcScript"+transId,
12285                 params : params,
12286                 arg : arg,
12287                 url : url,
12288                 callback : callback,
12289                 scope : scope,
12290                 reader : reader
12291             };
12292             var conn = this;
12293
12294             window[trans.cb] = function(o){
12295                 conn.handleResponse(o, trans);
12296             };
12297
12298             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12299
12300             if(this.autoAbort !== false){
12301                 this.abort();
12302             }
12303
12304             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12305
12306             var script = document.createElement("script");
12307             script.setAttribute("src", url);
12308             script.setAttribute("type", "text/javascript");
12309             script.setAttribute("id", trans.scriptId);
12310             this.head.appendChild(script);
12311
12312             this.trans = trans;
12313         }else{
12314             callback.call(scope||this, null, arg, false);
12315         }
12316     },
12317
12318     // private
12319     isLoading : function(){
12320         return this.trans ? true : false;
12321     },
12322
12323     /**
12324      * Abort the current server request.
12325      */
12326     abort : function(){
12327         if(this.isLoading()){
12328             this.destroyTrans(this.trans);
12329         }
12330     },
12331
12332     // private
12333     destroyTrans : function(trans, isLoaded){
12334         this.head.removeChild(document.getElementById(trans.scriptId));
12335         clearTimeout(trans.timeoutId);
12336         if(isLoaded){
12337             window[trans.cb] = undefined;
12338             try{
12339                 delete window[trans.cb];
12340             }catch(e){}
12341         }else{
12342             // if hasn't been loaded, wait for load to remove it to prevent script error
12343             window[trans.cb] = function(){
12344                 window[trans.cb] = undefined;
12345                 try{
12346                     delete window[trans.cb];
12347                 }catch(e){}
12348             };
12349         }
12350     },
12351
12352     // private
12353     handleResponse : function(o, trans){
12354         this.trans = false;
12355         this.destroyTrans(trans, true);
12356         var result;
12357         try {
12358             result = trans.reader.readRecords(o);
12359         }catch(e){
12360             this.fireEvent("loadexception", this, o, trans.arg, e);
12361             trans.callback.call(trans.scope||window, null, trans.arg, false);
12362             return;
12363         }
12364         this.fireEvent("load", this, o, trans.arg);
12365         trans.callback.call(trans.scope||window, result, trans.arg, true);
12366     },
12367
12368     // private
12369     handleFailure : function(trans){
12370         this.trans = false;
12371         this.destroyTrans(trans, false);
12372         this.fireEvent("loadexception", this, null, trans.arg);
12373         trans.callback.call(trans.scope||window, null, trans.arg, false);
12374     }
12375 });/*
12376  * Based on:
12377  * Ext JS Library 1.1.1
12378  * Copyright(c) 2006-2007, Ext JS, LLC.
12379  *
12380  * Originally Released Under LGPL - original licence link has changed is not relivant.
12381  *
12382  * Fork - LGPL
12383  * <script type="text/javascript">
12384  */
12385
12386 /**
12387  * @class Roo.data.JsonReader
12388  * @extends Roo.data.DataReader
12389  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12390  * based on mappings in a provided Roo.data.Record constructor.
12391  * 
12392  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12393  * in the reply previously. 
12394  * 
12395  * <p>
12396  * Example code:
12397  * <pre><code>
12398 var RecordDef = Roo.data.Record.create([
12399     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12400     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12401 ]);
12402 var myReader = new Roo.data.JsonReader({
12403     totalProperty: "results",    // The property which contains the total dataset size (optional)
12404     root: "rows",                // The property which contains an Array of row objects
12405     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12406 }, RecordDef);
12407 </code></pre>
12408  * <p>
12409  * This would consume a JSON file like this:
12410  * <pre><code>
12411 { 'results': 2, 'rows': [
12412     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12413     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12414 }
12415 </code></pre>
12416  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12417  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12418  * paged from the remote server.
12419  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12420  * @cfg {String} root name of the property which contains the Array of row objects.
12421  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12422  * @cfg {Array} fields Array of field definition objects
12423  * @constructor
12424  * Create a new JsonReader
12425  * @param {Object} meta Metadata configuration options
12426  * @param {Object} recordType Either an Array of field definition objects,
12427  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12428  */
12429 Roo.data.JsonReader = function(meta, recordType){
12430     
12431     meta = meta || {};
12432     // set some defaults:
12433     Roo.applyIf(meta, {
12434         totalProperty: 'total',
12435         successProperty : 'success',
12436         root : 'data',
12437         id : 'id'
12438     });
12439     
12440     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12441 };
12442 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12443     
12444     /**
12445      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12446      * Used by Store query builder to append _requestMeta to params.
12447      * 
12448      */
12449     metaFromRemote : false,
12450     /**
12451      * This method is only used by a DataProxy which has retrieved data from a remote server.
12452      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12453      * @return {Object} data A data block which is used by an Roo.data.Store object as
12454      * a cache of Roo.data.Records.
12455      */
12456     read : function(response){
12457         var json = response.responseText;
12458        
12459         var o = /* eval:var:o */ eval("("+json+")");
12460         if(!o) {
12461             throw {message: "JsonReader.read: Json object not found"};
12462         }
12463         
12464         if(o.metaData){
12465             
12466             delete this.ef;
12467             this.metaFromRemote = true;
12468             this.meta = o.metaData;
12469             this.recordType = Roo.data.Record.create(o.metaData.fields);
12470             this.onMetaChange(this.meta, this.recordType, o);
12471         }
12472         return this.readRecords(o);
12473     },
12474
12475     // private function a store will implement
12476     onMetaChange : function(meta, recordType, o){
12477
12478     },
12479
12480     /**
12481          * @ignore
12482          */
12483     simpleAccess: function(obj, subsc) {
12484         return obj[subsc];
12485     },
12486
12487         /**
12488          * @ignore
12489          */
12490     getJsonAccessor: function(){
12491         var re = /[\[\.]/;
12492         return function(expr) {
12493             try {
12494                 return(re.test(expr))
12495                     ? new Function("obj", "return obj." + expr)
12496                     : function(obj){
12497                         return obj[expr];
12498                     };
12499             } catch(e){}
12500             return Roo.emptyFn;
12501         };
12502     }(),
12503
12504     /**
12505      * Create a data block containing Roo.data.Records from an XML document.
12506      * @param {Object} o An object which contains an Array of row objects in the property specified
12507      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12508      * which contains the total size of the dataset.
12509      * @return {Object} data A data block which is used by an Roo.data.Store object as
12510      * a cache of Roo.data.Records.
12511      */
12512     readRecords : function(o){
12513         /**
12514          * After any data loads, the raw JSON data is available for further custom processing.
12515          * @type Object
12516          */
12517         this.o = o;
12518         var s = this.meta, Record = this.recordType,
12519             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12520
12521 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12522         if (!this.ef) {
12523             if(s.totalProperty) {
12524                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12525                 }
12526                 if(s.successProperty) {
12527                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12528                 }
12529                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12530                 if (s.id) {
12531                         var g = this.getJsonAccessor(s.id);
12532                         this.getId = function(rec) {
12533                                 var r = g(rec);  
12534                                 return (r === undefined || r === "") ? null : r;
12535                         };
12536                 } else {
12537                         this.getId = function(){return null;};
12538                 }
12539             this.ef = [];
12540             for(var jj = 0; jj < fl; jj++){
12541                 f = fi[jj];
12542                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12543                 this.ef[jj] = this.getJsonAccessor(map);
12544             }
12545         }
12546
12547         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12548         if(s.totalProperty){
12549             var vt = parseInt(this.getTotal(o), 10);
12550             if(!isNaN(vt)){
12551                 totalRecords = vt;
12552             }
12553         }
12554         if(s.successProperty){
12555             var vs = this.getSuccess(o);
12556             if(vs === false || vs === 'false'){
12557                 success = false;
12558             }
12559         }
12560         var records = [];
12561         for(var i = 0; i < c; i++){
12562                 var n = root[i];
12563             var values = {};
12564             var id = this.getId(n);
12565             for(var j = 0; j < fl; j++){
12566                 f = fi[j];
12567             var v = this.ef[j](n);
12568             if (!f.convert) {
12569                 Roo.log('missing convert for ' + f.name);
12570                 Roo.log(f);
12571                 continue;
12572             }
12573             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12574             }
12575             var record = new Record(values, id);
12576             record.json = n;
12577             records[i] = record;
12578         }
12579         return {
12580             raw : o,
12581             success : success,
12582             records : records,
12583             totalRecords : totalRecords
12584         };
12585     }
12586 });/*
12587  * Based on:
12588  * Ext JS Library 1.1.1
12589  * Copyright(c) 2006-2007, Ext JS, LLC.
12590  *
12591  * Originally Released Under LGPL - original licence link has changed is not relivant.
12592  *
12593  * Fork - LGPL
12594  * <script type="text/javascript">
12595  */
12596
12597 /**
12598  * @class Roo.data.ArrayReader
12599  * @extends Roo.data.DataReader
12600  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12601  * Each element of that Array represents a row of data fields. The
12602  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12603  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12604  * <p>
12605  * Example code:.
12606  * <pre><code>
12607 var RecordDef = Roo.data.Record.create([
12608     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12609     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12610 ]);
12611 var myReader = new Roo.data.ArrayReader({
12612     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12613 }, RecordDef);
12614 </code></pre>
12615  * <p>
12616  * This would consume an Array like this:
12617  * <pre><code>
12618 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12619   </code></pre>
12620  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12621  * @constructor
12622  * Create a new JsonReader
12623  * @param {Object} meta Metadata configuration options.
12624  * @param {Object} recordType Either an Array of field definition objects
12625  * as specified to {@link Roo.data.Record#create},
12626  * or an {@link Roo.data.Record} object
12627  * created using {@link Roo.data.Record#create}.
12628  */
12629 Roo.data.ArrayReader = function(meta, recordType){
12630     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12631 };
12632
12633 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12634     /**
12635      * Create a data block containing Roo.data.Records from an XML document.
12636      * @param {Object} o An Array of row objects which represents the dataset.
12637      * @return {Object} data A data block which is used by an Roo.data.Store object as
12638      * a cache of Roo.data.Records.
12639      */
12640     readRecords : function(o){
12641         var sid = this.meta ? this.meta.id : null;
12642         var recordType = this.recordType, fields = recordType.prototype.fields;
12643         var records = [];
12644         var root = o;
12645             for(var i = 0; i < root.length; i++){
12646                     var n = root[i];
12647                 var values = {};
12648                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12649                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12650                 var f = fields.items[j];
12651                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12652                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12653                 v = f.convert(v);
12654                 values[f.name] = v;
12655             }
12656                 var record = new recordType(values, id);
12657                 record.json = n;
12658                 records[records.length] = record;
12659             }
12660             return {
12661                 records : records,
12662                 totalRecords : records.length
12663             };
12664     }
12665 });/*
12666  * - LGPL
12667  * * 
12668  */
12669
12670 /**
12671  * @class Roo.bootstrap.ComboBox
12672  * @extends Roo.bootstrap.TriggerField
12673  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12674  * @cfg {Boolean} append (true|false) default false
12675  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12676  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12677  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12678  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12679  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12680  * @cfg {Boolean} animate default true
12681  * @cfg {Boolean} emptyResultText only for touch device
12682  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12683  * @cfg {String} emptyTitle default ''
12684  * @constructor
12685  * Create a new ComboBox.
12686  * @param {Object} config Configuration options
12687  */
12688 Roo.bootstrap.ComboBox = function(config){
12689     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12690     this.addEvents({
12691         /**
12692          * @event expand
12693          * Fires when the dropdown list is expanded
12694         * @param {Roo.bootstrap.ComboBox} combo This combo box
12695         */
12696         'expand' : true,
12697         /**
12698          * @event collapse
12699          * Fires when the dropdown list is collapsed
12700         * @param {Roo.bootstrap.ComboBox} combo This combo box
12701         */
12702         'collapse' : true,
12703         /**
12704          * @event beforeselect
12705          * Fires before a list item is selected. Return false to cancel the selection.
12706         * @param {Roo.bootstrap.ComboBox} combo This combo box
12707         * @param {Roo.data.Record} record The data record returned from the underlying store
12708         * @param {Number} index The index of the selected item in the dropdown list
12709         */
12710         'beforeselect' : true,
12711         /**
12712          * @event select
12713          * Fires when a list item is selected
12714         * @param {Roo.bootstrap.ComboBox} combo This combo box
12715         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12716         * @param {Number} index The index of the selected item in the dropdown list
12717         */
12718         'select' : true,
12719         /**
12720          * @event beforequery
12721          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12722          * The event object passed has these properties:
12723         * @param {Roo.bootstrap.ComboBox} combo This combo box
12724         * @param {String} query The query
12725         * @param {Boolean} forceAll true to force "all" query
12726         * @param {Boolean} cancel true to cancel the query
12727         * @param {Object} e The query event object
12728         */
12729         'beforequery': true,
12730          /**
12731          * @event add
12732          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12733         * @param {Roo.bootstrap.ComboBox} combo This combo box
12734         */
12735         'add' : true,
12736         /**
12737          * @event edit
12738          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12739         * @param {Roo.bootstrap.ComboBox} combo This combo box
12740         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12741         */
12742         'edit' : true,
12743         /**
12744          * @event remove
12745          * Fires when the remove value from the combobox array
12746         * @param {Roo.bootstrap.ComboBox} combo This combo box
12747         */
12748         'remove' : true,
12749         /**
12750          * @event afterremove
12751          * Fires when the remove value from the combobox array
12752         * @param {Roo.bootstrap.ComboBox} combo This combo box
12753         */
12754         'afterremove' : true,
12755         /**
12756          * @event specialfilter
12757          * Fires when specialfilter
12758             * @param {Roo.bootstrap.ComboBox} combo This combo box
12759             */
12760         'specialfilter' : true,
12761         /**
12762          * @event tick
12763          * Fires when tick the element
12764             * @param {Roo.bootstrap.ComboBox} combo This combo box
12765             */
12766         'tick' : true,
12767         /**
12768          * @event touchviewdisplay
12769          * Fires when touch view require special display (default is using displayField)
12770             * @param {Roo.bootstrap.ComboBox} combo This combo box
12771             * @param {Object} cfg set html .
12772             */
12773         'touchviewdisplay' : true
12774         
12775     });
12776     
12777     this.item = [];
12778     this.tickItems = [];
12779     
12780     this.selectedIndex = -1;
12781     if(this.mode == 'local'){
12782         if(config.queryDelay === undefined){
12783             this.queryDelay = 10;
12784         }
12785         if(config.minChars === undefined){
12786             this.minChars = 0;
12787         }
12788     }
12789 };
12790
12791 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12792      
12793     /**
12794      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12795      * rendering into an Roo.Editor, defaults to false)
12796      */
12797     /**
12798      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12799      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12800      */
12801     /**
12802      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12803      */
12804     /**
12805      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12806      * the dropdown list (defaults to undefined, with no header element)
12807      */
12808
12809      /**
12810      * @cfg {String/Roo.Template} tpl The template to use to render the output
12811      */
12812      
12813      /**
12814      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12815      */
12816     listWidth: undefined,
12817     /**
12818      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12819      * mode = 'remote' or 'text' if mode = 'local')
12820      */
12821     displayField: undefined,
12822     
12823     /**
12824      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12825      * mode = 'remote' or 'value' if mode = 'local'). 
12826      * Note: use of a valueField requires the user make a selection
12827      * in order for a value to be mapped.
12828      */
12829     valueField: undefined,
12830     /**
12831      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12832      */
12833     modalTitle : '',
12834     
12835     /**
12836      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12837      * field's data value (defaults to the underlying DOM element's name)
12838      */
12839     hiddenName: undefined,
12840     /**
12841      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12842      */
12843     listClass: '',
12844     /**
12845      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12846      */
12847     selectedClass: 'active',
12848     
12849     /**
12850      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12851      */
12852     shadow:'sides',
12853     /**
12854      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12855      * anchor positions (defaults to 'tl-bl')
12856      */
12857     listAlign: 'tl-bl?',
12858     /**
12859      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12860      */
12861     maxHeight: 300,
12862     /**
12863      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12864      * query specified by the allQuery config option (defaults to 'query')
12865      */
12866     triggerAction: 'query',
12867     /**
12868      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12869      * (defaults to 4, does not apply if editable = false)
12870      */
12871     minChars : 4,
12872     /**
12873      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12874      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12875      */
12876     typeAhead: false,
12877     /**
12878      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12879      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12880      */
12881     queryDelay: 500,
12882     /**
12883      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12884      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12885      */
12886     pageSize: 0,
12887     /**
12888      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12889      * when editable = true (defaults to false)
12890      */
12891     selectOnFocus:false,
12892     /**
12893      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12894      */
12895     queryParam: 'query',
12896     /**
12897      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12898      * when mode = 'remote' (defaults to 'Loading...')
12899      */
12900     loadingText: 'Loading...',
12901     /**
12902      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12903      */
12904     resizable: false,
12905     /**
12906      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12907      */
12908     handleHeight : 8,
12909     /**
12910      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12911      * traditional select (defaults to true)
12912      */
12913     editable: true,
12914     /**
12915      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12916      */
12917     allQuery: '',
12918     /**
12919      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12920      */
12921     mode: 'remote',
12922     /**
12923      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12924      * listWidth has a higher value)
12925      */
12926     minListWidth : 70,
12927     /**
12928      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12929      * allow the user to set arbitrary text into the field (defaults to false)
12930      */
12931     forceSelection:false,
12932     /**
12933      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12934      * if typeAhead = true (defaults to 250)
12935      */
12936     typeAheadDelay : 250,
12937     /**
12938      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12939      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12940      */
12941     valueNotFoundText : undefined,
12942     /**
12943      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12944      */
12945     blockFocus : false,
12946     
12947     /**
12948      * @cfg {Boolean} disableClear Disable showing of clear button.
12949      */
12950     disableClear : false,
12951     /**
12952      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12953      */
12954     alwaysQuery : false,
12955     
12956     /**
12957      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12958      */
12959     multiple : false,
12960     
12961     /**
12962      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12963      */
12964     invalidClass : "has-warning",
12965     
12966     /**
12967      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12968      */
12969     validClass : "has-success",
12970     
12971     /**
12972      * @cfg {Boolean} specialFilter (true|false) special filter default false
12973      */
12974     specialFilter : false,
12975     
12976     /**
12977      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12978      */
12979     mobileTouchView : true,
12980     
12981     /**
12982      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12983      */
12984     useNativeIOS : false,
12985     
12986     ios_options : false,
12987     
12988     //private
12989     addicon : false,
12990     editicon: false,
12991     
12992     page: 0,
12993     hasQuery: false,
12994     append: false,
12995     loadNext: false,
12996     autoFocus : true,
12997     tickable : false,
12998     btnPosition : 'right',
12999     triggerList : true,
13000     showToggleBtn : true,
13001     animate : true,
13002     emptyResultText: 'Empty',
13003     triggerText : 'Select',
13004     emptyTitle : '',
13005     
13006     // element that contains real text value.. (when hidden is used..)
13007     
13008     getAutoCreate : function()
13009     {   
13010         var cfg = false;
13011         //render
13012         /*
13013          * Render classic select for iso
13014          */
13015         
13016         if(Roo.isIOS && this.useNativeIOS){
13017             cfg = this.getAutoCreateNativeIOS();
13018             return cfg;
13019         }
13020         
13021         /*
13022          * Touch Devices
13023          */
13024         
13025         if(Roo.isTouch && this.mobileTouchView){
13026             cfg = this.getAutoCreateTouchView();
13027             return cfg;;
13028         }
13029         
13030         /*
13031          *  Normal ComboBox
13032          */
13033         if(!this.tickable){
13034             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13035             return cfg;
13036         }
13037         
13038         /*
13039          *  ComboBox with tickable selections
13040          */
13041              
13042         var align = this.labelAlign || this.parentLabelAlign();
13043         
13044         cfg = {
13045             cls : 'form-group roo-combobox-tickable' //input-group
13046         };
13047         
13048         var btn_text_select = '';
13049         var btn_text_done = '';
13050         var btn_text_cancel = '';
13051         
13052         if (this.btn_text_show) {
13053             btn_text_select = 'Select';
13054             btn_text_done = 'Done';
13055             btn_text_cancel = 'Cancel'; 
13056         }
13057         
13058         var buttons = {
13059             tag : 'div',
13060             cls : 'tickable-buttons',
13061             cn : [
13062                 {
13063                     tag : 'button',
13064                     type : 'button',
13065                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13066                     //html : this.triggerText
13067                     html: btn_text_select
13068                 },
13069                 {
13070                     tag : 'button',
13071                     type : 'button',
13072                     name : 'ok',
13073                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13074                     //html : 'Done'
13075                     html: btn_text_done
13076                 },
13077                 {
13078                     tag : 'button',
13079                     type : 'button',
13080                     name : 'cancel',
13081                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13082                     //html : 'Cancel'
13083                     html: btn_text_cancel
13084                 }
13085             ]
13086         };
13087         
13088         if(this.editable){
13089             buttons.cn.unshift({
13090                 tag: 'input',
13091                 cls: 'roo-select2-search-field-input'
13092             });
13093         }
13094         
13095         var _this = this;
13096         
13097         Roo.each(buttons.cn, function(c){
13098             if (_this.size) {
13099                 c.cls += ' btn-' + _this.size;
13100             }
13101
13102             if (_this.disabled) {
13103                 c.disabled = true;
13104             }
13105         });
13106         
13107         var box = {
13108             tag: 'div',
13109             cn: [
13110                 {
13111                     tag: 'input',
13112                     type : 'hidden',
13113                     cls: 'form-hidden-field'
13114                 },
13115                 {
13116                     tag: 'ul',
13117                     cls: 'roo-select2-choices',
13118                     cn:[
13119                         {
13120                             tag: 'li',
13121                             cls: 'roo-select2-search-field',
13122                             cn: [
13123                                 buttons
13124                             ]
13125                         }
13126                     ]
13127                 }
13128             ]
13129         };
13130         
13131         var combobox = {
13132             cls: 'roo-select2-container input-group roo-select2-container-multi',
13133             cn: [
13134                 box
13135 //                {
13136 //                    tag: 'ul',
13137 //                    cls: 'typeahead typeahead-long dropdown-menu',
13138 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13139 //                }
13140             ]
13141         };
13142         
13143         if(this.hasFeedback && !this.allowBlank){
13144             
13145             var feedback = {
13146                 tag: 'span',
13147                 cls: 'glyphicon form-control-feedback'
13148             };
13149
13150             combobox.cn.push(feedback);
13151         }
13152         
13153         
13154         if (align ==='left' && this.fieldLabel.length) {
13155             
13156             cfg.cls += ' roo-form-group-label-left';
13157             
13158             cfg.cn = [
13159                 {
13160                     tag : 'i',
13161                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13162                     tooltip : 'This field is required'
13163                 },
13164                 {
13165                     tag: 'label',
13166                     'for' :  id,
13167                     cls : 'control-label',
13168                     html : this.fieldLabel
13169
13170                 },
13171                 {
13172                     cls : "", 
13173                     cn: [
13174                         combobox
13175                     ]
13176                 }
13177
13178             ];
13179             
13180             var labelCfg = cfg.cn[1];
13181             var contentCfg = cfg.cn[2];
13182             
13183
13184             if(this.indicatorpos == 'right'){
13185                 
13186                 cfg.cn = [
13187                     {
13188                         tag: 'label',
13189                         'for' :  id,
13190                         cls : 'control-label',
13191                         cn : [
13192                             {
13193                                 tag : 'span',
13194                                 html : this.fieldLabel
13195                             },
13196                             {
13197                                 tag : 'i',
13198                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13199                                 tooltip : 'This field is required'
13200                             }
13201                         ]
13202                     },
13203                     {
13204                         cls : "",
13205                         cn: [
13206                             combobox
13207                         ]
13208                     }
13209
13210                 ];
13211                 
13212                 
13213                 
13214                 labelCfg = cfg.cn[0];
13215                 contentCfg = cfg.cn[1];
13216             
13217             }
13218             
13219             if(this.labelWidth > 12){
13220                 labelCfg.style = "width: " + this.labelWidth + 'px';
13221             }
13222             
13223             if(this.labelWidth < 13 && this.labelmd == 0){
13224                 this.labelmd = this.labelWidth;
13225             }
13226             
13227             if(this.labellg > 0){
13228                 labelCfg.cls += ' col-lg-' + this.labellg;
13229                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13230             }
13231             
13232             if(this.labelmd > 0){
13233                 labelCfg.cls += ' col-md-' + this.labelmd;
13234                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13235             }
13236             
13237             if(this.labelsm > 0){
13238                 labelCfg.cls += ' col-sm-' + this.labelsm;
13239                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13240             }
13241             
13242             if(this.labelxs > 0){
13243                 labelCfg.cls += ' col-xs-' + this.labelxs;
13244                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13245             }
13246                 
13247                 
13248         } else if ( this.fieldLabel.length) {
13249 //                Roo.log(" label");
13250                  cfg.cn = [
13251                     {
13252                         tag : 'i',
13253                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13254                         tooltip : 'This field is required'
13255                     },
13256                     {
13257                         tag: 'label',
13258                         //cls : 'input-group-addon',
13259                         html : this.fieldLabel
13260                     },
13261                     combobox
13262                 ];
13263                 
13264                 if(this.indicatorpos == 'right'){
13265                     cfg.cn = [
13266                         {
13267                             tag: 'label',
13268                             //cls : 'input-group-addon',
13269                             html : this.fieldLabel
13270                         },
13271                         {
13272                             tag : 'i',
13273                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13274                             tooltip : 'This field is required'
13275                         },
13276                         combobox
13277                     ];
13278                     
13279                 }
13280
13281         } else {
13282             
13283 //                Roo.log(" no label && no align");
13284                 cfg = combobox
13285                      
13286                 
13287         }
13288          
13289         var settings=this;
13290         ['xs','sm','md','lg'].map(function(size){
13291             if (settings[size]) {
13292                 cfg.cls += ' col-' + size + '-' + settings[size];
13293             }
13294         });
13295         
13296         return cfg;
13297         
13298     },
13299     
13300     _initEventsCalled : false,
13301     
13302     // private
13303     initEvents: function()
13304     {   
13305         if (this._initEventsCalled) { // as we call render... prevent looping...
13306             return;
13307         }
13308         this._initEventsCalled = true;
13309         
13310         if (!this.store) {
13311             throw "can not find store for combo";
13312         }
13313         
13314         this.indicator = this.indicatorEl();
13315         
13316         this.store = Roo.factory(this.store, Roo.data);
13317         this.store.parent = this;
13318         
13319         // if we are building from html. then this element is so complex, that we can not really
13320         // use the rendered HTML.
13321         // so we have to trash and replace the previous code.
13322         if (Roo.XComponent.build_from_html) {
13323             // remove this element....
13324             var e = this.el.dom, k=0;
13325             while (e ) { e = e.previousSibling;  ++k;}
13326
13327             this.el.remove();
13328             
13329             this.el=false;
13330             this.rendered = false;
13331             
13332             this.render(this.parent().getChildContainer(true), k);
13333         }
13334         
13335         if(Roo.isIOS && this.useNativeIOS){
13336             this.initIOSView();
13337             return;
13338         }
13339         
13340         /*
13341          * Touch Devices
13342          */
13343         
13344         if(Roo.isTouch && this.mobileTouchView){
13345             this.initTouchView();
13346             return;
13347         }
13348         
13349         if(this.tickable){
13350             this.initTickableEvents();
13351             return;
13352         }
13353         
13354         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13355         
13356         if(this.hiddenName){
13357             
13358             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13359             
13360             this.hiddenField.dom.value =
13361                 this.hiddenValue !== undefined ? this.hiddenValue :
13362                 this.value !== undefined ? this.value : '';
13363
13364             // prevent input submission
13365             this.el.dom.removeAttribute('name');
13366             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13367              
13368              
13369         }
13370         //if(Roo.isGecko){
13371         //    this.el.dom.setAttribute('autocomplete', 'off');
13372         //}
13373         
13374         var cls = 'x-combo-list';
13375         
13376         //this.list = new Roo.Layer({
13377         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13378         //});
13379         
13380         var _this = this;
13381         
13382         (function(){
13383             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13384             _this.list.setWidth(lw);
13385         }).defer(100);
13386         
13387         this.list.on('mouseover', this.onViewOver, this);
13388         this.list.on('mousemove', this.onViewMove, this);
13389         this.list.on('scroll', this.onViewScroll, this);
13390         
13391         /*
13392         this.list.swallowEvent('mousewheel');
13393         this.assetHeight = 0;
13394
13395         if(this.title){
13396             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13397             this.assetHeight += this.header.getHeight();
13398         }
13399
13400         this.innerList = this.list.createChild({cls:cls+'-inner'});
13401         this.innerList.on('mouseover', this.onViewOver, this);
13402         this.innerList.on('mousemove', this.onViewMove, this);
13403         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13404         
13405         if(this.allowBlank && !this.pageSize && !this.disableClear){
13406             this.footer = this.list.createChild({cls:cls+'-ft'});
13407             this.pageTb = new Roo.Toolbar(this.footer);
13408            
13409         }
13410         if(this.pageSize){
13411             this.footer = this.list.createChild({cls:cls+'-ft'});
13412             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13413                     {pageSize: this.pageSize});
13414             
13415         }
13416         
13417         if (this.pageTb && this.allowBlank && !this.disableClear) {
13418             var _this = this;
13419             this.pageTb.add(new Roo.Toolbar.Fill(), {
13420                 cls: 'x-btn-icon x-btn-clear',
13421                 text: '&#160;',
13422                 handler: function()
13423                 {
13424                     _this.collapse();
13425                     _this.clearValue();
13426                     _this.onSelect(false, -1);
13427                 }
13428             });
13429         }
13430         if (this.footer) {
13431             this.assetHeight += this.footer.getHeight();
13432         }
13433         */
13434             
13435         if(!this.tpl){
13436             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13437         }
13438
13439         this.view = new Roo.View(this.list, this.tpl, {
13440             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13441         });
13442         //this.view.wrapEl.setDisplayed(false);
13443         this.view.on('click', this.onViewClick, this);
13444         
13445         
13446         this.store.on('beforeload', this.onBeforeLoad, this);
13447         this.store.on('load', this.onLoad, this);
13448         this.store.on('loadexception', this.onLoadException, this);
13449         /*
13450         if(this.resizable){
13451             this.resizer = new Roo.Resizable(this.list,  {
13452                pinned:true, handles:'se'
13453             });
13454             this.resizer.on('resize', function(r, w, h){
13455                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13456                 this.listWidth = w;
13457                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13458                 this.restrictHeight();
13459             }, this);
13460             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13461         }
13462         */
13463         if(!this.editable){
13464             this.editable = true;
13465             this.setEditable(false);
13466         }
13467         
13468         /*
13469         
13470         if (typeof(this.events.add.listeners) != 'undefined') {
13471             
13472             this.addicon = this.wrap.createChild(
13473                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13474        
13475             this.addicon.on('click', function(e) {
13476                 this.fireEvent('add', this);
13477             }, this);
13478         }
13479         if (typeof(this.events.edit.listeners) != 'undefined') {
13480             
13481             this.editicon = this.wrap.createChild(
13482                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13483             if (this.addicon) {
13484                 this.editicon.setStyle('margin-left', '40px');
13485             }
13486             this.editicon.on('click', function(e) {
13487                 
13488                 // we fire even  if inothing is selected..
13489                 this.fireEvent('edit', this, this.lastData );
13490                 
13491             }, this);
13492         }
13493         */
13494         
13495         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13496             "up" : function(e){
13497                 this.inKeyMode = true;
13498                 this.selectPrev();
13499             },
13500
13501             "down" : function(e){
13502                 if(!this.isExpanded()){
13503                     this.onTriggerClick();
13504                 }else{
13505                     this.inKeyMode = true;
13506                     this.selectNext();
13507                 }
13508             },
13509
13510             "enter" : function(e){
13511 //                this.onViewClick();
13512                 //return true;
13513                 this.collapse();
13514                 
13515                 if(this.fireEvent("specialkey", this, e)){
13516                     this.onViewClick(false);
13517                 }
13518                 
13519                 return true;
13520             },
13521
13522             "esc" : function(e){
13523                 this.collapse();
13524             },
13525
13526             "tab" : function(e){
13527                 this.collapse();
13528                 
13529                 if(this.fireEvent("specialkey", this, e)){
13530                     this.onViewClick(false);
13531                 }
13532                 
13533                 return true;
13534             },
13535
13536             scope : this,
13537
13538             doRelay : function(foo, bar, hname){
13539                 if(hname == 'down' || this.scope.isExpanded()){
13540                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13541                 }
13542                 return true;
13543             },
13544
13545             forceKeyDown: true
13546         });
13547         
13548         
13549         this.queryDelay = Math.max(this.queryDelay || 10,
13550                 this.mode == 'local' ? 10 : 250);
13551         
13552         
13553         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13554         
13555         if(this.typeAhead){
13556             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13557         }
13558         if(this.editable !== false){
13559             this.inputEl().on("keyup", this.onKeyUp, this);
13560         }
13561         if(this.forceSelection){
13562             this.inputEl().on('blur', this.doForce, this);
13563         }
13564         
13565         if(this.multiple){
13566             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13567             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13568         }
13569     },
13570     
13571     initTickableEvents: function()
13572     {   
13573         this.createList();
13574         
13575         if(this.hiddenName){
13576             
13577             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13578             
13579             this.hiddenField.dom.value =
13580                 this.hiddenValue !== undefined ? this.hiddenValue :
13581                 this.value !== undefined ? this.value : '';
13582
13583             // prevent input submission
13584             this.el.dom.removeAttribute('name');
13585             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13586              
13587              
13588         }
13589         
13590 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13591         
13592         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13593         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13594         if(this.triggerList){
13595             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13596         }
13597          
13598         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13599         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13600         
13601         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13602         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13603         
13604         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13605         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13606         
13607         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13608         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13609         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13610         
13611         this.okBtn.hide();
13612         this.cancelBtn.hide();
13613         
13614         var _this = this;
13615         
13616         (function(){
13617             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13618             _this.list.setWidth(lw);
13619         }).defer(100);
13620         
13621         this.list.on('mouseover', this.onViewOver, this);
13622         this.list.on('mousemove', this.onViewMove, this);
13623         
13624         this.list.on('scroll', this.onViewScroll, this);
13625         
13626         if(!this.tpl){
13627             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13628                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13629         }
13630
13631         this.view = new Roo.View(this.list, this.tpl, {
13632             singleSelect:true,
13633             tickable:true,
13634             parent:this,
13635             store: this.store,
13636             selectedClass: this.selectedClass
13637         });
13638         
13639         //this.view.wrapEl.setDisplayed(false);
13640         this.view.on('click', this.onViewClick, this);
13641         
13642         
13643         
13644         this.store.on('beforeload', this.onBeforeLoad, this);
13645         this.store.on('load', this.onLoad, this);
13646         this.store.on('loadexception', this.onLoadException, this);
13647         
13648         if(this.editable){
13649             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13650                 "up" : function(e){
13651                     this.inKeyMode = true;
13652                     this.selectPrev();
13653                 },
13654
13655                 "down" : function(e){
13656                     this.inKeyMode = true;
13657                     this.selectNext();
13658                 },
13659
13660                 "enter" : function(e){
13661                     if(this.fireEvent("specialkey", this, e)){
13662                         this.onViewClick(false);
13663                     }
13664                     
13665                     return true;
13666                 },
13667
13668                 "esc" : function(e){
13669                     this.onTickableFooterButtonClick(e, false, false);
13670                 },
13671
13672                 "tab" : function(e){
13673                     this.fireEvent("specialkey", this, e);
13674                     
13675                     this.onTickableFooterButtonClick(e, false, false);
13676                     
13677                     return true;
13678                 },
13679
13680                 scope : this,
13681
13682                 doRelay : function(e, fn, key){
13683                     if(this.scope.isExpanded()){
13684                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13685                     }
13686                     return true;
13687                 },
13688
13689                 forceKeyDown: true
13690             });
13691         }
13692         
13693         this.queryDelay = Math.max(this.queryDelay || 10,
13694                 this.mode == 'local' ? 10 : 250);
13695         
13696         
13697         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13698         
13699         if(this.typeAhead){
13700             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13701         }
13702         
13703         if(this.editable !== false){
13704             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13705         }
13706         
13707         this.indicator = this.indicatorEl();
13708         
13709         if(this.indicator){
13710             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13711             this.indicator.hide();
13712         }
13713         
13714     },
13715
13716     onDestroy : function(){
13717         if(this.view){
13718             this.view.setStore(null);
13719             this.view.el.removeAllListeners();
13720             this.view.el.remove();
13721             this.view.purgeListeners();
13722         }
13723         if(this.list){
13724             this.list.dom.innerHTML  = '';
13725         }
13726         
13727         if(this.store){
13728             this.store.un('beforeload', this.onBeforeLoad, this);
13729             this.store.un('load', this.onLoad, this);
13730             this.store.un('loadexception', this.onLoadException, this);
13731         }
13732         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13733     },
13734
13735     // private
13736     fireKey : function(e){
13737         if(e.isNavKeyPress() && !this.list.isVisible()){
13738             this.fireEvent("specialkey", this, e);
13739         }
13740     },
13741
13742     // private
13743     onResize: function(w, h){
13744 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13745 //        
13746 //        if(typeof w != 'number'){
13747 //            // we do not handle it!?!?
13748 //            return;
13749 //        }
13750 //        var tw = this.trigger.getWidth();
13751 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13752 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13753 //        var x = w - tw;
13754 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13755 //            
13756 //        //this.trigger.setStyle('left', x+'px');
13757 //        
13758 //        if(this.list && this.listWidth === undefined){
13759 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13760 //            this.list.setWidth(lw);
13761 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13762 //        }
13763         
13764     
13765         
13766     },
13767
13768     /**
13769      * Allow or prevent the user from directly editing the field text.  If false is passed,
13770      * the user will only be able to select from the items defined in the dropdown list.  This method
13771      * is the runtime equivalent of setting the 'editable' config option at config time.
13772      * @param {Boolean} value True to allow the user to directly edit the field text
13773      */
13774     setEditable : function(value){
13775         if(value == this.editable){
13776             return;
13777         }
13778         this.editable = value;
13779         if(!value){
13780             this.inputEl().dom.setAttribute('readOnly', true);
13781             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13782             this.inputEl().addClass('x-combo-noedit');
13783         }else{
13784             this.inputEl().dom.setAttribute('readOnly', false);
13785             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13786             this.inputEl().removeClass('x-combo-noedit');
13787         }
13788     },
13789
13790     // private
13791     
13792     onBeforeLoad : function(combo,opts){
13793         if(!this.hasFocus){
13794             return;
13795         }
13796          if (!opts.add) {
13797             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13798          }
13799         this.restrictHeight();
13800         this.selectedIndex = -1;
13801     },
13802
13803     // private
13804     onLoad : function(){
13805         
13806         this.hasQuery = false;
13807         
13808         if(!this.hasFocus){
13809             return;
13810         }
13811         
13812         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13813             this.loading.hide();
13814         }
13815         
13816         if(this.store.getCount() > 0){
13817             
13818             this.expand();
13819             this.restrictHeight();
13820             if(this.lastQuery == this.allQuery){
13821                 if(this.editable && !this.tickable){
13822                     this.inputEl().dom.select();
13823                 }
13824                 
13825                 if(
13826                     !this.selectByValue(this.value, true) &&
13827                     this.autoFocus && 
13828                     (
13829                         !this.store.lastOptions ||
13830                         typeof(this.store.lastOptions.add) == 'undefined' || 
13831                         this.store.lastOptions.add != true
13832                     )
13833                 ){
13834                     this.select(0, true);
13835                 }
13836             }else{
13837                 if(this.autoFocus){
13838                     this.selectNext();
13839                 }
13840                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13841                     this.taTask.delay(this.typeAheadDelay);
13842                 }
13843             }
13844         }else{
13845             this.onEmptyResults();
13846         }
13847         
13848         //this.el.focus();
13849     },
13850     // private
13851     onLoadException : function()
13852     {
13853         this.hasQuery = false;
13854         
13855         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13856             this.loading.hide();
13857         }
13858         
13859         if(this.tickable && this.editable){
13860             return;
13861         }
13862         
13863         this.collapse();
13864         // only causes errors at present
13865         //Roo.log(this.store.reader.jsonData);
13866         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13867             // fixme
13868             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13869         //}
13870         
13871         
13872     },
13873     // private
13874     onTypeAhead : function(){
13875         if(this.store.getCount() > 0){
13876             var r = this.store.getAt(0);
13877             var newValue = r.data[this.displayField];
13878             var len = newValue.length;
13879             var selStart = this.getRawValue().length;
13880             
13881             if(selStart != len){
13882                 this.setRawValue(newValue);
13883                 this.selectText(selStart, newValue.length);
13884             }
13885         }
13886     },
13887
13888     // private
13889     onSelect : function(record, index){
13890         
13891         if(this.fireEvent('beforeselect', this, record, index) !== false){
13892         
13893             this.setFromData(index > -1 ? record.data : false);
13894             
13895             this.collapse();
13896             this.fireEvent('select', this, record, index);
13897         }
13898     },
13899
13900     /**
13901      * Returns the currently selected field value or empty string if no value is set.
13902      * @return {String} value The selected value
13903      */
13904     getValue : function()
13905     {
13906         if(Roo.isIOS && this.useNativeIOS){
13907             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13908         }
13909         
13910         if(this.multiple){
13911             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13912         }
13913         
13914         if(this.valueField){
13915             return typeof this.value != 'undefined' ? this.value : '';
13916         }else{
13917             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13918         }
13919     },
13920     
13921     getRawValue : function()
13922     {
13923         if(Roo.isIOS && this.useNativeIOS){
13924             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13925         }
13926         
13927         var v = this.inputEl().getValue();
13928         
13929         return v;
13930     },
13931
13932     /**
13933      * Clears any text/value currently set in the field
13934      */
13935     clearValue : function(){
13936         
13937         if(this.hiddenField){
13938             this.hiddenField.dom.value = '';
13939         }
13940         this.value = '';
13941         this.setRawValue('');
13942         this.lastSelectionText = '';
13943         this.lastData = false;
13944         
13945         var close = this.closeTriggerEl();
13946         
13947         if(close){
13948             close.hide();
13949         }
13950         
13951         this.validate();
13952         
13953     },
13954
13955     /**
13956      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13957      * will be displayed in the field.  If the value does not match the data value of an existing item,
13958      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13959      * Otherwise the field will be blank (although the value will still be set).
13960      * @param {String} value The value to match
13961      */
13962     setValue : function(v)
13963     {
13964         if(Roo.isIOS && this.useNativeIOS){
13965             this.setIOSValue(v);
13966             return;
13967         }
13968         
13969         if(this.multiple){
13970             this.syncValue();
13971             return;
13972         }
13973         
13974         var text = v;
13975         if(this.valueField){
13976             var r = this.findRecord(this.valueField, v);
13977             if(r){
13978                 text = r.data[this.displayField];
13979             }else if(this.valueNotFoundText !== undefined){
13980                 text = this.valueNotFoundText;
13981             }
13982         }
13983         this.lastSelectionText = text;
13984         if(this.hiddenField){
13985             this.hiddenField.dom.value = v;
13986         }
13987         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13988         this.value = v;
13989         
13990         var close = this.closeTriggerEl();
13991         
13992         if(close){
13993             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13994         }
13995         
13996         this.validate();
13997     },
13998     /**
13999      * @property {Object} the last set data for the element
14000      */
14001     
14002     lastData : false,
14003     /**
14004      * Sets the value of the field based on a object which is related to the record format for the store.
14005      * @param {Object} value the value to set as. or false on reset?
14006      */
14007     setFromData : function(o){
14008         
14009         if(this.multiple){
14010             this.addItem(o);
14011             return;
14012         }
14013             
14014         var dv = ''; // display value
14015         var vv = ''; // value value..
14016         this.lastData = o;
14017         if (this.displayField) {
14018             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14019         } else {
14020             // this is an error condition!!!
14021             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14022         }
14023         
14024         if(this.valueField){
14025             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14026         }
14027         
14028         var close = this.closeTriggerEl();
14029         
14030         if(close){
14031             if(dv.length || vv * 1 > 0){
14032                 close.show() ;
14033                 this.blockFocus=true;
14034             } else {
14035                 close.hide();
14036             }             
14037         }
14038         
14039         if(this.hiddenField){
14040             this.hiddenField.dom.value = vv;
14041             
14042             this.lastSelectionText = dv;
14043             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14044             this.value = vv;
14045             return;
14046         }
14047         // no hidden field.. - we store the value in 'value', but still display
14048         // display field!!!!
14049         this.lastSelectionText = dv;
14050         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14051         this.value = vv;
14052         
14053         
14054         
14055     },
14056     // private
14057     reset : function(){
14058         // overridden so that last data is reset..
14059         
14060         if(this.multiple){
14061             this.clearItem();
14062             return;
14063         }
14064         
14065         this.setValue(this.originalValue);
14066         //this.clearInvalid();
14067         this.lastData = false;
14068         if (this.view) {
14069             this.view.clearSelections();
14070         }
14071         
14072         this.validate();
14073     },
14074     // private
14075     findRecord : function(prop, value){
14076         var record;
14077         if(this.store.getCount() > 0){
14078             this.store.each(function(r){
14079                 if(r.data[prop] == value){
14080                     record = r;
14081                     return false;
14082                 }
14083                 return true;
14084             });
14085         }
14086         return record;
14087     },
14088     
14089     getName: function()
14090     {
14091         // returns hidden if it's set..
14092         if (!this.rendered) {return ''};
14093         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14094         
14095     },
14096     // private
14097     onViewMove : function(e, t){
14098         this.inKeyMode = false;
14099     },
14100
14101     // private
14102     onViewOver : function(e, t){
14103         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14104             return;
14105         }
14106         var item = this.view.findItemFromChild(t);
14107         
14108         if(item){
14109             var index = this.view.indexOf(item);
14110             this.select(index, false);
14111         }
14112     },
14113
14114     // private
14115     onViewClick : function(view, doFocus, el, e)
14116     {
14117         var index = this.view.getSelectedIndexes()[0];
14118         
14119         var r = this.store.getAt(index);
14120         
14121         if(this.tickable){
14122             
14123             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14124                 return;
14125             }
14126             
14127             var rm = false;
14128             var _this = this;
14129             
14130             Roo.each(this.tickItems, function(v,k){
14131                 
14132                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14133                     Roo.log(v);
14134                     _this.tickItems.splice(k, 1);
14135                     
14136                     if(typeof(e) == 'undefined' && view == false){
14137                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14138                     }
14139                     
14140                     rm = true;
14141                     return;
14142                 }
14143             });
14144             
14145             if(rm){
14146                 return;
14147             }
14148             
14149             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14150                 this.tickItems.push(r.data);
14151             }
14152             
14153             if(typeof(e) == 'undefined' && view == false){
14154                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14155             }
14156                     
14157             return;
14158         }
14159         
14160         if(r){
14161             this.onSelect(r, index);
14162         }
14163         if(doFocus !== false && !this.blockFocus){
14164             this.inputEl().focus();
14165         }
14166     },
14167
14168     // private
14169     restrictHeight : function(){
14170         //this.innerList.dom.style.height = '';
14171         //var inner = this.innerList.dom;
14172         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14173         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14174         //this.list.beginUpdate();
14175         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14176         this.list.alignTo(this.inputEl(), this.listAlign);
14177         this.list.alignTo(this.inputEl(), this.listAlign);
14178         //this.list.endUpdate();
14179     },
14180
14181     // private
14182     onEmptyResults : function(){
14183         
14184         if(this.tickable && this.editable){
14185             this.hasFocus = false;
14186             this.restrictHeight();
14187             return;
14188         }
14189         
14190         this.collapse();
14191     },
14192
14193     /**
14194      * Returns true if the dropdown list is expanded, else false.
14195      */
14196     isExpanded : function(){
14197         return this.list.isVisible();
14198     },
14199
14200     /**
14201      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14202      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14203      * @param {String} value The data value of the item to select
14204      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14205      * selected item if it is not currently in view (defaults to true)
14206      * @return {Boolean} True if the value matched an item in the list, else false
14207      */
14208     selectByValue : function(v, scrollIntoView){
14209         if(v !== undefined && v !== null){
14210             var r = this.findRecord(this.valueField || this.displayField, v);
14211             if(r){
14212                 this.select(this.store.indexOf(r), scrollIntoView);
14213                 return true;
14214             }
14215         }
14216         return false;
14217     },
14218
14219     /**
14220      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14221      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14222      * @param {Number} index The zero-based index of the list item to select
14223      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14224      * selected item if it is not currently in view (defaults to true)
14225      */
14226     select : function(index, scrollIntoView){
14227         this.selectedIndex = index;
14228         this.view.select(index);
14229         if(scrollIntoView !== false){
14230             var el = this.view.getNode(index);
14231             /*
14232              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14233              */
14234             if(el){
14235                 this.list.scrollChildIntoView(el, false);
14236             }
14237         }
14238     },
14239
14240     // private
14241     selectNext : function(){
14242         var ct = this.store.getCount();
14243         if(ct > 0){
14244             if(this.selectedIndex == -1){
14245                 this.select(0);
14246             }else if(this.selectedIndex < ct-1){
14247                 this.select(this.selectedIndex+1);
14248             }
14249         }
14250     },
14251
14252     // private
14253     selectPrev : function(){
14254         var ct = this.store.getCount();
14255         if(ct > 0){
14256             if(this.selectedIndex == -1){
14257                 this.select(0);
14258             }else if(this.selectedIndex != 0){
14259                 this.select(this.selectedIndex-1);
14260             }
14261         }
14262     },
14263
14264     // private
14265     onKeyUp : function(e){
14266         if(this.editable !== false && !e.isSpecialKey()){
14267             this.lastKey = e.getKey();
14268             this.dqTask.delay(this.queryDelay);
14269         }
14270     },
14271
14272     // private
14273     validateBlur : function(){
14274         return !this.list || !this.list.isVisible();   
14275     },
14276
14277     // private
14278     initQuery : function(){
14279         
14280         var v = this.getRawValue();
14281         
14282         if(this.tickable && this.editable){
14283             v = this.tickableInputEl().getValue();
14284         }
14285         
14286         this.doQuery(v);
14287     },
14288
14289     // private
14290     doForce : function(){
14291         if(this.inputEl().dom.value.length > 0){
14292             this.inputEl().dom.value =
14293                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14294              
14295         }
14296     },
14297
14298     /**
14299      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14300      * query allowing the query action to be canceled if needed.
14301      * @param {String} query The SQL query to execute
14302      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14303      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14304      * saved in the current store (defaults to false)
14305      */
14306     doQuery : function(q, forceAll){
14307         
14308         if(q === undefined || q === null){
14309             q = '';
14310         }
14311         var qe = {
14312             query: q,
14313             forceAll: forceAll,
14314             combo: this,
14315             cancel:false
14316         };
14317         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14318             return false;
14319         }
14320         q = qe.query;
14321         
14322         forceAll = qe.forceAll;
14323         if(forceAll === true || (q.length >= this.minChars)){
14324             
14325             this.hasQuery = true;
14326             
14327             if(this.lastQuery != q || this.alwaysQuery){
14328                 this.lastQuery = q;
14329                 if(this.mode == 'local'){
14330                     this.selectedIndex = -1;
14331                     if(forceAll){
14332                         this.store.clearFilter();
14333                     }else{
14334                         
14335                         if(this.specialFilter){
14336                             this.fireEvent('specialfilter', this);
14337                             this.onLoad();
14338                             return;
14339                         }
14340                         
14341                         this.store.filter(this.displayField, q);
14342                     }
14343                     
14344                     this.store.fireEvent("datachanged", this.store);
14345                     
14346                     this.onLoad();
14347                     
14348                     
14349                 }else{
14350                     
14351                     this.store.baseParams[this.queryParam] = q;
14352                     
14353                     var options = {params : this.getParams(q)};
14354                     
14355                     if(this.loadNext){
14356                         options.add = true;
14357                         options.params.start = this.page * this.pageSize;
14358                     }
14359                     
14360                     this.store.load(options);
14361                     
14362                     /*
14363                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14364                      *  we should expand the list on onLoad
14365                      *  so command out it
14366                      */
14367 //                    this.expand();
14368                 }
14369             }else{
14370                 this.selectedIndex = -1;
14371                 this.onLoad();   
14372             }
14373         }
14374         
14375         this.loadNext = false;
14376     },
14377     
14378     // private
14379     getParams : function(q){
14380         var p = {};
14381         //p[this.queryParam] = q;
14382         
14383         if(this.pageSize){
14384             p.start = 0;
14385             p.limit = this.pageSize;
14386         }
14387         return p;
14388     },
14389
14390     /**
14391      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14392      */
14393     collapse : function(){
14394         if(!this.isExpanded()){
14395             return;
14396         }
14397         
14398         this.list.hide();
14399         
14400         this.hasFocus = false;
14401         
14402         if(this.tickable){
14403             this.okBtn.hide();
14404             this.cancelBtn.hide();
14405             this.trigger.show();
14406             
14407             if(this.editable){
14408                 this.tickableInputEl().dom.value = '';
14409                 this.tickableInputEl().blur();
14410             }
14411             
14412         }
14413         
14414         Roo.get(document).un('mousedown', this.collapseIf, this);
14415         Roo.get(document).un('mousewheel', this.collapseIf, this);
14416         if (!this.editable) {
14417             Roo.get(document).un('keydown', this.listKeyPress, this);
14418         }
14419         this.fireEvent('collapse', this);
14420         
14421         this.validate();
14422     },
14423
14424     // private
14425     collapseIf : function(e){
14426         var in_combo  = e.within(this.el);
14427         var in_list =  e.within(this.list);
14428         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14429         
14430         if (in_combo || in_list || is_list) {
14431             //e.stopPropagation();
14432             return;
14433         }
14434         
14435         if(this.tickable){
14436             this.onTickableFooterButtonClick(e, false, false);
14437         }
14438
14439         this.collapse();
14440         
14441     },
14442
14443     /**
14444      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14445      */
14446     expand : function(){
14447        
14448         if(this.isExpanded() || !this.hasFocus){
14449             return;
14450         }
14451         
14452         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14453         this.list.setWidth(lw);
14454         
14455         Roo.log('expand');
14456         
14457         this.list.show();
14458         
14459         this.restrictHeight();
14460         
14461         if(this.tickable){
14462             
14463             this.tickItems = Roo.apply([], this.item);
14464             
14465             this.okBtn.show();
14466             this.cancelBtn.show();
14467             this.trigger.hide();
14468             
14469             if(this.editable){
14470                 this.tickableInputEl().focus();
14471             }
14472             
14473         }
14474         
14475         Roo.get(document).on('mousedown', this.collapseIf, this);
14476         Roo.get(document).on('mousewheel', this.collapseIf, this);
14477         if (!this.editable) {
14478             Roo.get(document).on('keydown', this.listKeyPress, this);
14479         }
14480         
14481         this.fireEvent('expand', this);
14482     },
14483
14484     // private
14485     // Implements the default empty TriggerField.onTriggerClick function
14486     onTriggerClick : function(e)
14487     {
14488         Roo.log('trigger click');
14489         
14490         if(this.disabled || !this.triggerList){
14491             return;
14492         }
14493         
14494         this.page = 0;
14495         this.loadNext = false;
14496         
14497         if(this.isExpanded()){
14498             this.collapse();
14499             if (!this.blockFocus) {
14500                 this.inputEl().focus();
14501             }
14502             
14503         }else {
14504             this.hasFocus = true;
14505             if(this.triggerAction == 'all') {
14506                 this.doQuery(this.allQuery, true);
14507             } else {
14508                 this.doQuery(this.getRawValue());
14509             }
14510             if (!this.blockFocus) {
14511                 this.inputEl().focus();
14512             }
14513         }
14514     },
14515     
14516     onTickableTriggerClick : function(e)
14517     {
14518         if(this.disabled){
14519             return;
14520         }
14521         
14522         this.page = 0;
14523         this.loadNext = false;
14524         this.hasFocus = true;
14525         
14526         if(this.triggerAction == 'all') {
14527             this.doQuery(this.allQuery, true);
14528         } else {
14529             this.doQuery(this.getRawValue());
14530         }
14531     },
14532     
14533     onSearchFieldClick : function(e)
14534     {
14535         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14536             this.onTickableFooterButtonClick(e, false, false);
14537             return;
14538         }
14539         
14540         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14541             return;
14542         }
14543         
14544         this.page = 0;
14545         this.loadNext = false;
14546         this.hasFocus = true;
14547         
14548         if(this.triggerAction == 'all') {
14549             this.doQuery(this.allQuery, true);
14550         } else {
14551             this.doQuery(this.getRawValue());
14552         }
14553     },
14554     
14555     listKeyPress : function(e)
14556     {
14557         //Roo.log('listkeypress');
14558         // scroll to first matching element based on key pres..
14559         if (e.isSpecialKey()) {
14560             return false;
14561         }
14562         var k = String.fromCharCode(e.getKey()).toUpperCase();
14563         //Roo.log(k);
14564         var match  = false;
14565         var csel = this.view.getSelectedNodes();
14566         var cselitem = false;
14567         if (csel.length) {
14568             var ix = this.view.indexOf(csel[0]);
14569             cselitem  = this.store.getAt(ix);
14570             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14571                 cselitem = false;
14572             }
14573             
14574         }
14575         
14576         this.store.each(function(v) { 
14577             if (cselitem) {
14578                 // start at existing selection.
14579                 if (cselitem.id == v.id) {
14580                     cselitem = false;
14581                 }
14582                 return true;
14583             }
14584                 
14585             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14586                 match = this.store.indexOf(v);
14587                 return false;
14588             }
14589             return true;
14590         }, this);
14591         
14592         if (match === false) {
14593             return true; // no more action?
14594         }
14595         // scroll to?
14596         this.view.select(match);
14597         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14598         sn.scrollIntoView(sn.dom.parentNode, false);
14599     },
14600     
14601     onViewScroll : function(e, t){
14602         
14603         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){
14604             return;
14605         }
14606         
14607         this.hasQuery = true;
14608         
14609         this.loading = this.list.select('.loading', true).first();
14610         
14611         if(this.loading === null){
14612             this.list.createChild({
14613                 tag: 'div',
14614                 cls: 'loading roo-select2-more-results roo-select2-active',
14615                 html: 'Loading more results...'
14616             });
14617             
14618             this.loading = this.list.select('.loading', true).first();
14619             
14620             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14621             
14622             this.loading.hide();
14623         }
14624         
14625         this.loading.show();
14626         
14627         var _combo = this;
14628         
14629         this.page++;
14630         this.loadNext = true;
14631         
14632         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14633         
14634         return;
14635     },
14636     
14637     addItem : function(o)
14638     {   
14639         var dv = ''; // display value
14640         
14641         if (this.displayField) {
14642             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14643         } else {
14644             // this is an error condition!!!
14645             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14646         }
14647         
14648         if(!dv.length){
14649             return;
14650         }
14651         
14652         var choice = this.choices.createChild({
14653             tag: 'li',
14654             cls: 'roo-select2-search-choice',
14655             cn: [
14656                 {
14657                     tag: 'div',
14658                     html: dv
14659                 },
14660                 {
14661                     tag: 'a',
14662                     href: '#',
14663                     cls: 'roo-select2-search-choice-close fa fa-times',
14664                     tabindex: '-1'
14665                 }
14666             ]
14667             
14668         }, this.searchField);
14669         
14670         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14671         
14672         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14673         
14674         this.item.push(o);
14675         
14676         this.lastData = o;
14677         
14678         this.syncValue();
14679         
14680         this.inputEl().dom.value = '';
14681         
14682         this.validate();
14683     },
14684     
14685     onRemoveItem : function(e, _self, o)
14686     {
14687         e.preventDefault();
14688         
14689         this.lastItem = Roo.apply([], this.item);
14690         
14691         var index = this.item.indexOf(o.data) * 1;
14692         
14693         if( index < 0){
14694             Roo.log('not this item?!');
14695             return;
14696         }
14697         
14698         this.item.splice(index, 1);
14699         o.item.remove();
14700         
14701         this.syncValue();
14702         
14703         this.fireEvent('remove', this, e);
14704         
14705         this.validate();
14706         
14707     },
14708     
14709     syncValue : function()
14710     {
14711         if(!this.item.length){
14712             this.clearValue();
14713             return;
14714         }
14715             
14716         var value = [];
14717         var _this = this;
14718         Roo.each(this.item, function(i){
14719             if(_this.valueField){
14720                 value.push(i[_this.valueField]);
14721                 return;
14722             }
14723
14724             value.push(i);
14725         });
14726
14727         this.value = value.join(',');
14728
14729         if(this.hiddenField){
14730             this.hiddenField.dom.value = this.value;
14731         }
14732         
14733         this.store.fireEvent("datachanged", this.store);
14734         
14735         this.validate();
14736     },
14737     
14738     clearItem : function()
14739     {
14740         if(!this.multiple){
14741             return;
14742         }
14743         
14744         this.item = [];
14745         
14746         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14747            c.remove();
14748         });
14749         
14750         this.syncValue();
14751         
14752         this.validate();
14753         
14754         if(this.tickable && !Roo.isTouch){
14755             this.view.refresh();
14756         }
14757     },
14758     
14759     inputEl: function ()
14760     {
14761         if(Roo.isIOS && this.useNativeIOS){
14762             return this.el.select('select.roo-ios-select', true).first();
14763         }
14764         
14765         if(Roo.isTouch && this.mobileTouchView){
14766             return this.el.select('input.form-control',true).first();
14767         }
14768         
14769         if(this.tickable){
14770             return this.searchField;
14771         }
14772         
14773         return this.el.select('input.form-control',true).first();
14774     },
14775     
14776     onTickableFooterButtonClick : function(e, btn, el)
14777     {
14778         e.preventDefault();
14779         
14780         this.lastItem = Roo.apply([], this.item);
14781         
14782         if(btn && btn.name == 'cancel'){
14783             this.tickItems = Roo.apply([], this.item);
14784             this.collapse();
14785             return;
14786         }
14787         
14788         this.clearItem();
14789         
14790         var _this = this;
14791         
14792         Roo.each(this.tickItems, function(o){
14793             _this.addItem(o);
14794         });
14795         
14796         this.collapse();
14797         
14798     },
14799     
14800     validate : function()
14801     {
14802         if(this.getVisibilityEl().hasClass('hidden')){
14803             return true;
14804         }
14805         
14806         var v = this.getRawValue();
14807         
14808         if(this.multiple){
14809             v = this.getValue();
14810         }
14811         
14812         if(this.disabled || this.allowBlank || v.length){
14813             this.markValid();
14814             return true;
14815         }
14816         
14817         this.markInvalid();
14818         return false;
14819     },
14820     
14821     tickableInputEl : function()
14822     {
14823         if(!this.tickable || !this.editable){
14824             return this.inputEl();
14825         }
14826         
14827         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14828     },
14829     
14830     
14831     getAutoCreateTouchView : function()
14832     {
14833         var id = Roo.id();
14834         
14835         var cfg = {
14836             cls: 'form-group' //input-group
14837         };
14838         
14839         var input =  {
14840             tag: 'input',
14841             id : id,
14842             type : this.inputType,
14843             cls : 'form-control x-combo-noedit',
14844             autocomplete: 'new-password',
14845             placeholder : this.placeholder || '',
14846             readonly : true
14847         };
14848         
14849         if (this.name) {
14850             input.name = this.name;
14851         }
14852         
14853         if (this.size) {
14854             input.cls += ' input-' + this.size;
14855         }
14856         
14857         if (this.disabled) {
14858             input.disabled = true;
14859         }
14860         
14861         var inputblock = {
14862             cls : '',
14863             cn : [
14864                 input
14865             ]
14866         };
14867         
14868         if(this.before){
14869             inputblock.cls += ' input-group';
14870             
14871             inputblock.cn.unshift({
14872                 tag :'span',
14873                 cls : 'input-group-addon',
14874                 html : this.before
14875             });
14876         }
14877         
14878         if(this.removable && !this.multiple){
14879             inputblock.cls += ' roo-removable';
14880             
14881             inputblock.cn.push({
14882                 tag: 'button',
14883                 html : 'x',
14884                 cls : 'roo-combo-removable-btn close'
14885             });
14886         }
14887
14888         if(this.hasFeedback && !this.allowBlank){
14889             
14890             inputblock.cls += ' has-feedback';
14891             
14892             inputblock.cn.push({
14893                 tag: 'span',
14894                 cls: 'glyphicon form-control-feedback'
14895             });
14896             
14897         }
14898         
14899         if (this.after) {
14900             
14901             inputblock.cls += (this.before) ? '' : ' input-group';
14902             
14903             inputblock.cn.push({
14904                 tag :'span',
14905                 cls : 'input-group-addon',
14906                 html : this.after
14907             });
14908         }
14909
14910         var box = {
14911             tag: 'div',
14912             cn: [
14913                 {
14914                     tag: 'input',
14915                     type : 'hidden',
14916                     cls: 'form-hidden-field'
14917                 },
14918                 inputblock
14919             ]
14920             
14921         };
14922         
14923         if(this.multiple){
14924             box = {
14925                 tag: 'div',
14926                 cn: [
14927                     {
14928                         tag: 'input',
14929                         type : 'hidden',
14930                         cls: 'form-hidden-field'
14931                     },
14932                     {
14933                         tag: 'ul',
14934                         cls: 'roo-select2-choices',
14935                         cn:[
14936                             {
14937                                 tag: 'li',
14938                                 cls: 'roo-select2-search-field',
14939                                 cn: [
14940
14941                                     inputblock
14942                                 ]
14943                             }
14944                         ]
14945                     }
14946                 ]
14947             }
14948         };
14949         
14950         var combobox = {
14951             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14952             cn: [
14953                 box
14954             ]
14955         };
14956         
14957         if(!this.multiple && this.showToggleBtn){
14958             
14959             var caret = {
14960                         tag: 'span',
14961                         cls: 'caret'
14962             };
14963             
14964             if (this.caret != false) {
14965                 caret = {
14966                      tag: 'i',
14967                      cls: 'fa fa-' + this.caret
14968                 };
14969                 
14970             }
14971             
14972             combobox.cn.push({
14973                 tag :'span',
14974                 cls : 'input-group-addon btn dropdown-toggle',
14975                 cn : [
14976                     caret,
14977                     {
14978                         tag: 'span',
14979                         cls: 'combobox-clear',
14980                         cn  : [
14981                             {
14982                                 tag : 'i',
14983                                 cls: 'icon-remove'
14984                             }
14985                         ]
14986                     }
14987                 ]
14988
14989             })
14990         }
14991         
14992         if(this.multiple){
14993             combobox.cls += ' roo-select2-container-multi';
14994         }
14995         
14996         var align = this.labelAlign || this.parentLabelAlign();
14997         
14998         if (align ==='left' && this.fieldLabel.length) {
14999
15000             cfg.cn = [
15001                 {
15002                    tag : 'i',
15003                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15004                    tooltip : 'This field is required'
15005                 },
15006                 {
15007                     tag: 'label',
15008                     cls : 'control-label',
15009                     html : this.fieldLabel
15010
15011                 },
15012                 {
15013                     cls : '', 
15014                     cn: [
15015                         combobox
15016                     ]
15017                 }
15018             ];
15019             
15020             var labelCfg = cfg.cn[1];
15021             var contentCfg = cfg.cn[2];
15022             
15023
15024             if(this.indicatorpos == 'right'){
15025                 cfg.cn = [
15026                     {
15027                         tag: 'label',
15028                         'for' :  id,
15029                         cls : 'control-label',
15030                         cn : [
15031                             {
15032                                 tag : 'span',
15033                                 html : this.fieldLabel
15034                             },
15035                             {
15036                                 tag : 'i',
15037                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15038                                 tooltip : 'This field is required'
15039                             }
15040                         ]
15041                     },
15042                     {
15043                         cls : "",
15044                         cn: [
15045                             combobox
15046                         ]
15047                     }
15048
15049                 ];
15050                 
15051                 labelCfg = cfg.cn[0];
15052                 contentCfg = cfg.cn[1];
15053             }
15054             
15055            
15056             
15057             if(this.labelWidth > 12){
15058                 labelCfg.style = "width: " + this.labelWidth + 'px';
15059             }
15060             
15061             if(this.labelWidth < 13 && this.labelmd == 0){
15062                 this.labelmd = this.labelWidth;
15063             }
15064             
15065             if(this.labellg > 0){
15066                 labelCfg.cls += ' col-lg-' + this.labellg;
15067                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15068             }
15069             
15070             if(this.labelmd > 0){
15071                 labelCfg.cls += ' col-md-' + this.labelmd;
15072                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15073             }
15074             
15075             if(this.labelsm > 0){
15076                 labelCfg.cls += ' col-sm-' + this.labelsm;
15077                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15078             }
15079             
15080             if(this.labelxs > 0){
15081                 labelCfg.cls += ' col-xs-' + this.labelxs;
15082                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15083             }
15084                 
15085                 
15086         } else if ( this.fieldLabel.length) {
15087             cfg.cn = [
15088                 {
15089                    tag : 'i',
15090                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15091                    tooltip : 'This field is required'
15092                 },
15093                 {
15094                     tag: 'label',
15095                     cls : 'control-label',
15096                     html : this.fieldLabel
15097
15098                 },
15099                 {
15100                     cls : '', 
15101                     cn: [
15102                         combobox
15103                     ]
15104                 }
15105             ];
15106             
15107             if(this.indicatorpos == 'right'){
15108                 cfg.cn = [
15109                     {
15110                         tag: 'label',
15111                         cls : 'control-label',
15112                         html : this.fieldLabel,
15113                         cn : [
15114                             {
15115                                tag : 'i',
15116                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15117                                tooltip : 'This field is required'
15118                             }
15119                         ]
15120                     },
15121                     {
15122                         cls : '', 
15123                         cn: [
15124                             combobox
15125                         ]
15126                     }
15127                 ];
15128             }
15129         } else {
15130             cfg.cn = combobox;    
15131         }
15132         
15133         
15134         var settings = this;
15135         
15136         ['xs','sm','md','lg'].map(function(size){
15137             if (settings[size]) {
15138                 cfg.cls += ' col-' + size + '-' + settings[size];
15139             }
15140         });
15141         
15142         return cfg;
15143     },
15144     
15145     initTouchView : function()
15146     {
15147         this.renderTouchView();
15148         
15149         this.touchViewEl.on('scroll', function(){
15150             this.el.dom.scrollTop = 0;
15151         }, this);
15152         
15153         this.originalValue = this.getValue();
15154         
15155         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15156         
15157         this.inputEl().on("click", this.showTouchView, this);
15158         if (this.triggerEl) {
15159             this.triggerEl.on("click", this.showTouchView, this);
15160         }
15161         
15162         
15163         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15164         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15165         
15166         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15167         
15168         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15169         this.store.on('load', this.onTouchViewLoad, this);
15170         this.store.on('loadexception', this.onTouchViewLoadException, this);
15171         
15172         if(this.hiddenName){
15173             
15174             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15175             
15176             this.hiddenField.dom.value =
15177                 this.hiddenValue !== undefined ? this.hiddenValue :
15178                 this.value !== undefined ? this.value : '';
15179         
15180             this.el.dom.removeAttribute('name');
15181             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15182         }
15183         
15184         if(this.multiple){
15185             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15186             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15187         }
15188         
15189         if(this.removable && !this.multiple){
15190             var close = this.closeTriggerEl();
15191             if(close){
15192                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15193                 close.on('click', this.removeBtnClick, this, close);
15194             }
15195         }
15196         /*
15197          * fix the bug in Safari iOS8
15198          */
15199         this.inputEl().on("focus", function(e){
15200             document.activeElement.blur();
15201         }, this);
15202         
15203         return;
15204         
15205         
15206     },
15207     
15208     renderTouchView : function()
15209     {
15210         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15211         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15212         
15213         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15214         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15215         
15216         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15217         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15218         this.touchViewBodyEl.setStyle('overflow', 'auto');
15219         
15220         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15221         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15222         
15223         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15224         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15225         
15226     },
15227     
15228     showTouchView : function()
15229     {
15230         if(this.disabled){
15231             return;
15232         }
15233         
15234         this.touchViewHeaderEl.hide();
15235
15236         if(this.modalTitle.length){
15237             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15238             this.touchViewHeaderEl.show();
15239         }
15240
15241         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15242         this.touchViewEl.show();
15243
15244         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15245         
15246         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15247         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15248
15249         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15250
15251         if(this.modalTitle.length){
15252             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15253         }
15254         
15255         this.touchViewBodyEl.setHeight(bodyHeight);
15256
15257         if(this.animate){
15258             var _this = this;
15259             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15260         }else{
15261             this.touchViewEl.addClass('in');
15262         }
15263
15264         this.doTouchViewQuery();
15265         
15266     },
15267     
15268     hideTouchView : function()
15269     {
15270         this.touchViewEl.removeClass('in');
15271
15272         if(this.animate){
15273             var _this = this;
15274             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15275         }else{
15276             this.touchViewEl.setStyle('display', 'none');
15277         }
15278         
15279     },
15280     
15281     setTouchViewValue : function()
15282     {
15283         if(this.multiple){
15284             this.clearItem();
15285         
15286             var _this = this;
15287
15288             Roo.each(this.tickItems, function(o){
15289                 this.addItem(o);
15290             }, this);
15291         }
15292         
15293         this.hideTouchView();
15294     },
15295     
15296     doTouchViewQuery : function()
15297     {
15298         var qe = {
15299             query: '',
15300             forceAll: true,
15301             combo: this,
15302             cancel:false
15303         };
15304         
15305         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15306             return false;
15307         }
15308         
15309         if(!this.alwaysQuery || this.mode == 'local'){
15310             this.onTouchViewLoad();
15311             return;
15312         }
15313         
15314         this.store.load();
15315     },
15316     
15317     onTouchViewBeforeLoad : function(combo,opts)
15318     {
15319         return;
15320     },
15321
15322     // private
15323     onTouchViewLoad : function()
15324     {
15325         if(this.store.getCount() < 1){
15326             this.onTouchViewEmptyResults();
15327             return;
15328         }
15329         
15330         this.clearTouchView();
15331         
15332         var rawValue = this.getRawValue();
15333         
15334         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15335         
15336         this.tickItems = [];
15337         
15338         this.store.data.each(function(d, rowIndex){
15339             var row = this.touchViewListGroup.createChild(template);
15340             
15341             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15342                 row.addClass(d.data.cls);
15343             }
15344             
15345             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15346                 var cfg = {
15347                     data : d.data,
15348                     html : d.data[this.displayField]
15349                 };
15350                 
15351                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15352                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15353                 }
15354             }
15355             row.removeClass('selected');
15356             if(!this.multiple && this.valueField &&
15357                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15358             {
15359                 // radio buttons..
15360                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15361                 row.addClass('selected');
15362             }
15363             
15364             if(this.multiple && this.valueField &&
15365                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15366             {
15367                 
15368                 // checkboxes...
15369                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15370                 this.tickItems.push(d.data);
15371             }
15372             
15373             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15374             
15375         }, this);
15376         
15377         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15378         
15379         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15380
15381         if(this.modalTitle.length){
15382             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15383         }
15384
15385         var listHeight = this.touchViewListGroup.getHeight();
15386         
15387         var _this = this;
15388         
15389         if(firstChecked && listHeight > bodyHeight){
15390             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15391         }
15392         
15393     },
15394     
15395     onTouchViewLoadException : function()
15396     {
15397         this.hideTouchView();
15398     },
15399     
15400     onTouchViewEmptyResults : function()
15401     {
15402         this.clearTouchView();
15403         
15404         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15405         
15406         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15407         
15408     },
15409     
15410     clearTouchView : function()
15411     {
15412         this.touchViewListGroup.dom.innerHTML = '';
15413     },
15414     
15415     onTouchViewClick : function(e, el, o)
15416     {
15417         e.preventDefault();
15418         
15419         var row = o.row;
15420         var rowIndex = o.rowIndex;
15421         
15422         var r = this.store.getAt(rowIndex);
15423         
15424         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15425             
15426             if(!this.multiple){
15427                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15428                     c.dom.removeAttribute('checked');
15429                 }, this);
15430
15431                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15432
15433                 this.setFromData(r.data);
15434
15435                 var close = this.closeTriggerEl();
15436
15437                 if(close){
15438                     close.show();
15439                 }
15440
15441                 this.hideTouchView();
15442
15443                 this.fireEvent('select', this, r, rowIndex);
15444
15445                 return;
15446             }
15447
15448             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15449                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15450                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15451                 return;
15452             }
15453
15454             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15455             this.addItem(r.data);
15456             this.tickItems.push(r.data);
15457         }
15458     },
15459     
15460     getAutoCreateNativeIOS : function()
15461     {
15462         var cfg = {
15463             cls: 'form-group' //input-group,
15464         };
15465         
15466         var combobox =  {
15467             tag: 'select',
15468             cls : 'roo-ios-select'
15469         };
15470         
15471         if (this.name) {
15472             combobox.name = this.name;
15473         }
15474         
15475         if (this.disabled) {
15476             combobox.disabled = true;
15477         }
15478         
15479         var settings = this;
15480         
15481         ['xs','sm','md','lg'].map(function(size){
15482             if (settings[size]) {
15483                 cfg.cls += ' col-' + size + '-' + settings[size];
15484             }
15485         });
15486         
15487         cfg.cn = combobox;
15488         
15489         return cfg;
15490         
15491     },
15492     
15493     initIOSView : function()
15494     {
15495         this.store.on('load', this.onIOSViewLoad, this);
15496         
15497         return;
15498     },
15499     
15500     onIOSViewLoad : function()
15501     {
15502         if(this.store.getCount() < 1){
15503             return;
15504         }
15505         
15506         this.clearIOSView();
15507         
15508         if(this.allowBlank) {
15509             
15510             var default_text = '-- SELECT --';
15511             
15512             if(this.placeholder.length){
15513                 default_text = this.placeholder;
15514             }
15515             
15516             if(this.emptyTitle.length){
15517                 default_text += ' - ' + this.emptyTitle + ' -';
15518             }
15519             
15520             var opt = this.inputEl().createChild({
15521                 tag: 'option',
15522                 value : 0,
15523                 html : default_text
15524             });
15525             
15526             var o = {};
15527             o[this.valueField] = 0;
15528             o[this.displayField] = default_text;
15529             
15530             this.ios_options.push({
15531                 data : o,
15532                 el : opt
15533             });
15534             
15535         }
15536         
15537         this.store.data.each(function(d, rowIndex){
15538             
15539             var html = '';
15540             
15541             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15542                 html = d.data[this.displayField];
15543             }
15544             
15545             var value = '';
15546             
15547             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15548                 value = d.data[this.valueField];
15549             }
15550             
15551             var option = {
15552                 tag: 'option',
15553                 value : value,
15554                 html : html
15555             };
15556             
15557             if(this.value == d.data[this.valueField]){
15558                 option['selected'] = true;
15559             }
15560             
15561             var opt = this.inputEl().createChild(option);
15562             
15563             this.ios_options.push({
15564                 data : d.data,
15565                 el : opt
15566             });
15567             
15568         }, this);
15569         
15570         this.inputEl().on('change', function(){
15571            this.fireEvent('select', this);
15572         }, this);
15573         
15574     },
15575     
15576     clearIOSView: function()
15577     {
15578         this.inputEl().dom.innerHTML = '';
15579         
15580         this.ios_options = [];
15581     },
15582     
15583     setIOSValue: function(v)
15584     {
15585         this.value = v;
15586         
15587         if(!this.ios_options){
15588             return;
15589         }
15590         
15591         Roo.each(this.ios_options, function(opts){
15592            
15593            opts.el.dom.removeAttribute('selected');
15594            
15595            if(opts.data[this.valueField] != v){
15596                return;
15597            }
15598            
15599            opts.el.dom.setAttribute('selected', true);
15600            
15601         }, this);
15602     }
15603
15604     /** 
15605     * @cfg {Boolean} grow 
15606     * @hide 
15607     */
15608     /** 
15609     * @cfg {Number} growMin 
15610     * @hide 
15611     */
15612     /** 
15613     * @cfg {Number} growMax 
15614     * @hide 
15615     */
15616     /**
15617      * @hide
15618      * @method autoSize
15619      */
15620 });
15621
15622 Roo.apply(Roo.bootstrap.ComboBox,  {
15623     
15624     header : {
15625         tag: 'div',
15626         cls: 'modal-header',
15627         cn: [
15628             {
15629                 tag: 'h4',
15630                 cls: 'modal-title'
15631             }
15632         ]
15633     },
15634     
15635     body : {
15636         tag: 'div',
15637         cls: 'modal-body',
15638         cn: [
15639             {
15640                 tag: 'ul',
15641                 cls: 'list-group'
15642             }
15643         ]
15644     },
15645     
15646     listItemRadio : {
15647         tag: 'li',
15648         cls: 'list-group-item',
15649         cn: [
15650             {
15651                 tag: 'span',
15652                 cls: 'roo-combobox-list-group-item-value'
15653             },
15654             {
15655                 tag: 'div',
15656                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15657                 cn: [
15658                     {
15659                         tag: 'input',
15660                         type: 'radio'
15661                     },
15662                     {
15663                         tag: 'label'
15664                     }
15665                 ]
15666             }
15667         ]
15668     },
15669     
15670     listItemCheckbox : {
15671         tag: 'li',
15672         cls: 'list-group-item',
15673         cn: [
15674             {
15675                 tag: 'span',
15676                 cls: 'roo-combobox-list-group-item-value'
15677             },
15678             {
15679                 tag: 'div',
15680                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15681                 cn: [
15682                     {
15683                         tag: 'input',
15684                         type: 'checkbox'
15685                     },
15686                     {
15687                         tag: 'label'
15688                     }
15689                 ]
15690             }
15691         ]
15692     },
15693     
15694     emptyResult : {
15695         tag: 'div',
15696         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15697     },
15698     
15699     footer : {
15700         tag: 'div',
15701         cls: 'modal-footer',
15702         cn: [
15703             {
15704                 tag: 'div',
15705                 cls: 'row',
15706                 cn: [
15707                     {
15708                         tag: 'div',
15709                         cls: 'col-xs-6 text-left',
15710                         cn: {
15711                             tag: 'button',
15712                             cls: 'btn btn-danger roo-touch-view-cancel',
15713                             html: 'Cancel'
15714                         }
15715                     },
15716                     {
15717                         tag: 'div',
15718                         cls: 'col-xs-6 text-right',
15719                         cn: {
15720                             tag: 'button',
15721                             cls: 'btn btn-success roo-touch-view-ok',
15722                             html: 'OK'
15723                         }
15724                     }
15725                 ]
15726             }
15727         ]
15728         
15729     }
15730 });
15731
15732 Roo.apply(Roo.bootstrap.ComboBox,  {
15733     
15734     touchViewTemplate : {
15735         tag: 'div',
15736         cls: 'modal fade roo-combobox-touch-view',
15737         cn: [
15738             {
15739                 tag: 'div',
15740                 cls: 'modal-dialog',
15741                 style : 'position:fixed', // we have to fix position....
15742                 cn: [
15743                     {
15744                         tag: 'div',
15745                         cls: 'modal-content',
15746                         cn: [
15747                             Roo.bootstrap.ComboBox.header,
15748                             Roo.bootstrap.ComboBox.body,
15749                             Roo.bootstrap.ComboBox.footer
15750                         ]
15751                     }
15752                 ]
15753             }
15754         ]
15755     }
15756 });/*
15757  * Based on:
15758  * Ext JS Library 1.1.1
15759  * Copyright(c) 2006-2007, Ext JS, LLC.
15760  *
15761  * Originally Released Under LGPL - original licence link has changed is not relivant.
15762  *
15763  * Fork - LGPL
15764  * <script type="text/javascript">
15765  */
15766
15767 /**
15768  * @class Roo.View
15769  * @extends Roo.util.Observable
15770  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15771  * This class also supports single and multi selection modes. <br>
15772  * Create a data model bound view:
15773  <pre><code>
15774  var store = new Roo.data.Store(...);
15775
15776  var view = new Roo.View({
15777     el : "my-element",
15778     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15779  
15780     singleSelect: true,
15781     selectedClass: "ydataview-selected",
15782     store: store
15783  });
15784
15785  // listen for node click?
15786  view.on("click", function(vw, index, node, e){
15787  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15788  });
15789
15790  // load XML data
15791  dataModel.load("foobar.xml");
15792  </code></pre>
15793  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15794  * <br><br>
15795  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15796  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15797  * 
15798  * Note: old style constructor is still suported (container, template, config)
15799  * 
15800  * @constructor
15801  * Create a new View
15802  * @param {Object} config The config object
15803  * 
15804  */
15805 Roo.View = function(config, depreciated_tpl, depreciated_config){
15806     
15807     this.parent = false;
15808     
15809     if (typeof(depreciated_tpl) == 'undefined') {
15810         // new way.. - universal constructor.
15811         Roo.apply(this, config);
15812         this.el  = Roo.get(this.el);
15813     } else {
15814         // old format..
15815         this.el  = Roo.get(config);
15816         this.tpl = depreciated_tpl;
15817         Roo.apply(this, depreciated_config);
15818     }
15819     this.wrapEl  = this.el.wrap().wrap();
15820     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15821     
15822     
15823     if(typeof(this.tpl) == "string"){
15824         this.tpl = new Roo.Template(this.tpl);
15825     } else {
15826         // support xtype ctors..
15827         this.tpl = new Roo.factory(this.tpl, Roo);
15828     }
15829     
15830     
15831     this.tpl.compile();
15832     
15833     /** @private */
15834     this.addEvents({
15835         /**
15836          * @event beforeclick
15837          * Fires before a click is processed. Returns false to cancel the default action.
15838          * @param {Roo.View} this
15839          * @param {Number} index The index of the target node
15840          * @param {HTMLElement} node The target node
15841          * @param {Roo.EventObject} e The raw event object
15842          */
15843             "beforeclick" : true,
15844         /**
15845          * @event click
15846          * Fires when a template node is clicked.
15847          * @param {Roo.View} this
15848          * @param {Number} index The index of the target node
15849          * @param {HTMLElement} node The target node
15850          * @param {Roo.EventObject} e The raw event object
15851          */
15852             "click" : true,
15853         /**
15854          * @event dblclick
15855          * Fires when a template node is double clicked.
15856          * @param {Roo.View} this
15857          * @param {Number} index The index of the target node
15858          * @param {HTMLElement} node The target node
15859          * @param {Roo.EventObject} e The raw event object
15860          */
15861             "dblclick" : true,
15862         /**
15863          * @event contextmenu
15864          * Fires when a template node is right clicked.
15865          * @param {Roo.View} this
15866          * @param {Number} index The index of the target node
15867          * @param {HTMLElement} node The target node
15868          * @param {Roo.EventObject} e The raw event object
15869          */
15870             "contextmenu" : true,
15871         /**
15872          * @event selectionchange
15873          * Fires when the selected nodes change.
15874          * @param {Roo.View} this
15875          * @param {Array} selections Array of the selected nodes
15876          */
15877             "selectionchange" : true,
15878     
15879         /**
15880          * @event beforeselect
15881          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15882          * @param {Roo.View} this
15883          * @param {HTMLElement} node The node to be selected
15884          * @param {Array} selections Array of currently selected nodes
15885          */
15886             "beforeselect" : true,
15887         /**
15888          * @event preparedata
15889          * Fires on every row to render, to allow you to change the data.
15890          * @param {Roo.View} this
15891          * @param {Object} data to be rendered (change this)
15892          */
15893           "preparedata" : true
15894           
15895           
15896         });
15897
15898
15899
15900     this.el.on({
15901         "click": this.onClick,
15902         "dblclick": this.onDblClick,
15903         "contextmenu": this.onContextMenu,
15904         scope:this
15905     });
15906
15907     this.selections = [];
15908     this.nodes = [];
15909     this.cmp = new Roo.CompositeElementLite([]);
15910     if(this.store){
15911         this.store = Roo.factory(this.store, Roo.data);
15912         this.setStore(this.store, true);
15913     }
15914     
15915     if ( this.footer && this.footer.xtype) {
15916            
15917          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15918         
15919         this.footer.dataSource = this.store;
15920         this.footer.container = fctr;
15921         this.footer = Roo.factory(this.footer, Roo);
15922         fctr.insertFirst(this.el);
15923         
15924         // this is a bit insane - as the paging toolbar seems to detach the el..
15925 //        dom.parentNode.parentNode.parentNode
15926          // they get detached?
15927     }
15928     
15929     
15930     Roo.View.superclass.constructor.call(this);
15931     
15932     
15933 };
15934
15935 Roo.extend(Roo.View, Roo.util.Observable, {
15936     
15937      /**
15938      * @cfg {Roo.data.Store} store Data store to load data from.
15939      */
15940     store : false,
15941     
15942     /**
15943      * @cfg {String|Roo.Element} el The container element.
15944      */
15945     el : '',
15946     
15947     /**
15948      * @cfg {String|Roo.Template} tpl The template used by this View 
15949      */
15950     tpl : false,
15951     /**
15952      * @cfg {String} dataName the named area of the template to use as the data area
15953      *                          Works with domtemplates roo-name="name"
15954      */
15955     dataName: false,
15956     /**
15957      * @cfg {String} selectedClass The css class to add to selected nodes
15958      */
15959     selectedClass : "x-view-selected",
15960      /**
15961      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15962      */
15963     emptyText : "",
15964     
15965     /**
15966      * @cfg {String} text to display on mask (default Loading)
15967      */
15968     mask : false,
15969     /**
15970      * @cfg {Boolean} multiSelect Allow multiple selection
15971      */
15972     multiSelect : false,
15973     /**
15974      * @cfg {Boolean} singleSelect Allow single selection
15975      */
15976     singleSelect:  false,
15977     
15978     /**
15979      * @cfg {Boolean} toggleSelect - selecting 
15980      */
15981     toggleSelect : false,
15982     
15983     /**
15984      * @cfg {Boolean} tickable - selecting 
15985      */
15986     tickable : false,
15987     
15988     /**
15989      * Returns the element this view is bound to.
15990      * @return {Roo.Element}
15991      */
15992     getEl : function(){
15993         return this.wrapEl;
15994     },
15995     
15996     
15997
15998     /**
15999      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16000      */
16001     refresh : function(){
16002         //Roo.log('refresh');
16003         var t = this.tpl;
16004         
16005         // if we are using something like 'domtemplate', then
16006         // the what gets used is:
16007         // t.applySubtemplate(NAME, data, wrapping data..)
16008         // the outer template then get' applied with
16009         //     the store 'extra data'
16010         // and the body get's added to the
16011         //      roo-name="data" node?
16012         //      <span class='roo-tpl-{name}'></span> ?????
16013         
16014         
16015         
16016         this.clearSelections();
16017         this.el.update("");
16018         var html = [];
16019         var records = this.store.getRange();
16020         if(records.length < 1) {
16021             
16022             // is this valid??  = should it render a template??
16023             
16024             this.el.update(this.emptyText);
16025             return;
16026         }
16027         var el = this.el;
16028         if (this.dataName) {
16029             this.el.update(t.apply(this.store.meta)); //????
16030             el = this.el.child('.roo-tpl-' + this.dataName);
16031         }
16032         
16033         for(var i = 0, len = records.length; i < len; i++){
16034             var data = this.prepareData(records[i].data, i, records[i]);
16035             this.fireEvent("preparedata", this, data, i, records[i]);
16036             
16037             var d = Roo.apply({}, data);
16038             
16039             if(this.tickable){
16040                 Roo.apply(d, {'roo-id' : Roo.id()});
16041                 
16042                 var _this = this;
16043             
16044                 Roo.each(this.parent.item, function(item){
16045                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16046                         return;
16047                     }
16048                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16049                 });
16050             }
16051             
16052             html[html.length] = Roo.util.Format.trim(
16053                 this.dataName ?
16054                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16055                     t.apply(d)
16056             );
16057         }
16058         
16059         
16060         
16061         el.update(html.join(""));
16062         this.nodes = el.dom.childNodes;
16063         this.updateIndexes(0);
16064     },
16065     
16066
16067     /**
16068      * Function to override to reformat the data that is sent to
16069      * the template for each node.
16070      * DEPRICATED - use the preparedata event handler.
16071      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16072      * a JSON object for an UpdateManager bound view).
16073      */
16074     prepareData : function(data, index, record)
16075     {
16076         this.fireEvent("preparedata", this, data, index, record);
16077         return data;
16078     },
16079
16080     onUpdate : function(ds, record){
16081         // Roo.log('on update');   
16082         this.clearSelections();
16083         var index = this.store.indexOf(record);
16084         var n = this.nodes[index];
16085         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16086         n.parentNode.removeChild(n);
16087         this.updateIndexes(index, index);
16088     },
16089
16090     
16091     
16092 // --------- FIXME     
16093     onAdd : function(ds, records, index)
16094     {
16095         //Roo.log(['on Add', ds, records, index] );        
16096         this.clearSelections();
16097         if(this.nodes.length == 0){
16098             this.refresh();
16099             return;
16100         }
16101         var n = this.nodes[index];
16102         for(var i = 0, len = records.length; i < len; i++){
16103             var d = this.prepareData(records[i].data, i, records[i]);
16104             if(n){
16105                 this.tpl.insertBefore(n, d);
16106             }else{
16107                 
16108                 this.tpl.append(this.el, d);
16109             }
16110         }
16111         this.updateIndexes(index);
16112     },
16113
16114     onRemove : function(ds, record, index){
16115        // Roo.log('onRemove');
16116         this.clearSelections();
16117         var el = this.dataName  ?
16118             this.el.child('.roo-tpl-' + this.dataName) :
16119             this.el; 
16120         
16121         el.dom.removeChild(this.nodes[index]);
16122         this.updateIndexes(index);
16123     },
16124
16125     /**
16126      * Refresh an individual node.
16127      * @param {Number} index
16128      */
16129     refreshNode : function(index){
16130         this.onUpdate(this.store, this.store.getAt(index));
16131     },
16132
16133     updateIndexes : function(startIndex, endIndex){
16134         var ns = this.nodes;
16135         startIndex = startIndex || 0;
16136         endIndex = endIndex || ns.length - 1;
16137         for(var i = startIndex; i <= endIndex; i++){
16138             ns[i].nodeIndex = i;
16139         }
16140     },
16141
16142     /**
16143      * Changes the data store this view uses and refresh the view.
16144      * @param {Store} store
16145      */
16146     setStore : function(store, initial){
16147         if(!initial && this.store){
16148             this.store.un("datachanged", this.refresh);
16149             this.store.un("add", this.onAdd);
16150             this.store.un("remove", this.onRemove);
16151             this.store.un("update", this.onUpdate);
16152             this.store.un("clear", this.refresh);
16153             this.store.un("beforeload", this.onBeforeLoad);
16154             this.store.un("load", this.onLoad);
16155             this.store.un("loadexception", this.onLoad);
16156         }
16157         if(store){
16158           
16159             store.on("datachanged", this.refresh, this);
16160             store.on("add", this.onAdd, this);
16161             store.on("remove", this.onRemove, this);
16162             store.on("update", this.onUpdate, this);
16163             store.on("clear", this.refresh, this);
16164             store.on("beforeload", this.onBeforeLoad, this);
16165             store.on("load", this.onLoad, this);
16166             store.on("loadexception", this.onLoad, this);
16167         }
16168         
16169         if(store){
16170             this.refresh();
16171         }
16172     },
16173     /**
16174      * onbeforeLoad - masks the loading area.
16175      *
16176      */
16177     onBeforeLoad : function(store,opts)
16178     {
16179          //Roo.log('onBeforeLoad');   
16180         if (!opts.add) {
16181             this.el.update("");
16182         }
16183         this.el.mask(this.mask ? this.mask : "Loading" ); 
16184     },
16185     onLoad : function ()
16186     {
16187         this.el.unmask();
16188     },
16189     
16190
16191     /**
16192      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16193      * @param {HTMLElement} node
16194      * @return {HTMLElement} The template node
16195      */
16196     findItemFromChild : function(node){
16197         var el = this.dataName  ?
16198             this.el.child('.roo-tpl-' + this.dataName,true) :
16199             this.el.dom; 
16200         
16201         if(!node || node.parentNode == el){
16202                     return node;
16203             }
16204             var p = node.parentNode;
16205             while(p && p != el){
16206             if(p.parentNode == el){
16207                 return p;
16208             }
16209             p = p.parentNode;
16210         }
16211             return null;
16212     },
16213
16214     /** @ignore */
16215     onClick : function(e){
16216         var item = this.findItemFromChild(e.getTarget());
16217         if(item){
16218             var index = this.indexOf(item);
16219             if(this.onItemClick(item, index, e) !== false){
16220                 this.fireEvent("click", this, index, item, e);
16221             }
16222         }else{
16223             this.clearSelections();
16224         }
16225     },
16226
16227     /** @ignore */
16228     onContextMenu : function(e){
16229         var item = this.findItemFromChild(e.getTarget());
16230         if(item){
16231             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16232         }
16233     },
16234
16235     /** @ignore */
16236     onDblClick : function(e){
16237         var item = this.findItemFromChild(e.getTarget());
16238         if(item){
16239             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16240         }
16241     },
16242
16243     onItemClick : function(item, index, e)
16244     {
16245         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16246             return false;
16247         }
16248         if (this.toggleSelect) {
16249             var m = this.isSelected(item) ? 'unselect' : 'select';
16250             //Roo.log(m);
16251             var _t = this;
16252             _t[m](item, true, false);
16253             return true;
16254         }
16255         if(this.multiSelect || this.singleSelect){
16256             if(this.multiSelect && e.shiftKey && this.lastSelection){
16257                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16258             }else{
16259                 this.select(item, this.multiSelect && e.ctrlKey);
16260                 this.lastSelection = item;
16261             }
16262             
16263             if(!this.tickable){
16264                 e.preventDefault();
16265             }
16266             
16267         }
16268         return true;
16269     },
16270
16271     /**
16272      * Get the number of selected nodes.
16273      * @return {Number}
16274      */
16275     getSelectionCount : function(){
16276         return this.selections.length;
16277     },
16278
16279     /**
16280      * Get the currently selected nodes.
16281      * @return {Array} An array of HTMLElements
16282      */
16283     getSelectedNodes : function(){
16284         return this.selections;
16285     },
16286
16287     /**
16288      * Get the indexes of the selected nodes.
16289      * @return {Array}
16290      */
16291     getSelectedIndexes : function(){
16292         var indexes = [], s = this.selections;
16293         for(var i = 0, len = s.length; i < len; i++){
16294             indexes.push(s[i].nodeIndex);
16295         }
16296         return indexes;
16297     },
16298
16299     /**
16300      * Clear all selections
16301      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16302      */
16303     clearSelections : function(suppressEvent){
16304         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16305             this.cmp.elements = this.selections;
16306             this.cmp.removeClass(this.selectedClass);
16307             this.selections = [];
16308             if(!suppressEvent){
16309                 this.fireEvent("selectionchange", this, this.selections);
16310             }
16311         }
16312     },
16313
16314     /**
16315      * Returns true if the passed node is selected
16316      * @param {HTMLElement/Number} node The node or node index
16317      * @return {Boolean}
16318      */
16319     isSelected : function(node){
16320         var s = this.selections;
16321         if(s.length < 1){
16322             return false;
16323         }
16324         node = this.getNode(node);
16325         return s.indexOf(node) !== -1;
16326     },
16327
16328     /**
16329      * Selects nodes.
16330      * @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
16331      * @param {Boolean} keepExisting (optional) true to keep existing selections
16332      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16333      */
16334     select : function(nodeInfo, keepExisting, suppressEvent){
16335         if(nodeInfo instanceof Array){
16336             if(!keepExisting){
16337                 this.clearSelections(true);
16338             }
16339             for(var i = 0, len = nodeInfo.length; i < len; i++){
16340                 this.select(nodeInfo[i], true, true);
16341             }
16342             return;
16343         } 
16344         var node = this.getNode(nodeInfo);
16345         if(!node || this.isSelected(node)){
16346             return; // already selected.
16347         }
16348         if(!keepExisting){
16349             this.clearSelections(true);
16350         }
16351         
16352         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16353             Roo.fly(node).addClass(this.selectedClass);
16354             this.selections.push(node);
16355             if(!suppressEvent){
16356                 this.fireEvent("selectionchange", this, this.selections);
16357             }
16358         }
16359         
16360         
16361     },
16362       /**
16363      * Unselects nodes.
16364      * @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
16365      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16366      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16367      */
16368     unselect : function(nodeInfo, keepExisting, suppressEvent)
16369     {
16370         if(nodeInfo instanceof Array){
16371             Roo.each(this.selections, function(s) {
16372                 this.unselect(s, nodeInfo);
16373             }, this);
16374             return;
16375         }
16376         var node = this.getNode(nodeInfo);
16377         if(!node || !this.isSelected(node)){
16378             //Roo.log("not selected");
16379             return; // not selected.
16380         }
16381         // fireevent???
16382         var ns = [];
16383         Roo.each(this.selections, function(s) {
16384             if (s == node ) {
16385                 Roo.fly(node).removeClass(this.selectedClass);
16386
16387                 return;
16388             }
16389             ns.push(s);
16390         },this);
16391         
16392         this.selections= ns;
16393         this.fireEvent("selectionchange", this, this.selections);
16394     },
16395
16396     /**
16397      * Gets a template node.
16398      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16399      * @return {HTMLElement} The node or null if it wasn't found
16400      */
16401     getNode : function(nodeInfo){
16402         if(typeof nodeInfo == "string"){
16403             return document.getElementById(nodeInfo);
16404         }else if(typeof nodeInfo == "number"){
16405             return this.nodes[nodeInfo];
16406         }
16407         return nodeInfo;
16408     },
16409
16410     /**
16411      * Gets a range template nodes.
16412      * @param {Number} startIndex
16413      * @param {Number} endIndex
16414      * @return {Array} An array of nodes
16415      */
16416     getNodes : function(start, end){
16417         var ns = this.nodes;
16418         start = start || 0;
16419         end = typeof end == "undefined" ? ns.length - 1 : end;
16420         var nodes = [];
16421         if(start <= end){
16422             for(var i = start; i <= end; i++){
16423                 nodes.push(ns[i]);
16424             }
16425         } else{
16426             for(var i = start; i >= end; i--){
16427                 nodes.push(ns[i]);
16428             }
16429         }
16430         return nodes;
16431     },
16432
16433     /**
16434      * Finds the index of the passed node
16435      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16436      * @return {Number} The index of the node or -1
16437      */
16438     indexOf : function(node){
16439         node = this.getNode(node);
16440         if(typeof node.nodeIndex == "number"){
16441             return node.nodeIndex;
16442         }
16443         var ns = this.nodes;
16444         for(var i = 0, len = ns.length; i < len; i++){
16445             if(ns[i] == node){
16446                 return i;
16447             }
16448         }
16449         return -1;
16450     }
16451 });
16452 /*
16453  * - LGPL
16454  *
16455  * based on jquery fullcalendar
16456  * 
16457  */
16458
16459 Roo.bootstrap = Roo.bootstrap || {};
16460 /**
16461  * @class Roo.bootstrap.Calendar
16462  * @extends Roo.bootstrap.Component
16463  * Bootstrap Calendar class
16464  * @cfg {Boolean} loadMask (true|false) default false
16465  * @cfg {Object} header generate the user specific header of the calendar, default false
16466
16467  * @constructor
16468  * Create a new Container
16469  * @param {Object} config The config object
16470  */
16471
16472
16473
16474 Roo.bootstrap.Calendar = function(config){
16475     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16476      this.addEvents({
16477         /**
16478              * @event select
16479              * Fires when a date is selected
16480              * @param {DatePicker} this
16481              * @param {Date} date The selected date
16482              */
16483         'select': true,
16484         /**
16485              * @event monthchange
16486              * Fires when the displayed month changes 
16487              * @param {DatePicker} this
16488              * @param {Date} date The selected month
16489              */
16490         'monthchange': true,
16491         /**
16492              * @event evententer
16493              * Fires when mouse over an event
16494              * @param {Calendar} this
16495              * @param {event} Event
16496              */
16497         'evententer': true,
16498         /**
16499              * @event eventleave
16500              * Fires when the mouse leaves an
16501              * @param {Calendar} this
16502              * @param {event}
16503              */
16504         'eventleave': true,
16505         /**
16506              * @event eventclick
16507              * Fires when the mouse click an
16508              * @param {Calendar} this
16509              * @param {event}
16510              */
16511         'eventclick': true
16512         
16513     });
16514
16515 };
16516
16517 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16518     
16519      /**
16520      * @cfg {Number} startDay
16521      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16522      */
16523     startDay : 0,
16524     
16525     loadMask : false,
16526     
16527     header : false,
16528       
16529     getAutoCreate : function(){
16530         
16531         
16532         var fc_button = function(name, corner, style, content ) {
16533             return Roo.apply({},{
16534                 tag : 'span',
16535                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16536                          (corner.length ?
16537                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16538                             ''
16539                         ),
16540                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16541                 unselectable: 'on'
16542             });
16543         };
16544         
16545         var header = {};
16546         
16547         if(!this.header){
16548             header = {
16549                 tag : 'table',
16550                 cls : 'fc-header',
16551                 style : 'width:100%',
16552                 cn : [
16553                     {
16554                         tag: 'tr',
16555                         cn : [
16556                             {
16557                                 tag : 'td',
16558                                 cls : 'fc-header-left',
16559                                 cn : [
16560                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16561                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16562                                     { tag: 'span', cls: 'fc-header-space' },
16563                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16564
16565
16566                                 ]
16567                             },
16568
16569                             {
16570                                 tag : 'td',
16571                                 cls : 'fc-header-center',
16572                                 cn : [
16573                                     {
16574                                         tag: 'span',
16575                                         cls: 'fc-header-title',
16576                                         cn : {
16577                                             tag: 'H2',
16578                                             html : 'month / year'
16579                                         }
16580                                     }
16581
16582                                 ]
16583                             },
16584                             {
16585                                 tag : 'td',
16586                                 cls : 'fc-header-right',
16587                                 cn : [
16588                               /*      fc_button('month', 'left', '', 'month' ),
16589                                     fc_button('week', '', '', 'week' ),
16590                                     fc_button('day', 'right', '', 'day' )
16591                                 */    
16592
16593                                 ]
16594                             }
16595
16596                         ]
16597                     }
16598                 ]
16599             };
16600         }
16601         
16602         header = this.header;
16603         
16604        
16605         var cal_heads = function() {
16606             var ret = [];
16607             // fixme - handle this.
16608             
16609             for (var i =0; i < Date.dayNames.length; i++) {
16610                 var d = Date.dayNames[i];
16611                 ret.push({
16612                     tag: 'th',
16613                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16614                     html : d.substring(0,3)
16615                 });
16616                 
16617             }
16618             ret[0].cls += ' fc-first';
16619             ret[6].cls += ' fc-last';
16620             return ret;
16621         };
16622         var cal_cell = function(n) {
16623             return  {
16624                 tag: 'td',
16625                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16626                 cn : [
16627                     {
16628                         cn : [
16629                             {
16630                                 cls: 'fc-day-number',
16631                                 html: 'D'
16632                             },
16633                             {
16634                                 cls: 'fc-day-content',
16635                              
16636                                 cn : [
16637                                      {
16638                                         style: 'position: relative;' // height: 17px;
16639                                     }
16640                                 ]
16641                             }
16642                             
16643                             
16644                         ]
16645                     }
16646                 ]
16647                 
16648             }
16649         };
16650         var cal_rows = function() {
16651             
16652             var ret = [];
16653             for (var r = 0; r < 6; r++) {
16654                 var row= {
16655                     tag : 'tr',
16656                     cls : 'fc-week',
16657                     cn : []
16658                 };
16659                 
16660                 for (var i =0; i < Date.dayNames.length; i++) {
16661                     var d = Date.dayNames[i];
16662                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16663
16664                 }
16665                 row.cn[0].cls+=' fc-first';
16666                 row.cn[0].cn[0].style = 'min-height:90px';
16667                 row.cn[6].cls+=' fc-last';
16668                 ret.push(row);
16669                 
16670             }
16671             ret[0].cls += ' fc-first';
16672             ret[4].cls += ' fc-prev-last';
16673             ret[5].cls += ' fc-last';
16674             return ret;
16675             
16676         };
16677         
16678         var cal_table = {
16679             tag: 'table',
16680             cls: 'fc-border-separate',
16681             style : 'width:100%',
16682             cellspacing  : 0,
16683             cn : [
16684                 { 
16685                     tag: 'thead',
16686                     cn : [
16687                         { 
16688                             tag: 'tr',
16689                             cls : 'fc-first fc-last',
16690                             cn : cal_heads()
16691                         }
16692                     ]
16693                 },
16694                 { 
16695                     tag: 'tbody',
16696                     cn : cal_rows()
16697                 }
16698                   
16699             ]
16700         };
16701          
16702          var cfg = {
16703             cls : 'fc fc-ltr',
16704             cn : [
16705                 header,
16706                 {
16707                     cls : 'fc-content',
16708                     style : "position: relative;",
16709                     cn : [
16710                         {
16711                             cls : 'fc-view fc-view-month fc-grid',
16712                             style : 'position: relative',
16713                             unselectable : 'on',
16714                             cn : [
16715                                 {
16716                                     cls : 'fc-event-container',
16717                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16718                                 },
16719                                 cal_table
16720                             ]
16721                         }
16722                     ]
16723     
16724                 }
16725            ] 
16726             
16727         };
16728         
16729          
16730         
16731         return cfg;
16732     },
16733     
16734     
16735     initEvents : function()
16736     {
16737         if(!this.store){
16738             throw "can not find store for calendar";
16739         }
16740         
16741         var mark = {
16742             tag: "div",
16743             cls:"x-dlg-mask",
16744             style: "text-align:center",
16745             cn: [
16746                 {
16747                     tag: "div",
16748                     style: "background-color:white;width:50%;margin:250 auto",
16749                     cn: [
16750                         {
16751                             tag: "img",
16752                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16753                         },
16754                         {
16755                             tag: "span",
16756                             html: "Loading"
16757                         }
16758                         
16759                     ]
16760                 }
16761             ]
16762         };
16763         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16764         
16765         var size = this.el.select('.fc-content', true).first().getSize();
16766         this.maskEl.setSize(size.width, size.height);
16767         this.maskEl.enableDisplayMode("block");
16768         if(!this.loadMask){
16769             this.maskEl.hide();
16770         }
16771         
16772         this.store = Roo.factory(this.store, Roo.data);
16773         this.store.on('load', this.onLoad, this);
16774         this.store.on('beforeload', this.onBeforeLoad, this);
16775         
16776         this.resize();
16777         
16778         this.cells = this.el.select('.fc-day',true);
16779         //Roo.log(this.cells);
16780         this.textNodes = this.el.query('.fc-day-number');
16781         this.cells.addClassOnOver('fc-state-hover');
16782         
16783         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16784         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16785         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16786         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16787         
16788         this.on('monthchange', this.onMonthChange, this);
16789         
16790         this.update(new Date().clearTime());
16791     },
16792     
16793     resize : function() {
16794         var sz  = this.el.getSize();
16795         
16796         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16797         this.el.select('.fc-day-content div',true).setHeight(34);
16798     },
16799     
16800     
16801     // private
16802     showPrevMonth : function(e){
16803         this.update(this.activeDate.add("mo", -1));
16804     },
16805     showToday : function(e){
16806         this.update(new Date().clearTime());
16807     },
16808     // private
16809     showNextMonth : function(e){
16810         this.update(this.activeDate.add("mo", 1));
16811     },
16812
16813     // private
16814     showPrevYear : function(){
16815         this.update(this.activeDate.add("y", -1));
16816     },
16817
16818     // private
16819     showNextYear : function(){
16820         this.update(this.activeDate.add("y", 1));
16821     },
16822
16823     
16824    // private
16825     update : function(date)
16826     {
16827         var vd = this.activeDate;
16828         this.activeDate = date;
16829 //        if(vd && this.el){
16830 //            var t = date.getTime();
16831 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16832 //                Roo.log('using add remove');
16833 //                
16834 //                this.fireEvent('monthchange', this, date);
16835 //                
16836 //                this.cells.removeClass("fc-state-highlight");
16837 //                this.cells.each(function(c){
16838 //                   if(c.dateValue == t){
16839 //                       c.addClass("fc-state-highlight");
16840 //                       setTimeout(function(){
16841 //                            try{c.dom.firstChild.focus();}catch(e){}
16842 //                       }, 50);
16843 //                       return false;
16844 //                   }
16845 //                   return true;
16846 //                });
16847 //                return;
16848 //            }
16849 //        }
16850         
16851         var days = date.getDaysInMonth();
16852         
16853         var firstOfMonth = date.getFirstDateOfMonth();
16854         var startingPos = firstOfMonth.getDay()-this.startDay;
16855         
16856         if(startingPos < this.startDay){
16857             startingPos += 7;
16858         }
16859         
16860         var pm = date.add(Date.MONTH, -1);
16861         var prevStart = pm.getDaysInMonth()-startingPos;
16862 //        
16863         this.cells = this.el.select('.fc-day',true);
16864         this.textNodes = this.el.query('.fc-day-number');
16865         this.cells.addClassOnOver('fc-state-hover');
16866         
16867         var cells = this.cells.elements;
16868         var textEls = this.textNodes;
16869         
16870         Roo.each(cells, function(cell){
16871             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16872         });
16873         
16874         days += startingPos;
16875
16876         // convert everything to numbers so it's fast
16877         var day = 86400000;
16878         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16879         //Roo.log(d);
16880         //Roo.log(pm);
16881         //Roo.log(prevStart);
16882         
16883         var today = new Date().clearTime().getTime();
16884         var sel = date.clearTime().getTime();
16885         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16886         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16887         var ddMatch = this.disabledDatesRE;
16888         var ddText = this.disabledDatesText;
16889         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16890         var ddaysText = this.disabledDaysText;
16891         var format = this.format;
16892         
16893         var setCellClass = function(cal, cell){
16894             cell.row = 0;
16895             cell.events = [];
16896             cell.more = [];
16897             //Roo.log('set Cell Class');
16898             cell.title = "";
16899             var t = d.getTime();
16900             
16901             //Roo.log(d);
16902             
16903             cell.dateValue = t;
16904             if(t == today){
16905                 cell.className += " fc-today";
16906                 cell.className += " fc-state-highlight";
16907                 cell.title = cal.todayText;
16908             }
16909             if(t == sel){
16910                 // disable highlight in other month..
16911                 //cell.className += " fc-state-highlight";
16912                 
16913             }
16914             // disabling
16915             if(t < min) {
16916                 cell.className = " fc-state-disabled";
16917                 cell.title = cal.minText;
16918                 return;
16919             }
16920             if(t > max) {
16921                 cell.className = " fc-state-disabled";
16922                 cell.title = cal.maxText;
16923                 return;
16924             }
16925             if(ddays){
16926                 if(ddays.indexOf(d.getDay()) != -1){
16927                     cell.title = ddaysText;
16928                     cell.className = " fc-state-disabled";
16929                 }
16930             }
16931             if(ddMatch && format){
16932                 var fvalue = d.dateFormat(format);
16933                 if(ddMatch.test(fvalue)){
16934                     cell.title = ddText.replace("%0", fvalue);
16935                     cell.className = " fc-state-disabled";
16936                 }
16937             }
16938             
16939             if (!cell.initialClassName) {
16940                 cell.initialClassName = cell.dom.className;
16941             }
16942             
16943             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16944         };
16945
16946         var i = 0;
16947         
16948         for(; i < startingPos; i++) {
16949             textEls[i].innerHTML = (++prevStart);
16950             d.setDate(d.getDate()+1);
16951             
16952             cells[i].className = "fc-past fc-other-month";
16953             setCellClass(this, cells[i]);
16954         }
16955         
16956         var intDay = 0;
16957         
16958         for(; i < days; i++){
16959             intDay = i - startingPos + 1;
16960             textEls[i].innerHTML = (intDay);
16961             d.setDate(d.getDate()+1);
16962             
16963             cells[i].className = ''; // "x-date-active";
16964             setCellClass(this, cells[i]);
16965         }
16966         var extraDays = 0;
16967         
16968         for(; i < 42; i++) {
16969             textEls[i].innerHTML = (++extraDays);
16970             d.setDate(d.getDate()+1);
16971             
16972             cells[i].className = "fc-future fc-other-month";
16973             setCellClass(this, cells[i]);
16974         }
16975         
16976         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16977         
16978         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16979         
16980         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16981         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16982         
16983         if(totalRows != 6){
16984             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16985             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16986         }
16987         
16988         this.fireEvent('monthchange', this, date);
16989         
16990         
16991         /*
16992         if(!this.internalRender){
16993             var main = this.el.dom.firstChild;
16994             var w = main.offsetWidth;
16995             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16996             Roo.fly(main).setWidth(w);
16997             this.internalRender = true;
16998             // opera does not respect the auto grow header center column
16999             // then, after it gets a width opera refuses to recalculate
17000             // without a second pass
17001             if(Roo.isOpera && !this.secondPass){
17002                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17003                 this.secondPass = true;
17004                 this.update.defer(10, this, [date]);
17005             }
17006         }
17007         */
17008         
17009     },
17010     
17011     findCell : function(dt) {
17012         dt = dt.clearTime().getTime();
17013         var ret = false;
17014         this.cells.each(function(c){
17015             //Roo.log("check " +c.dateValue + '?=' + dt);
17016             if(c.dateValue == dt){
17017                 ret = c;
17018                 return false;
17019             }
17020             return true;
17021         });
17022         
17023         return ret;
17024     },
17025     
17026     findCells : function(ev) {
17027         var s = ev.start.clone().clearTime().getTime();
17028        // Roo.log(s);
17029         var e= ev.end.clone().clearTime().getTime();
17030        // Roo.log(e);
17031         var ret = [];
17032         this.cells.each(function(c){
17033              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17034             
17035             if(c.dateValue > e){
17036                 return ;
17037             }
17038             if(c.dateValue < s){
17039                 return ;
17040             }
17041             ret.push(c);
17042         });
17043         
17044         return ret;    
17045     },
17046     
17047 //    findBestRow: function(cells)
17048 //    {
17049 //        var ret = 0;
17050 //        
17051 //        for (var i =0 ; i < cells.length;i++) {
17052 //            ret  = Math.max(cells[i].rows || 0,ret);
17053 //        }
17054 //        return ret;
17055 //        
17056 //    },
17057     
17058     
17059     addItem : function(ev)
17060     {
17061         // look for vertical location slot in
17062         var cells = this.findCells(ev);
17063         
17064 //        ev.row = this.findBestRow(cells);
17065         
17066         // work out the location.
17067         
17068         var crow = false;
17069         var rows = [];
17070         for(var i =0; i < cells.length; i++) {
17071             
17072             cells[i].row = cells[0].row;
17073             
17074             if(i == 0){
17075                 cells[i].row = cells[i].row + 1;
17076             }
17077             
17078             if (!crow) {
17079                 crow = {
17080                     start : cells[i],
17081                     end :  cells[i]
17082                 };
17083                 continue;
17084             }
17085             if (crow.start.getY() == cells[i].getY()) {
17086                 // on same row.
17087                 crow.end = cells[i];
17088                 continue;
17089             }
17090             // different row.
17091             rows.push(crow);
17092             crow = {
17093                 start: cells[i],
17094                 end : cells[i]
17095             };
17096             
17097         }
17098         
17099         rows.push(crow);
17100         ev.els = [];
17101         ev.rows = rows;
17102         ev.cells = cells;
17103         
17104         cells[0].events.push(ev);
17105         
17106         this.calevents.push(ev);
17107     },
17108     
17109     clearEvents: function() {
17110         
17111         if(!this.calevents){
17112             return;
17113         }
17114         
17115         Roo.each(this.cells.elements, function(c){
17116             c.row = 0;
17117             c.events = [];
17118             c.more = [];
17119         });
17120         
17121         Roo.each(this.calevents, function(e) {
17122             Roo.each(e.els, function(el) {
17123                 el.un('mouseenter' ,this.onEventEnter, this);
17124                 el.un('mouseleave' ,this.onEventLeave, this);
17125                 el.remove();
17126             },this);
17127         },this);
17128         
17129         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17130             e.remove();
17131         });
17132         
17133     },
17134     
17135     renderEvents: function()
17136     {   
17137         var _this = this;
17138         
17139         this.cells.each(function(c) {
17140             
17141             if(c.row < 5){
17142                 return;
17143             }
17144             
17145             var ev = c.events;
17146             
17147             var r = 4;
17148             if(c.row != c.events.length){
17149                 r = 4 - (4 - (c.row - c.events.length));
17150             }
17151             
17152             c.events = ev.slice(0, r);
17153             c.more = ev.slice(r);
17154             
17155             if(c.more.length && c.more.length == 1){
17156                 c.events.push(c.more.pop());
17157             }
17158             
17159             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17160             
17161         });
17162             
17163         this.cells.each(function(c) {
17164             
17165             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17166             
17167             
17168             for (var e = 0; e < c.events.length; e++){
17169                 var ev = c.events[e];
17170                 var rows = ev.rows;
17171                 
17172                 for(var i = 0; i < rows.length; i++) {
17173                 
17174                     // how many rows should it span..
17175
17176                     var  cfg = {
17177                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17178                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17179
17180                         unselectable : "on",
17181                         cn : [
17182                             {
17183                                 cls: 'fc-event-inner',
17184                                 cn : [
17185     //                                {
17186     //                                  tag:'span',
17187     //                                  cls: 'fc-event-time',
17188     //                                  html : cells.length > 1 ? '' : ev.time
17189     //                                },
17190                                     {
17191                                       tag:'span',
17192                                       cls: 'fc-event-title',
17193                                       html : String.format('{0}', ev.title)
17194                                     }
17195
17196
17197                                 ]
17198                             },
17199                             {
17200                                 cls: 'ui-resizable-handle ui-resizable-e',
17201                                 html : '&nbsp;&nbsp;&nbsp'
17202                             }
17203
17204                         ]
17205                     };
17206
17207                     if (i == 0) {
17208                         cfg.cls += ' fc-event-start';
17209                     }
17210                     if ((i+1) == rows.length) {
17211                         cfg.cls += ' fc-event-end';
17212                     }
17213
17214                     var ctr = _this.el.select('.fc-event-container',true).first();
17215                     var cg = ctr.createChild(cfg);
17216
17217                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17218                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17219
17220                     var r = (c.more.length) ? 1 : 0;
17221                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17222                     cg.setWidth(ebox.right - sbox.x -2);
17223
17224                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17225                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17226                     cg.on('click', _this.onEventClick, _this, ev);
17227
17228                     ev.els.push(cg);
17229                     
17230                 }
17231                 
17232             }
17233             
17234             
17235             if(c.more.length){
17236                 var  cfg = {
17237                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17238                     style : 'position: absolute',
17239                     unselectable : "on",
17240                     cn : [
17241                         {
17242                             cls: 'fc-event-inner',
17243                             cn : [
17244                                 {
17245                                   tag:'span',
17246                                   cls: 'fc-event-title',
17247                                   html : 'More'
17248                                 }
17249
17250
17251                             ]
17252                         },
17253                         {
17254                             cls: 'ui-resizable-handle ui-resizable-e',
17255                             html : '&nbsp;&nbsp;&nbsp'
17256                         }
17257
17258                     ]
17259                 };
17260
17261                 var ctr = _this.el.select('.fc-event-container',true).first();
17262                 var cg = ctr.createChild(cfg);
17263
17264                 var sbox = c.select('.fc-day-content',true).first().getBox();
17265                 var ebox = c.select('.fc-day-content',true).first().getBox();
17266                 //Roo.log(cg);
17267                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17268                 cg.setWidth(ebox.right - sbox.x -2);
17269
17270                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17271                 
17272             }
17273             
17274         });
17275         
17276         
17277         
17278     },
17279     
17280     onEventEnter: function (e, el,event,d) {
17281         this.fireEvent('evententer', this, el, event);
17282     },
17283     
17284     onEventLeave: function (e, el,event,d) {
17285         this.fireEvent('eventleave', this, el, event);
17286     },
17287     
17288     onEventClick: function (e, el,event,d) {
17289         this.fireEvent('eventclick', this, el, event);
17290     },
17291     
17292     onMonthChange: function () {
17293         this.store.load();
17294     },
17295     
17296     onMoreEventClick: function(e, el, more)
17297     {
17298         var _this = this;
17299         
17300         this.calpopover.placement = 'right';
17301         this.calpopover.setTitle('More');
17302         
17303         this.calpopover.setContent('');
17304         
17305         var ctr = this.calpopover.el.select('.popover-content', true).first();
17306         
17307         Roo.each(more, function(m){
17308             var cfg = {
17309                 cls : 'fc-event-hori fc-event-draggable',
17310                 html : m.title
17311             };
17312             var cg = ctr.createChild(cfg);
17313             
17314             cg.on('click', _this.onEventClick, _this, m);
17315         });
17316         
17317         this.calpopover.show(el);
17318         
17319         
17320     },
17321     
17322     onLoad: function () 
17323     {   
17324         this.calevents = [];
17325         var cal = this;
17326         
17327         if(this.store.getCount() > 0){
17328             this.store.data.each(function(d){
17329                cal.addItem({
17330                     id : d.data.id,
17331                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17332                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17333                     time : d.data.start_time,
17334                     title : d.data.title,
17335                     description : d.data.description,
17336                     venue : d.data.venue
17337                 });
17338             });
17339         }
17340         
17341         this.renderEvents();
17342         
17343         if(this.calevents.length && this.loadMask){
17344             this.maskEl.hide();
17345         }
17346     },
17347     
17348     onBeforeLoad: function()
17349     {
17350         this.clearEvents();
17351         if(this.loadMask){
17352             this.maskEl.show();
17353         }
17354     }
17355 });
17356
17357  
17358  /*
17359  * - LGPL
17360  *
17361  * element
17362  * 
17363  */
17364
17365 /**
17366  * @class Roo.bootstrap.Popover
17367  * @extends Roo.bootstrap.Component
17368  * Bootstrap Popover class
17369  * @cfg {String} html contents of the popover   (or false to use children..)
17370  * @cfg {String} title of popover (or false to hide)
17371  * @cfg {String} placement how it is placed
17372  * @cfg {String} trigger click || hover (or false to trigger manually)
17373  * @cfg {String} over what (parent or false to trigger manually.)
17374  * @cfg {Number} delay - delay before showing
17375  
17376  * @constructor
17377  * Create a new Popover
17378  * @param {Object} config The config object
17379  */
17380
17381 Roo.bootstrap.Popover = function(config){
17382     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17383     
17384     this.addEvents({
17385         // raw events
17386          /**
17387          * @event show
17388          * After the popover show
17389          * 
17390          * @param {Roo.bootstrap.Popover} this
17391          */
17392         "show" : true,
17393         /**
17394          * @event hide
17395          * After the popover hide
17396          * 
17397          * @param {Roo.bootstrap.Popover} this
17398          */
17399         "hide" : true
17400     });
17401 };
17402
17403 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17404     
17405     title: 'Fill in a title',
17406     html: false,
17407     
17408     placement : 'right',
17409     trigger : 'hover', // hover
17410     
17411     delay : 0,
17412     
17413     over: 'parent',
17414     
17415     can_build_overlaid : false,
17416     
17417     getChildContainer : function()
17418     {
17419         return this.el.select('.popover-content',true).first();
17420     },
17421     
17422     getAutoCreate : function(){
17423          
17424         var cfg = {
17425            cls : 'popover roo-dynamic',
17426            style: 'display:block',
17427            cn : [
17428                 {
17429                     cls : 'arrow'
17430                 },
17431                 {
17432                     cls : 'popover-inner',
17433                     cn : [
17434                         {
17435                             tag: 'h3',
17436                             cls: 'popover-title',
17437                             html : this.title
17438                         },
17439                         {
17440                             cls : 'popover-content',
17441                             html : this.html
17442                         }
17443                     ]
17444                     
17445                 }
17446            ]
17447         };
17448         
17449         return cfg;
17450     },
17451     setTitle: function(str)
17452     {
17453         this.title = str;
17454         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17455     },
17456     setContent: function(str)
17457     {
17458         this.html = str;
17459         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17460     },
17461     // as it get's added to the bottom of the page.
17462     onRender : function(ct, position)
17463     {
17464         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17465         if(!this.el){
17466             var cfg = Roo.apply({},  this.getAutoCreate());
17467             cfg.id = Roo.id();
17468             
17469             if (this.cls) {
17470                 cfg.cls += ' ' + this.cls;
17471             }
17472             if (this.style) {
17473                 cfg.style = this.style;
17474             }
17475             //Roo.log("adding to ");
17476             this.el = Roo.get(document.body).createChild(cfg, position);
17477 //            Roo.log(this.el);
17478         }
17479         this.initEvents();
17480     },
17481     
17482     initEvents : function()
17483     {
17484         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17485         this.el.enableDisplayMode('block');
17486         this.el.hide();
17487         if (this.over === false) {
17488             return; 
17489         }
17490         if (this.triggers === false) {
17491             return;
17492         }
17493         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17494         var triggers = this.trigger ? this.trigger.split(' ') : [];
17495         Roo.each(triggers, function(trigger) {
17496         
17497             if (trigger == 'click') {
17498                 on_el.on('click', this.toggle, this);
17499             } else if (trigger != 'manual') {
17500                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17501                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17502       
17503                 on_el.on(eventIn  ,this.enter, this);
17504                 on_el.on(eventOut, this.leave, this);
17505             }
17506         }, this);
17507         
17508     },
17509     
17510     
17511     // private
17512     timeout : null,
17513     hoverState : null,
17514     
17515     toggle : function () {
17516         this.hoverState == 'in' ? this.leave() : this.enter();
17517     },
17518     
17519     enter : function () {
17520         
17521         clearTimeout(this.timeout);
17522     
17523         this.hoverState = 'in';
17524     
17525         if (!this.delay || !this.delay.show) {
17526             this.show();
17527             return;
17528         }
17529         var _t = this;
17530         this.timeout = setTimeout(function () {
17531             if (_t.hoverState == 'in') {
17532                 _t.show();
17533             }
17534         }, this.delay.show)
17535     },
17536     
17537     leave : function() {
17538         clearTimeout(this.timeout);
17539     
17540         this.hoverState = 'out';
17541     
17542         if (!this.delay || !this.delay.hide) {
17543             this.hide();
17544             return;
17545         }
17546         var _t = this;
17547         this.timeout = setTimeout(function () {
17548             if (_t.hoverState == 'out') {
17549                 _t.hide();
17550             }
17551         }, this.delay.hide)
17552     },
17553     
17554     show : function (on_el)
17555     {
17556         if (!on_el) {
17557             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17558         }
17559         
17560         // set content.
17561         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17562         if (this.html !== false) {
17563             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17564         }
17565         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17566         if (!this.title.length) {
17567             this.el.select('.popover-title',true).hide();
17568         }
17569         
17570         var placement = typeof this.placement == 'function' ?
17571             this.placement.call(this, this.el, on_el) :
17572             this.placement;
17573             
17574         var autoToken = /\s?auto?\s?/i;
17575         var autoPlace = autoToken.test(placement);
17576         if (autoPlace) {
17577             placement = placement.replace(autoToken, '') || 'top';
17578         }
17579         
17580         //this.el.detach()
17581         //this.el.setXY([0,0]);
17582         this.el.show();
17583         this.el.dom.style.display='block';
17584         this.el.addClass(placement);
17585         
17586         //this.el.appendTo(on_el);
17587         
17588         var p = this.getPosition();
17589         var box = this.el.getBox();
17590         
17591         if (autoPlace) {
17592             // fixme..
17593         }
17594         var align = Roo.bootstrap.Popover.alignment[placement];
17595         
17596 //        Roo.log(align);
17597         this.el.alignTo(on_el, align[0],align[1]);
17598         //var arrow = this.el.select('.arrow',true).first();
17599         //arrow.set(align[2], 
17600         
17601         this.el.addClass('in');
17602         
17603         
17604         if (this.el.hasClass('fade')) {
17605             // fade it?
17606         }
17607         
17608         this.hoverState = 'in';
17609         
17610         this.fireEvent('show', this);
17611         
17612     },
17613     hide : function()
17614     {
17615         this.el.setXY([0,0]);
17616         this.el.removeClass('in');
17617         this.el.hide();
17618         this.hoverState = null;
17619         
17620         this.fireEvent('hide', this);
17621     }
17622     
17623 });
17624
17625 Roo.bootstrap.Popover.alignment = {
17626     'left' : ['r-l', [-10,0], 'right'],
17627     'right' : ['l-r', [10,0], 'left'],
17628     'bottom' : ['t-b', [0,10], 'top'],
17629     'top' : [ 'b-t', [0,-10], 'bottom']
17630 };
17631
17632  /*
17633  * - LGPL
17634  *
17635  * Progress
17636  * 
17637  */
17638
17639 /**
17640  * @class Roo.bootstrap.Progress
17641  * @extends Roo.bootstrap.Component
17642  * Bootstrap Progress class
17643  * @cfg {Boolean} striped striped of the progress bar
17644  * @cfg {Boolean} active animated of the progress bar
17645  * 
17646  * 
17647  * @constructor
17648  * Create a new Progress
17649  * @param {Object} config The config object
17650  */
17651
17652 Roo.bootstrap.Progress = function(config){
17653     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17654 };
17655
17656 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17657     
17658     striped : false,
17659     active: false,
17660     
17661     getAutoCreate : function(){
17662         var cfg = {
17663             tag: 'div',
17664             cls: 'progress'
17665         };
17666         
17667         
17668         if(this.striped){
17669             cfg.cls += ' progress-striped';
17670         }
17671       
17672         if(this.active){
17673             cfg.cls += ' active';
17674         }
17675         
17676         
17677         return cfg;
17678     }
17679    
17680 });
17681
17682  
17683
17684  /*
17685  * - LGPL
17686  *
17687  * ProgressBar
17688  * 
17689  */
17690
17691 /**
17692  * @class Roo.bootstrap.ProgressBar
17693  * @extends Roo.bootstrap.Component
17694  * Bootstrap ProgressBar class
17695  * @cfg {Number} aria_valuenow aria-value now
17696  * @cfg {Number} aria_valuemin aria-value min
17697  * @cfg {Number} aria_valuemax aria-value max
17698  * @cfg {String} label label for the progress bar
17699  * @cfg {String} panel (success | info | warning | danger )
17700  * @cfg {String} role role of the progress bar
17701  * @cfg {String} sr_only text
17702  * 
17703  * 
17704  * @constructor
17705  * Create a new ProgressBar
17706  * @param {Object} config The config object
17707  */
17708
17709 Roo.bootstrap.ProgressBar = function(config){
17710     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17711 };
17712
17713 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17714     
17715     aria_valuenow : 0,
17716     aria_valuemin : 0,
17717     aria_valuemax : 100,
17718     label : false,
17719     panel : false,
17720     role : false,
17721     sr_only: false,
17722     
17723     getAutoCreate : function()
17724     {
17725         
17726         var cfg = {
17727             tag: 'div',
17728             cls: 'progress-bar',
17729             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17730         };
17731         
17732         if(this.sr_only){
17733             cfg.cn = {
17734                 tag: 'span',
17735                 cls: 'sr-only',
17736                 html: this.sr_only
17737             }
17738         }
17739         
17740         if(this.role){
17741             cfg.role = this.role;
17742         }
17743         
17744         if(this.aria_valuenow){
17745             cfg['aria-valuenow'] = this.aria_valuenow;
17746         }
17747         
17748         if(this.aria_valuemin){
17749             cfg['aria-valuemin'] = this.aria_valuemin;
17750         }
17751         
17752         if(this.aria_valuemax){
17753             cfg['aria-valuemax'] = this.aria_valuemax;
17754         }
17755         
17756         if(this.label && !this.sr_only){
17757             cfg.html = this.label;
17758         }
17759         
17760         if(this.panel){
17761             cfg.cls += ' progress-bar-' + this.panel;
17762         }
17763         
17764         return cfg;
17765     },
17766     
17767     update : function(aria_valuenow)
17768     {
17769         this.aria_valuenow = aria_valuenow;
17770         
17771         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17772     }
17773    
17774 });
17775
17776  
17777
17778  /*
17779  * - LGPL
17780  *
17781  * column
17782  * 
17783  */
17784
17785 /**
17786  * @class Roo.bootstrap.TabGroup
17787  * @extends Roo.bootstrap.Column
17788  * Bootstrap Column class
17789  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17790  * @cfg {Boolean} carousel true to make the group behave like a carousel
17791  * @cfg {Boolean} bullets show bullets for the panels
17792  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17793  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17794  * @cfg {Boolean} showarrow (true|false) show arrow default true
17795  * 
17796  * @constructor
17797  * Create a new TabGroup
17798  * @param {Object} config The config object
17799  */
17800
17801 Roo.bootstrap.TabGroup = function(config){
17802     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17803     if (!this.navId) {
17804         this.navId = Roo.id();
17805     }
17806     this.tabs = [];
17807     Roo.bootstrap.TabGroup.register(this);
17808     
17809 };
17810
17811 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17812     
17813     carousel : false,
17814     transition : false,
17815     bullets : 0,
17816     timer : 0,
17817     autoslide : false,
17818     slideFn : false,
17819     slideOnTouch : false,
17820     showarrow : true,
17821     
17822     getAutoCreate : function()
17823     {
17824         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17825         
17826         cfg.cls += ' tab-content';
17827         
17828         if (this.carousel) {
17829             cfg.cls += ' carousel slide';
17830             
17831             cfg.cn = [{
17832                cls : 'carousel-inner',
17833                cn : []
17834             }];
17835         
17836             if(this.bullets  && !Roo.isTouch){
17837                 
17838                 var bullets = {
17839                     cls : 'carousel-bullets',
17840                     cn : []
17841                 };
17842                
17843                 if(this.bullets_cls){
17844                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17845                 }
17846                 
17847                 bullets.cn.push({
17848                     cls : 'clear'
17849                 });
17850                 
17851                 cfg.cn[0].cn.push(bullets);
17852             }
17853             
17854             if(this.showarrow){
17855                 cfg.cn[0].cn.push({
17856                     tag : 'div',
17857                     class : 'carousel-arrow',
17858                     cn : [
17859                         {
17860                             tag : 'div',
17861                             class : 'carousel-prev',
17862                             cn : [
17863                                 {
17864                                     tag : 'i',
17865                                     class : 'fa fa-chevron-left'
17866                                 }
17867                             ]
17868                         },
17869                         {
17870                             tag : 'div',
17871                             class : 'carousel-next',
17872                             cn : [
17873                                 {
17874                                     tag : 'i',
17875                                     class : 'fa fa-chevron-right'
17876                                 }
17877                             ]
17878                         }
17879                     ]
17880                 });
17881             }
17882             
17883         }
17884         
17885         return cfg;
17886     },
17887     
17888     initEvents:  function()
17889     {
17890 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17891 //            this.el.on("touchstart", this.onTouchStart, this);
17892 //        }
17893         
17894         if(this.autoslide){
17895             var _this = this;
17896             
17897             this.slideFn = window.setInterval(function() {
17898                 _this.showPanelNext();
17899             }, this.timer);
17900         }
17901         
17902         if(this.showarrow){
17903             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17904             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17905         }
17906         
17907         
17908     },
17909     
17910 //    onTouchStart : function(e, el, o)
17911 //    {
17912 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17913 //            return;
17914 //        }
17915 //        
17916 //        this.showPanelNext();
17917 //    },
17918     
17919     
17920     getChildContainer : function()
17921     {
17922         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17923     },
17924     
17925     /**
17926     * register a Navigation item
17927     * @param {Roo.bootstrap.NavItem} the navitem to add
17928     */
17929     register : function(item)
17930     {
17931         this.tabs.push( item);
17932         item.navId = this.navId; // not really needed..
17933         this.addBullet();
17934     
17935     },
17936     
17937     getActivePanel : function()
17938     {
17939         var r = false;
17940         Roo.each(this.tabs, function(t) {
17941             if (t.active) {
17942                 r = t;
17943                 return false;
17944             }
17945             return null;
17946         });
17947         return r;
17948         
17949     },
17950     getPanelByName : function(n)
17951     {
17952         var r = false;
17953         Roo.each(this.tabs, function(t) {
17954             if (t.tabId == n) {
17955                 r = t;
17956                 return false;
17957             }
17958             return null;
17959         });
17960         return r;
17961     },
17962     indexOfPanel : function(p)
17963     {
17964         var r = false;
17965         Roo.each(this.tabs, function(t,i) {
17966             if (t.tabId == p.tabId) {
17967                 r = i;
17968                 return false;
17969             }
17970             return null;
17971         });
17972         return r;
17973     },
17974     /**
17975      * show a specific panel
17976      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17977      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17978      */
17979     showPanel : function (pan)
17980     {
17981         if(this.transition || typeof(pan) == 'undefined'){
17982             Roo.log("waiting for the transitionend");
17983             return;
17984         }
17985         
17986         if (typeof(pan) == 'number') {
17987             pan = this.tabs[pan];
17988         }
17989         
17990         if (typeof(pan) == 'string') {
17991             pan = this.getPanelByName(pan);
17992         }
17993         
17994         var cur = this.getActivePanel();
17995         
17996         if(!pan || !cur){
17997             Roo.log('pan or acitve pan is undefined');
17998             return false;
17999         }
18000         
18001         if (pan.tabId == this.getActivePanel().tabId) {
18002             return true;
18003         }
18004         
18005         if (false === cur.fireEvent('beforedeactivate')) {
18006             return false;
18007         }
18008         
18009         if(this.bullets > 0 && !Roo.isTouch){
18010             this.setActiveBullet(this.indexOfPanel(pan));
18011         }
18012         
18013         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18014             
18015             this.transition = true;
18016             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18017             var lr = dir == 'next' ? 'left' : 'right';
18018             pan.el.addClass(dir); // or prev
18019             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18020             cur.el.addClass(lr); // or right
18021             pan.el.addClass(lr);
18022             
18023             var _this = this;
18024             cur.el.on('transitionend', function() {
18025                 Roo.log("trans end?");
18026                 
18027                 pan.el.removeClass([lr,dir]);
18028                 pan.setActive(true);
18029                 
18030                 cur.el.removeClass([lr]);
18031                 cur.setActive(false);
18032                 
18033                 _this.transition = false;
18034                 
18035             }, this, { single:  true } );
18036             
18037             return true;
18038         }
18039         
18040         cur.setActive(false);
18041         pan.setActive(true);
18042         
18043         return true;
18044         
18045     },
18046     showPanelNext : function()
18047     {
18048         var i = this.indexOfPanel(this.getActivePanel());
18049         
18050         if (i >= this.tabs.length - 1 && !this.autoslide) {
18051             return;
18052         }
18053         
18054         if (i >= this.tabs.length - 1 && this.autoslide) {
18055             i = -1;
18056         }
18057         
18058         this.showPanel(this.tabs[i+1]);
18059     },
18060     
18061     showPanelPrev : function()
18062     {
18063         var i = this.indexOfPanel(this.getActivePanel());
18064         
18065         if (i  < 1 && !this.autoslide) {
18066             return;
18067         }
18068         
18069         if (i < 1 && this.autoslide) {
18070             i = this.tabs.length;
18071         }
18072         
18073         this.showPanel(this.tabs[i-1]);
18074     },
18075     
18076     
18077     addBullet: function()
18078     {
18079         if(!this.bullets || Roo.isTouch){
18080             return;
18081         }
18082         var ctr = this.el.select('.carousel-bullets',true).first();
18083         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18084         var bullet = ctr.createChild({
18085             cls : 'bullet bullet-' + i
18086         },ctr.dom.lastChild);
18087         
18088         
18089         var _this = this;
18090         
18091         bullet.on('click', (function(e, el, o, ii, t){
18092
18093             e.preventDefault();
18094
18095             this.showPanel(ii);
18096
18097             if(this.autoslide && this.slideFn){
18098                 clearInterval(this.slideFn);
18099                 this.slideFn = window.setInterval(function() {
18100                     _this.showPanelNext();
18101                 }, this.timer);
18102             }
18103
18104         }).createDelegate(this, [i, bullet], true));
18105                 
18106         
18107     },
18108      
18109     setActiveBullet : function(i)
18110     {
18111         if(Roo.isTouch){
18112             return;
18113         }
18114         
18115         Roo.each(this.el.select('.bullet', true).elements, function(el){
18116             el.removeClass('selected');
18117         });
18118
18119         var bullet = this.el.select('.bullet-' + i, true).first();
18120         
18121         if(!bullet){
18122             return;
18123         }
18124         
18125         bullet.addClass('selected');
18126     }
18127     
18128     
18129   
18130 });
18131
18132  
18133
18134  
18135  
18136 Roo.apply(Roo.bootstrap.TabGroup, {
18137     
18138     groups: {},
18139      /**
18140     * register a Navigation Group
18141     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18142     */
18143     register : function(navgrp)
18144     {
18145         this.groups[navgrp.navId] = navgrp;
18146         
18147     },
18148     /**
18149     * fetch a Navigation Group based on the navigation ID
18150     * if one does not exist , it will get created.
18151     * @param {string} the navgroup to add
18152     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18153     */
18154     get: function(navId) {
18155         if (typeof(this.groups[navId]) == 'undefined') {
18156             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18157         }
18158         return this.groups[navId] ;
18159     }
18160     
18161     
18162     
18163 });
18164
18165  /*
18166  * - LGPL
18167  *
18168  * TabPanel
18169  * 
18170  */
18171
18172 /**
18173  * @class Roo.bootstrap.TabPanel
18174  * @extends Roo.bootstrap.Component
18175  * Bootstrap TabPanel class
18176  * @cfg {Boolean} active panel active
18177  * @cfg {String} html panel content
18178  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18179  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18180  * @cfg {String} href click to link..
18181  * 
18182  * 
18183  * @constructor
18184  * Create a new TabPanel
18185  * @param {Object} config The config object
18186  */
18187
18188 Roo.bootstrap.TabPanel = function(config){
18189     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18190     this.addEvents({
18191         /**
18192              * @event changed
18193              * Fires when the active status changes
18194              * @param {Roo.bootstrap.TabPanel} this
18195              * @param {Boolean} state the new state
18196             
18197          */
18198         'changed': true,
18199         /**
18200              * @event beforedeactivate
18201              * Fires before a tab is de-activated - can be used to do validation on a form.
18202              * @param {Roo.bootstrap.TabPanel} this
18203              * @return {Boolean} false if there is an error
18204             
18205          */
18206         'beforedeactivate': true
18207      });
18208     
18209     this.tabId = this.tabId || Roo.id();
18210   
18211 };
18212
18213 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18214     
18215     active: false,
18216     html: false,
18217     tabId: false,
18218     navId : false,
18219     href : '',
18220     
18221     getAutoCreate : function(){
18222         var cfg = {
18223             tag: 'div',
18224             // item is needed for carousel - not sure if it has any effect otherwise
18225             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18226             html: this.html || ''
18227         };
18228         
18229         if(this.active){
18230             cfg.cls += ' active';
18231         }
18232         
18233         if(this.tabId){
18234             cfg.tabId = this.tabId;
18235         }
18236         
18237         
18238         return cfg;
18239     },
18240     
18241     initEvents:  function()
18242     {
18243         var p = this.parent();
18244         
18245         this.navId = this.navId || p.navId;
18246         
18247         if (typeof(this.navId) != 'undefined') {
18248             // not really needed.. but just in case.. parent should be a NavGroup.
18249             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18250             
18251             tg.register(this);
18252             
18253             var i = tg.tabs.length - 1;
18254             
18255             if(this.active && tg.bullets > 0 && i < tg.bullets){
18256                 tg.setActiveBullet(i);
18257             }
18258         }
18259         
18260         this.el.on('click', this.onClick, this);
18261         
18262         if(Roo.isTouch){
18263             this.el.on("touchstart", this.onTouchStart, this);
18264             this.el.on("touchmove", this.onTouchMove, this);
18265             this.el.on("touchend", this.onTouchEnd, this);
18266         }
18267         
18268     },
18269     
18270     onRender : function(ct, position)
18271     {
18272         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18273     },
18274     
18275     setActive : function(state)
18276     {
18277         Roo.log("panel - set active " + this.tabId + "=" + state);
18278         
18279         this.active = state;
18280         if (!state) {
18281             this.el.removeClass('active');
18282             
18283         } else  if (!this.el.hasClass('active')) {
18284             this.el.addClass('active');
18285         }
18286         
18287         this.fireEvent('changed', this, state);
18288     },
18289     
18290     onClick : function(e)
18291     {
18292         e.preventDefault();
18293         
18294         if(!this.href.length){
18295             return;
18296         }
18297         
18298         window.location.href = this.href;
18299     },
18300     
18301     startX : 0,
18302     startY : 0,
18303     endX : 0,
18304     endY : 0,
18305     swiping : false,
18306     
18307     onTouchStart : function(e)
18308     {
18309         this.swiping = false;
18310         
18311         this.startX = e.browserEvent.touches[0].clientX;
18312         this.startY = e.browserEvent.touches[0].clientY;
18313     },
18314     
18315     onTouchMove : function(e)
18316     {
18317         this.swiping = true;
18318         
18319         this.endX = e.browserEvent.touches[0].clientX;
18320         this.endY = e.browserEvent.touches[0].clientY;
18321     },
18322     
18323     onTouchEnd : function(e)
18324     {
18325         if(!this.swiping){
18326             this.onClick(e);
18327             return;
18328         }
18329         
18330         var tabGroup = this.parent();
18331         
18332         if(this.endX > this.startX){ // swiping right
18333             tabGroup.showPanelPrev();
18334             return;
18335         }
18336         
18337         if(this.startX > this.endX){ // swiping left
18338             tabGroup.showPanelNext();
18339             return;
18340         }
18341     }
18342     
18343     
18344 });
18345  
18346
18347  
18348
18349  /*
18350  * - LGPL
18351  *
18352  * DateField
18353  * 
18354  */
18355
18356 /**
18357  * @class Roo.bootstrap.DateField
18358  * @extends Roo.bootstrap.Input
18359  * Bootstrap DateField class
18360  * @cfg {Number} weekStart default 0
18361  * @cfg {String} viewMode default empty, (months|years)
18362  * @cfg {String} minViewMode default empty, (months|years)
18363  * @cfg {Number} startDate default -Infinity
18364  * @cfg {Number} endDate default Infinity
18365  * @cfg {Boolean} todayHighlight default false
18366  * @cfg {Boolean} todayBtn default false
18367  * @cfg {Boolean} calendarWeeks default false
18368  * @cfg {Object} daysOfWeekDisabled default empty
18369  * @cfg {Boolean} singleMode default false (true | false)
18370  * 
18371  * @cfg {Boolean} keyboardNavigation default true
18372  * @cfg {String} language default en
18373  * 
18374  * @constructor
18375  * Create a new DateField
18376  * @param {Object} config The config object
18377  */
18378
18379 Roo.bootstrap.DateField = function(config){
18380     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18381      this.addEvents({
18382             /**
18383              * @event show
18384              * Fires when this field show.
18385              * @param {Roo.bootstrap.DateField} this
18386              * @param {Mixed} date The date value
18387              */
18388             show : true,
18389             /**
18390              * @event show
18391              * Fires when this field hide.
18392              * @param {Roo.bootstrap.DateField} this
18393              * @param {Mixed} date The date value
18394              */
18395             hide : true,
18396             /**
18397              * @event select
18398              * Fires when select a date.
18399              * @param {Roo.bootstrap.DateField} this
18400              * @param {Mixed} date The date value
18401              */
18402             select : true,
18403             /**
18404              * @event beforeselect
18405              * Fires when before select a date.
18406              * @param {Roo.bootstrap.DateField} this
18407              * @param {Mixed} date The date value
18408              */
18409             beforeselect : true
18410         });
18411 };
18412
18413 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18414     
18415     /**
18416      * @cfg {String} format
18417      * The default date format string which can be overriden for localization support.  The format must be
18418      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18419      */
18420     format : "m/d/y",
18421     /**
18422      * @cfg {String} altFormats
18423      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18424      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18425      */
18426     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18427     
18428     weekStart : 0,
18429     
18430     viewMode : '',
18431     
18432     minViewMode : '',
18433     
18434     todayHighlight : false,
18435     
18436     todayBtn: false,
18437     
18438     language: 'en',
18439     
18440     keyboardNavigation: true,
18441     
18442     calendarWeeks: false,
18443     
18444     startDate: -Infinity,
18445     
18446     endDate: Infinity,
18447     
18448     daysOfWeekDisabled: [],
18449     
18450     _events: [],
18451     
18452     singleMode : false,
18453     
18454     UTCDate: function()
18455     {
18456         return new Date(Date.UTC.apply(Date, arguments));
18457     },
18458     
18459     UTCToday: function()
18460     {
18461         var today = new Date();
18462         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18463     },
18464     
18465     getDate: function() {
18466             var d = this.getUTCDate();
18467             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18468     },
18469     
18470     getUTCDate: function() {
18471             return this.date;
18472     },
18473     
18474     setDate: function(d) {
18475             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18476     },
18477     
18478     setUTCDate: function(d) {
18479             this.date = d;
18480             this.setValue(this.formatDate(this.date));
18481     },
18482         
18483     onRender: function(ct, position)
18484     {
18485         
18486         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18487         
18488         this.language = this.language || 'en';
18489         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18490         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18491         
18492         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18493         this.format = this.format || 'm/d/y';
18494         this.isInline = false;
18495         this.isInput = true;
18496         this.component = this.el.select('.add-on', true).first() || false;
18497         this.component = (this.component && this.component.length === 0) ? false : this.component;
18498         this.hasInput = this.component && this.inputEl().length;
18499         
18500         if (typeof(this.minViewMode === 'string')) {
18501             switch (this.minViewMode) {
18502                 case 'months':
18503                     this.minViewMode = 1;
18504                     break;
18505                 case 'years':
18506                     this.minViewMode = 2;
18507                     break;
18508                 default:
18509                     this.minViewMode = 0;
18510                     break;
18511             }
18512         }
18513         
18514         if (typeof(this.viewMode === 'string')) {
18515             switch (this.viewMode) {
18516                 case 'months':
18517                     this.viewMode = 1;
18518                     break;
18519                 case 'years':
18520                     this.viewMode = 2;
18521                     break;
18522                 default:
18523                     this.viewMode = 0;
18524                     break;
18525             }
18526         }
18527                 
18528         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18529         
18530 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18531         
18532         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18533         
18534         this.picker().on('mousedown', this.onMousedown, this);
18535         this.picker().on('click', this.onClick, this);
18536         
18537         this.picker().addClass('datepicker-dropdown');
18538         
18539         this.startViewMode = this.viewMode;
18540         
18541         if(this.singleMode){
18542             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18543                 v.setVisibilityMode(Roo.Element.DISPLAY);
18544                 v.hide();
18545             });
18546             
18547             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18548                 v.setStyle('width', '189px');
18549             });
18550         }
18551         
18552         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18553             if(!this.calendarWeeks){
18554                 v.remove();
18555                 return;
18556             }
18557             
18558             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18559             v.attr('colspan', function(i, val){
18560                 return parseInt(val) + 1;
18561             });
18562         });
18563                         
18564         
18565         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18566         
18567         this.setStartDate(this.startDate);
18568         this.setEndDate(this.endDate);
18569         
18570         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18571         
18572         this.fillDow();
18573         this.fillMonths();
18574         this.update();
18575         this.showMode();
18576         
18577         if(this.isInline) {
18578             this.showPopup();
18579         }
18580     },
18581     
18582     picker : function()
18583     {
18584         return this.pickerEl;
18585 //        return this.el.select('.datepicker', true).first();
18586     },
18587     
18588     fillDow: function()
18589     {
18590         var dowCnt = this.weekStart;
18591         
18592         var dow = {
18593             tag: 'tr',
18594             cn: [
18595                 
18596             ]
18597         };
18598         
18599         if(this.calendarWeeks){
18600             dow.cn.push({
18601                 tag: 'th',
18602                 cls: 'cw',
18603                 html: '&nbsp;'
18604             })
18605         }
18606         
18607         while (dowCnt < this.weekStart + 7) {
18608             dow.cn.push({
18609                 tag: 'th',
18610                 cls: 'dow',
18611                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18612             });
18613         }
18614         
18615         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18616     },
18617     
18618     fillMonths: function()
18619     {    
18620         var i = 0;
18621         var months = this.picker().select('>.datepicker-months td', true).first();
18622         
18623         months.dom.innerHTML = '';
18624         
18625         while (i < 12) {
18626             var month = {
18627                 tag: 'span',
18628                 cls: 'month',
18629                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18630             };
18631             
18632             months.createChild(month);
18633         }
18634         
18635     },
18636     
18637     update: function()
18638     {
18639         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;
18640         
18641         if (this.date < this.startDate) {
18642             this.viewDate = new Date(this.startDate);
18643         } else if (this.date > this.endDate) {
18644             this.viewDate = new Date(this.endDate);
18645         } else {
18646             this.viewDate = new Date(this.date);
18647         }
18648         
18649         this.fill();
18650     },
18651     
18652     fill: function() 
18653     {
18654         var d = new Date(this.viewDate),
18655                 year = d.getUTCFullYear(),
18656                 month = d.getUTCMonth(),
18657                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18658                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18659                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18660                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18661                 currentDate = this.date && this.date.valueOf(),
18662                 today = this.UTCToday();
18663         
18664         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18665         
18666 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18667         
18668 //        this.picker.select('>tfoot th.today').
18669 //                                              .text(dates[this.language].today)
18670 //                                              .toggle(this.todayBtn !== false);
18671     
18672         this.updateNavArrows();
18673         this.fillMonths();
18674                                                 
18675         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18676         
18677         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18678          
18679         prevMonth.setUTCDate(day);
18680         
18681         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18682         
18683         var nextMonth = new Date(prevMonth);
18684         
18685         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18686         
18687         nextMonth = nextMonth.valueOf();
18688         
18689         var fillMonths = false;
18690         
18691         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18692         
18693         while(prevMonth.valueOf() <= nextMonth) {
18694             var clsName = '';
18695             
18696             if (prevMonth.getUTCDay() === this.weekStart) {
18697                 if(fillMonths){
18698                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18699                 }
18700                     
18701                 fillMonths = {
18702                     tag: 'tr',
18703                     cn: []
18704                 };
18705                 
18706                 if(this.calendarWeeks){
18707                     // ISO 8601: First week contains first thursday.
18708                     // ISO also states week starts on Monday, but we can be more abstract here.
18709                     var
18710                     // Start of current week: based on weekstart/current date
18711                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18712                     // Thursday of this week
18713                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18714                     // First Thursday of year, year from thursday
18715                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18716                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18717                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18718                     
18719                     fillMonths.cn.push({
18720                         tag: 'td',
18721                         cls: 'cw',
18722                         html: calWeek
18723                     });
18724                 }
18725             }
18726             
18727             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18728                 clsName += ' old';
18729             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18730                 clsName += ' new';
18731             }
18732             if (this.todayHighlight &&
18733                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18734                 prevMonth.getUTCMonth() == today.getMonth() &&
18735                 prevMonth.getUTCDate() == today.getDate()) {
18736                 clsName += ' today';
18737             }
18738             
18739             if (currentDate && prevMonth.valueOf() === currentDate) {
18740                 clsName += ' active';
18741             }
18742             
18743             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18744                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18745                     clsName += ' disabled';
18746             }
18747             
18748             fillMonths.cn.push({
18749                 tag: 'td',
18750                 cls: 'day ' + clsName,
18751                 html: prevMonth.getDate()
18752             });
18753             
18754             prevMonth.setDate(prevMonth.getDate()+1);
18755         }
18756           
18757         var currentYear = this.date && this.date.getUTCFullYear();
18758         var currentMonth = this.date && this.date.getUTCMonth();
18759         
18760         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18761         
18762         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18763             v.removeClass('active');
18764             
18765             if(currentYear === year && k === currentMonth){
18766                 v.addClass('active');
18767             }
18768             
18769             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18770                 v.addClass('disabled');
18771             }
18772             
18773         });
18774         
18775         
18776         year = parseInt(year/10, 10) * 10;
18777         
18778         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18779         
18780         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18781         
18782         year -= 1;
18783         for (var i = -1; i < 11; i++) {
18784             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18785                 tag: 'span',
18786                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18787                 html: year
18788             });
18789             
18790             year += 1;
18791         }
18792     },
18793     
18794     showMode: function(dir) 
18795     {
18796         if (dir) {
18797             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18798         }
18799         
18800         Roo.each(this.picker().select('>div',true).elements, function(v){
18801             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18802             v.hide();
18803         });
18804         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18805     },
18806     
18807     place: function()
18808     {
18809         if(this.isInline) {
18810             return;
18811         }
18812         
18813         this.picker().removeClass(['bottom', 'top']);
18814         
18815         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18816             /*
18817              * place to the top of element!
18818              *
18819              */
18820             
18821             this.picker().addClass('top');
18822             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18823             
18824             return;
18825         }
18826         
18827         this.picker().addClass('bottom');
18828         
18829         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18830     },
18831     
18832     parseDate : function(value)
18833     {
18834         if(!value || value instanceof Date){
18835             return value;
18836         }
18837         var v = Date.parseDate(value, this.format);
18838         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18839             v = Date.parseDate(value, 'Y-m-d');
18840         }
18841         if(!v && this.altFormats){
18842             if(!this.altFormatsArray){
18843                 this.altFormatsArray = this.altFormats.split("|");
18844             }
18845             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18846                 v = Date.parseDate(value, this.altFormatsArray[i]);
18847             }
18848         }
18849         return v;
18850     },
18851     
18852     formatDate : function(date, fmt)
18853     {   
18854         return (!date || !(date instanceof Date)) ?
18855         date : date.dateFormat(fmt || this.format);
18856     },
18857     
18858     onFocus : function()
18859     {
18860         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18861         this.showPopup();
18862     },
18863     
18864     onBlur : function()
18865     {
18866         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18867         
18868         var d = this.inputEl().getValue();
18869         
18870         this.setValue(d);
18871                 
18872         this.hidePopup();
18873     },
18874     
18875     showPopup : function()
18876     {
18877         this.picker().show();
18878         this.update();
18879         this.place();
18880         
18881         this.fireEvent('showpopup', this, this.date);
18882     },
18883     
18884     hidePopup : function()
18885     {
18886         if(this.isInline) {
18887             return;
18888         }
18889         this.picker().hide();
18890         this.viewMode = this.startViewMode;
18891         this.showMode();
18892         
18893         this.fireEvent('hidepopup', this, this.date);
18894         
18895     },
18896     
18897     onMousedown: function(e)
18898     {
18899         e.stopPropagation();
18900         e.preventDefault();
18901     },
18902     
18903     keyup: function(e)
18904     {
18905         Roo.bootstrap.DateField.superclass.keyup.call(this);
18906         this.update();
18907     },
18908
18909     setValue: function(v)
18910     {
18911         if(this.fireEvent('beforeselect', this, v) !== false){
18912             var d = new Date(this.parseDate(v) ).clearTime();
18913         
18914             if(isNaN(d.getTime())){
18915                 this.date = this.viewDate = '';
18916                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18917                 return;
18918             }
18919
18920             v = this.formatDate(d);
18921
18922             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18923
18924             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18925
18926             this.update();
18927
18928             this.fireEvent('select', this, this.date);
18929         }
18930     },
18931     
18932     getValue: function()
18933     {
18934         return this.formatDate(this.date);
18935     },
18936     
18937     fireKey: function(e)
18938     {
18939         if (!this.picker().isVisible()){
18940             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18941                 this.showPopup();
18942             }
18943             return;
18944         }
18945         
18946         var dateChanged = false,
18947         dir, day, month,
18948         newDate, newViewDate;
18949         
18950         switch(e.keyCode){
18951             case 27: // escape
18952                 this.hidePopup();
18953                 e.preventDefault();
18954                 break;
18955             case 37: // left
18956             case 39: // right
18957                 if (!this.keyboardNavigation) {
18958                     break;
18959                 }
18960                 dir = e.keyCode == 37 ? -1 : 1;
18961                 
18962                 if (e.ctrlKey){
18963                     newDate = this.moveYear(this.date, dir);
18964                     newViewDate = this.moveYear(this.viewDate, dir);
18965                 } else if (e.shiftKey){
18966                     newDate = this.moveMonth(this.date, dir);
18967                     newViewDate = this.moveMonth(this.viewDate, dir);
18968                 } else {
18969                     newDate = new Date(this.date);
18970                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18971                     newViewDate = new Date(this.viewDate);
18972                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18973                 }
18974                 if (this.dateWithinRange(newDate)){
18975                     this.date = newDate;
18976                     this.viewDate = newViewDate;
18977                     this.setValue(this.formatDate(this.date));
18978 //                    this.update();
18979                     e.preventDefault();
18980                     dateChanged = true;
18981                 }
18982                 break;
18983             case 38: // up
18984             case 40: // down
18985                 if (!this.keyboardNavigation) {
18986                     break;
18987                 }
18988                 dir = e.keyCode == 38 ? -1 : 1;
18989                 if (e.ctrlKey){
18990                     newDate = this.moveYear(this.date, dir);
18991                     newViewDate = this.moveYear(this.viewDate, dir);
18992                 } else if (e.shiftKey){
18993                     newDate = this.moveMonth(this.date, dir);
18994                     newViewDate = this.moveMonth(this.viewDate, dir);
18995                 } else {
18996                     newDate = new Date(this.date);
18997                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18998                     newViewDate = new Date(this.viewDate);
18999                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19000                 }
19001                 if (this.dateWithinRange(newDate)){
19002                     this.date = newDate;
19003                     this.viewDate = newViewDate;
19004                     this.setValue(this.formatDate(this.date));
19005 //                    this.update();
19006                     e.preventDefault();
19007                     dateChanged = true;
19008                 }
19009                 break;
19010             case 13: // enter
19011                 this.setValue(this.formatDate(this.date));
19012                 this.hidePopup();
19013                 e.preventDefault();
19014                 break;
19015             case 9: // tab
19016                 this.setValue(this.formatDate(this.date));
19017                 this.hidePopup();
19018                 break;
19019             case 16: // shift
19020             case 17: // ctrl
19021             case 18: // alt
19022                 break;
19023             default :
19024                 this.hide();
19025                 
19026         }
19027     },
19028     
19029     
19030     onClick: function(e) 
19031     {
19032         e.stopPropagation();
19033         e.preventDefault();
19034         
19035         var target = e.getTarget();
19036         
19037         if(target.nodeName.toLowerCase() === 'i'){
19038             target = Roo.get(target).dom.parentNode;
19039         }
19040         
19041         var nodeName = target.nodeName;
19042         var className = target.className;
19043         var html = target.innerHTML;
19044         //Roo.log(nodeName);
19045         
19046         switch(nodeName.toLowerCase()) {
19047             case 'th':
19048                 switch(className) {
19049                     case 'switch':
19050                         this.showMode(1);
19051                         break;
19052                     case 'prev':
19053                     case 'next':
19054                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19055                         switch(this.viewMode){
19056                                 case 0:
19057                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19058                                         break;
19059                                 case 1:
19060                                 case 2:
19061                                         this.viewDate = this.moveYear(this.viewDate, dir);
19062                                         break;
19063                         }
19064                         this.fill();
19065                         break;
19066                     case 'today':
19067                         var date = new Date();
19068                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19069 //                        this.fill()
19070                         this.setValue(this.formatDate(this.date));
19071                         
19072                         this.hidePopup();
19073                         break;
19074                 }
19075                 break;
19076             case 'span':
19077                 if (className.indexOf('disabled') < 0) {
19078                     this.viewDate.setUTCDate(1);
19079                     if (className.indexOf('month') > -1) {
19080                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19081                     } else {
19082                         var year = parseInt(html, 10) || 0;
19083                         this.viewDate.setUTCFullYear(year);
19084                         
19085                     }
19086                     
19087                     if(this.singleMode){
19088                         this.setValue(this.formatDate(this.viewDate));
19089                         this.hidePopup();
19090                         return;
19091                     }
19092                     
19093                     this.showMode(-1);
19094                     this.fill();
19095                 }
19096                 break;
19097                 
19098             case 'td':
19099                 //Roo.log(className);
19100                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19101                     var day = parseInt(html, 10) || 1;
19102                     var year = this.viewDate.getUTCFullYear(),
19103                         month = this.viewDate.getUTCMonth();
19104
19105                     if (className.indexOf('old') > -1) {
19106                         if(month === 0 ){
19107                             month = 11;
19108                             year -= 1;
19109                         }else{
19110                             month -= 1;
19111                         }
19112                     } else if (className.indexOf('new') > -1) {
19113                         if (month == 11) {
19114                             month = 0;
19115                             year += 1;
19116                         } else {
19117                             month += 1;
19118                         }
19119                     }
19120                     //Roo.log([year,month,day]);
19121                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19122                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19123 //                    this.fill();
19124                     //Roo.log(this.formatDate(this.date));
19125                     this.setValue(this.formatDate(this.date));
19126                     this.hidePopup();
19127                 }
19128                 break;
19129         }
19130     },
19131     
19132     setStartDate: function(startDate)
19133     {
19134         this.startDate = startDate || -Infinity;
19135         if (this.startDate !== -Infinity) {
19136             this.startDate = this.parseDate(this.startDate);
19137         }
19138         this.update();
19139         this.updateNavArrows();
19140     },
19141
19142     setEndDate: function(endDate)
19143     {
19144         this.endDate = endDate || Infinity;
19145         if (this.endDate !== Infinity) {
19146             this.endDate = this.parseDate(this.endDate);
19147         }
19148         this.update();
19149         this.updateNavArrows();
19150     },
19151     
19152     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19153     {
19154         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19155         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19156             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19157         }
19158         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19159             return parseInt(d, 10);
19160         });
19161         this.update();
19162         this.updateNavArrows();
19163     },
19164     
19165     updateNavArrows: function() 
19166     {
19167         if(this.singleMode){
19168             return;
19169         }
19170         
19171         var d = new Date(this.viewDate),
19172         year = d.getUTCFullYear(),
19173         month = d.getUTCMonth();
19174         
19175         Roo.each(this.picker().select('.prev', true).elements, function(v){
19176             v.show();
19177             switch (this.viewMode) {
19178                 case 0:
19179
19180                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19181                         v.hide();
19182                     }
19183                     break;
19184                 case 1:
19185                 case 2:
19186                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19187                         v.hide();
19188                     }
19189                     break;
19190             }
19191         });
19192         
19193         Roo.each(this.picker().select('.next', true).elements, function(v){
19194             v.show();
19195             switch (this.viewMode) {
19196                 case 0:
19197
19198                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19199                         v.hide();
19200                     }
19201                     break;
19202                 case 1:
19203                 case 2:
19204                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19205                         v.hide();
19206                     }
19207                     break;
19208             }
19209         })
19210     },
19211     
19212     moveMonth: function(date, dir)
19213     {
19214         if (!dir) {
19215             return date;
19216         }
19217         var new_date = new Date(date.valueOf()),
19218         day = new_date.getUTCDate(),
19219         month = new_date.getUTCMonth(),
19220         mag = Math.abs(dir),
19221         new_month, test;
19222         dir = dir > 0 ? 1 : -1;
19223         if (mag == 1){
19224             test = dir == -1
19225             // If going back one month, make sure month is not current month
19226             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19227             ? function(){
19228                 return new_date.getUTCMonth() == month;
19229             }
19230             // If going forward one month, make sure month is as expected
19231             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19232             : function(){
19233                 return new_date.getUTCMonth() != new_month;
19234             };
19235             new_month = month + dir;
19236             new_date.setUTCMonth(new_month);
19237             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19238             if (new_month < 0 || new_month > 11) {
19239                 new_month = (new_month + 12) % 12;
19240             }
19241         } else {
19242             // For magnitudes >1, move one month at a time...
19243             for (var i=0; i<mag; i++) {
19244                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19245                 new_date = this.moveMonth(new_date, dir);
19246             }
19247             // ...then reset the day, keeping it in the new month
19248             new_month = new_date.getUTCMonth();
19249             new_date.setUTCDate(day);
19250             test = function(){
19251                 return new_month != new_date.getUTCMonth();
19252             };
19253         }
19254         // Common date-resetting loop -- if date is beyond end of month, make it
19255         // end of month
19256         while (test()){
19257             new_date.setUTCDate(--day);
19258             new_date.setUTCMonth(new_month);
19259         }
19260         return new_date;
19261     },
19262
19263     moveYear: function(date, dir)
19264     {
19265         return this.moveMonth(date, dir*12);
19266     },
19267
19268     dateWithinRange: function(date)
19269     {
19270         return date >= this.startDate && date <= this.endDate;
19271     },
19272
19273     
19274     remove: function() 
19275     {
19276         this.picker().remove();
19277     },
19278     
19279     validateValue : function(value)
19280     {
19281         if(this.getVisibilityEl().hasClass('hidden')){
19282             return true;
19283         }
19284         
19285         if(value.length < 1)  {
19286             if(this.allowBlank){
19287                 return true;
19288             }
19289             return false;
19290         }
19291         
19292         if(value.length < this.minLength){
19293             return false;
19294         }
19295         if(value.length > this.maxLength){
19296             return false;
19297         }
19298         if(this.vtype){
19299             var vt = Roo.form.VTypes;
19300             if(!vt[this.vtype](value, this)){
19301                 return false;
19302             }
19303         }
19304         if(typeof this.validator == "function"){
19305             var msg = this.validator(value);
19306             if(msg !== true){
19307                 return false;
19308             }
19309         }
19310         
19311         if(this.regex && !this.regex.test(value)){
19312             return false;
19313         }
19314         
19315         if(typeof(this.parseDate(value)) == 'undefined'){
19316             return false;
19317         }
19318         
19319         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19320             return false;
19321         }      
19322         
19323         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19324             return false;
19325         } 
19326         
19327         
19328         return true;
19329     },
19330     
19331     reset : function()
19332     {
19333         this.date = this.viewDate = '';
19334         
19335         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19336     }
19337    
19338 });
19339
19340 Roo.apply(Roo.bootstrap.DateField,  {
19341     
19342     head : {
19343         tag: 'thead',
19344         cn: [
19345         {
19346             tag: 'tr',
19347             cn: [
19348             {
19349                 tag: 'th',
19350                 cls: 'prev',
19351                 html: '<i class="fa fa-arrow-left"/>'
19352             },
19353             {
19354                 tag: 'th',
19355                 cls: 'switch',
19356                 colspan: '5'
19357             },
19358             {
19359                 tag: 'th',
19360                 cls: 'next',
19361                 html: '<i class="fa fa-arrow-right"/>'
19362             }
19363
19364             ]
19365         }
19366         ]
19367     },
19368     
19369     content : {
19370         tag: 'tbody',
19371         cn: [
19372         {
19373             tag: 'tr',
19374             cn: [
19375             {
19376                 tag: 'td',
19377                 colspan: '7'
19378             }
19379             ]
19380         }
19381         ]
19382     },
19383     
19384     footer : {
19385         tag: 'tfoot',
19386         cn: [
19387         {
19388             tag: 'tr',
19389             cn: [
19390             {
19391                 tag: 'th',
19392                 colspan: '7',
19393                 cls: 'today'
19394             }
19395                     
19396             ]
19397         }
19398         ]
19399     },
19400     
19401     dates:{
19402         en: {
19403             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19404             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19405             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19406             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19407             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19408             today: "Today"
19409         }
19410     },
19411     
19412     modes: [
19413     {
19414         clsName: 'days',
19415         navFnc: 'Month',
19416         navStep: 1
19417     },
19418     {
19419         clsName: 'months',
19420         navFnc: 'FullYear',
19421         navStep: 1
19422     },
19423     {
19424         clsName: 'years',
19425         navFnc: 'FullYear',
19426         navStep: 10
19427     }]
19428 });
19429
19430 Roo.apply(Roo.bootstrap.DateField,  {
19431   
19432     template : {
19433         tag: 'div',
19434         cls: 'datepicker dropdown-menu roo-dynamic',
19435         cn: [
19436         {
19437             tag: 'div',
19438             cls: 'datepicker-days',
19439             cn: [
19440             {
19441                 tag: 'table',
19442                 cls: 'table-condensed',
19443                 cn:[
19444                 Roo.bootstrap.DateField.head,
19445                 {
19446                     tag: 'tbody'
19447                 },
19448                 Roo.bootstrap.DateField.footer
19449                 ]
19450             }
19451             ]
19452         },
19453         {
19454             tag: 'div',
19455             cls: 'datepicker-months',
19456             cn: [
19457             {
19458                 tag: 'table',
19459                 cls: 'table-condensed',
19460                 cn:[
19461                 Roo.bootstrap.DateField.head,
19462                 Roo.bootstrap.DateField.content,
19463                 Roo.bootstrap.DateField.footer
19464                 ]
19465             }
19466             ]
19467         },
19468         {
19469             tag: 'div',
19470             cls: 'datepicker-years',
19471             cn: [
19472             {
19473                 tag: 'table',
19474                 cls: 'table-condensed',
19475                 cn:[
19476                 Roo.bootstrap.DateField.head,
19477                 Roo.bootstrap.DateField.content,
19478                 Roo.bootstrap.DateField.footer
19479                 ]
19480             }
19481             ]
19482         }
19483         ]
19484     }
19485 });
19486
19487  
19488
19489  /*
19490  * - LGPL
19491  *
19492  * TimeField
19493  * 
19494  */
19495
19496 /**
19497  * @class Roo.bootstrap.TimeField
19498  * @extends Roo.bootstrap.Input
19499  * Bootstrap DateField class
19500  * 
19501  * 
19502  * @constructor
19503  * Create a new TimeField
19504  * @param {Object} config The config object
19505  */
19506
19507 Roo.bootstrap.TimeField = function(config){
19508     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19509     this.addEvents({
19510             /**
19511              * @event show
19512              * Fires when this field show.
19513              * @param {Roo.bootstrap.DateField} thisthis
19514              * @param {Mixed} date The date value
19515              */
19516             show : true,
19517             /**
19518              * @event show
19519              * Fires when this field hide.
19520              * @param {Roo.bootstrap.DateField} this
19521              * @param {Mixed} date The date value
19522              */
19523             hide : true,
19524             /**
19525              * @event select
19526              * Fires when select a date.
19527              * @param {Roo.bootstrap.DateField} this
19528              * @param {Mixed} date The date value
19529              */
19530             select : true
19531         });
19532 };
19533
19534 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19535     
19536     /**
19537      * @cfg {String} format
19538      * The default time format string which can be overriden for localization support.  The format must be
19539      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19540      */
19541     format : "H:i",
19542        
19543     onRender: function(ct, position)
19544     {
19545         
19546         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19547                 
19548         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19549         
19550         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19551         
19552         this.pop = this.picker().select('>.datepicker-time',true).first();
19553         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19554         
19555         this.picker().on('mousedown', this.onMousedown, this);
19556         this.picker().on('click', this.onClick, this);
19557         
19558         this.picker().addClass('datepicker-dropdown');
19559     
19560         this.fillTime();
19561         this.update();
19562             
19563         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19564         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19565         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19566         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19567         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19568         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19569
19570     },
19571     
19572     fireKey: function(e){
19573         if (!this.picker().isVisible()){
19574             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19575                 this.show();
19576             }
19577             return;
19578         }
19579
19580         e.preventDefault();
19581         
19582         switch(e.keyCode){
19583             case 27: // escape
19584                 this.hide();
19585                 break;
19586             case 37: // left
19587             case 39: // right
19588                 this.onTogglePeriod();
19589                 break;
19590             case 38: // up
19591                 this.onIncrementMinutes();
19592                 break;
19593             case 40: // down
19594                 this.onDecrementMinutes();
19595                 break;
19596             case 13: // enter
19597             case 9: // tab
19598                 this.setTime();
19599                 break;
19600         }
19601     },
19602     
19603     onClick: function(e) {
19604         e.stopPropagation();
19605         e.preventDefault();
19606     },
19607     
19608     picker : function()
19609     {
19610         return this.el.select('.datepicker', true).first();
19611     },
19612     
19613     fillTime: function()
19614     {    
19615         var time = this.pop.select('tbody', true).first();
19616         
19617         time.dom.innerHTML = '';
19618         
19619         time.createChild({
19620             tag: 'tr',
19621             cn: [
19622                 {
19623                     tag: 'td',
19624                     cn: [
19625                         {
19626                             tag: 'a',
19627                             href: '#',
19628                             cls: 'btn',
19629                             cn: [
19630                                 {
19631                                     tag: 'span',
19632                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19633                                 }
19634                             ]
19635                         } 
19636                     ]
19637                 },
19638                 {
19639                     tag: 'td',
19640                     cls: 'separator'
19641                 },
19642                 {
19643                     tag: 'td',
19644                     cn: [
19645                         {
19646                             tag: 'a',
19647                             href: '#',
19648                             cls: 'btn',
19649                             cn: [
19650                                 {
19651                                     tag: 'span',
19652                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19653                                 }
19654                             ]
19655                         }
19656                     ]
19657                 },
19658                 {
19659                     tag: 'td',
19660                     cls: 'separator'
19661                 }
19662             ]
19663         });
19664         
19665         time.createChild({
19666             tag: 'tr',
19667             cn: [
19668                 {
19669                     tag: 'td',
19670                     cn: [
19671                         {
19672                             tag: 'span',
19673                             cls: 'timepicker-hour',
19674                             html: '00'
19675                         }  
19676                     ]
19677                 },
19678                 {
19679                     tag: 'td',
19680                     cls: 'separator',
19681                     html: ':'
19682                 },
19683                 {
19684                     tag: 'td',
19685                     cn: [
19686                         {
19687                             tag: 'span',
19688                             cls: 'timepicker-minute',
19689                             html: '00'
19690                         }  
19691                     ]
19692                 },
19693                 {
19694                     tag: 'td',
19695                     cls: 'separator'
19696                 },
19697                 {
19698                     tag: 'td',
19699                     cn: [
19700                         {
19701                             tag: 'button',
19702                             type: 'button',
19703                             cls: 'btn btn-primary period',
19704                             html: 'AM'
19705                             
19706                         }
19707                     ]
19708                 }
19709             ]
19710         });
19711         
19712         time.createChild({
19713             tag: 'tr',
19714             cn: [
19715                 {
19716                     tag: 'td',
19717                     cn: [
19718                         {
19719                             tag: 'a',
19720                             href: '#',
19721                             cls: 'btn',
19722                             cn: [
19723                                 {
19724                                     tag: 'span',
19725                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19726                                 }
19727                             ]
19728                         }
19729                     ]
19730                 },
19731                 {
19732                     tag: 'td',
19733                     cls: 'separator'
19734                 },
19735                 {
19736                     tag: 'td',
19737                     cn: [
19738                         {
19739                             tag: 'a',
19740                             href: '#',
19741                             cls: 'btn',
19742                             cn: [
19743                                 {
19744                                     tag: 'span',
19745                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19746                                 }
19747                             ]
19748                         }
19749                     ]
19750                 },
19751                 {
19752                     tag: 'td',
19753                     cls: 'separator'
19754                 }
19755             ]
19756         });
19757         
19758     },
19759     
19760     update: function()
19761     {
19762         
19763         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19764         
19765         this.fill();
19766     },
19767     
19768     fill: function() 
19769     {
19770         var hours = this.time.getHours();
19771         var minutes = this.time.getMinutes();
19772         var period = 'AM';
19773         
19774         if(hours > 11){
19775             period = 'PM';
19776         }
19777         
19778         if(hours == 0){
19779             hours = 12;
19780         }
19781         
19782         
19783         if(hours > 12){
19784             hours = hours - 12;
19785         }
19786         
19787         if(hours < 10){
19788             hours = '0' + hours;
19789         }
19790         
19791         if(minutes < 10){
19792             minutes = '0' + minutes;
19793         }
19794         
19795         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19796         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19797         this.pop.select('button', true).first().dom.innerHTML = period;
19798         
19799     },
19800     
19801     place: function()
19802     {   
19803         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19804         
19805         var cls = ['bottom'];
19806         
19807         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19808             cls.pop();
19809             cls.push('top');
19810         }
19811         
19812         cls.push('right');
19813         
19814         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19815             cls.pop();
19816             cls.push('left');
19817         }
19818         
19819         this.picker().addClass(cls.join('-'));
19820         
19821         var _this = this;
19822         
19823         Roo.each(cls, function(c){
19824             if(c == 'bottom'){
19825                 _this.picker().setTop(_this.inputEl().getHeight());
19826                 return;
19827             }
19828             if(c == 'top'){
19829                 _this.picker().setTop(0 - _this.picker().getHeight());
19830                 return;
19831             }
19832             
19833             if(c == 'left'){
19834                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19835                 return;
19836             }
19837             if(c == 'right'){
19838                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19839                 return;
19840             }
19841         });
19842         
19843     },
19844   
19845     onFocus : function()
19846     {
19847         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19848         this.show();
19849     },
19850     
19851     onBlur : function()
19852     {
19853         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19854         this.hide();
19855     },
19856     
19857     show : function()
19858     {
19859         this.picker().show();
19860         this.pop.show();
19861         this.update();
19862         this.place();
19863         
19864         this.fireEvent('show', this, this.date);
19865     },
19866     
19867     hide : function()
19868     {
19869         this.picker().hide();
19870         this.pop.hide();
19871         
19872         this.fireEvent('hide', this, this.date);
19873     },
19874     
19875     setTime : function()
19876     {
19877         this.hide();
19878         this.setValue(this.time.format(this.format));
19879         
19880         this.fireEvent('select', this, this.date);
19881         
19882         
19883     },
19884     
19885     onMousedown: function(e){
19886         e.stopPropagation();
19887         e.preventDefault();
19888     },
19889     
19890     onIncrementHours: function()
19891     {
19892         Roo.log('onIncrementHours');
19893         this.time = this.time.add(Date.HOUR, 1);
19894         this.update();
19895         
19896     },
19897     
19898     onDecrementHours: function()
19899     {
19900         Roo.log('onDecrementHours');
19901         this.time = this.time.add(Date.HOUR, -1);
19902         this.update();
19903     },
19904     
19905     onIncrementMinutes: function()
19906     {
19907         Roo.log('onIncrementMinutes');
19908         this.time = this.time.add(Date.MINUTE, 1);
19909         this.update();
19910     },
19911     
19912     onDecrementMinutes: function()
19913     {
19914         Roo.log('onDecrementMinutes');
19915         this.time = this.time.add(Date.MINUTE, -1);
19916         this.update();
19917     },
19918     
19919     onTogglePeriod: function()
19920     {
19921         Roo.log('onTogglePeriod');
19922         this.time = this.time.add(Date.HOUR, 12);
19923         this.update();
19924     }
19925     
19926    
19927 });
19928
19929 Roo.apply(Roo.bootstrap.TimeField,  {
19930     
19931     content : {
19932         tag: 'tbody',
19933         cn: [
19934             {
19935                 tag: 'tr',
19936                 cn: [
19937                 {
19938                     tag: 'td',
19939                     colspan: '7'
19940                 }
19941                 ]
19942             }
19943         ]
19944     },
19945     
19946     footer : {
19947         tag: 'tfoot',
19948         cn: [
19949             {
19950                 tag: 'tr',
19951                 cn: [
19952                 {
19953                     tag: 'th',
19954                     colspan: '7',
19955                     cls: '',
19956                     cn: [
19957                         {
19958                             tag: 'button',
19959                             cls: 'btn btn-info ok',
19960                             html: 'OK'
19961                         }
19962                     ]
19963                 }
19964
19965                 ]
19966             }
19967         ]
19968     }
19969 });
19970
19971 Roo.apply(Roo.bootstrap.TimeField,  {
19972   
19973     template : {
19974         tag: 'div',
19975         cls: 'datepicker dropdown-menu',
19976         cn: [
19977             {
19978                 tag: 'div',
19979                 cls: 'datepicker-time',
19980                 cn: [
19981                 {
19982                     tag: 'table',
19983                     cls: 'table-condensed',
19984                     cn:[
19985                     Roo.bootstrap.TimeField.content,
19986                     Roo.bootstrap.TimeField.footer
19987                     ]
19988                 }
19989                 ]
19990             }
19991         ]
19992     }
19993 });
19994
19995  
19996
19997  /*
19998  * - LGPL
19999  *
20000  * MonthField
20001  * 
20002  */
20003
20004 /**
20005  * @class Roo.bootstrap.MonthField
20006  * @extends Roo.bootstrap.Input
20007  * Bootstrap MonthField class
20008  * 
20009  * @cfg {String} language default en
20010  * 
20011  * @constructor
20012  * Create a new MonthField
20013  * @param {Object} config The config object
20014  */
20015
20016 Roo.bootstrap.MonthField = function(config){
20017     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20018     
20019     this.addEvents({
20020         /**
20021          * @event show
20022          * Fires when this field show.
20023          * @param {Roo.bootstrap.MonthField} this
20024          * @param {Mixed} date The date value
20025          */
20026         show : true,
20027         /**
20028          * @event show
20029          * Fires when this field hide.
20030          * @param {Roo.bootstrap.MonthField} this
20031          * @param {Mixed} date The date value
20032          */
20033         hide : true,
20034         /**
20035          * @event select
20036          * Fires when select a date.
20037          * @param {Roo.bootstrap.MonthField} this
20038          * @param {String} oldvalue The old value
20039          * @param {String} newvalue The new value
20040          */
20041         select : true
20042     });
20043 };
20044
20045 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20046     
20047     onRender: function(ct, position)
20048     {
20049         
20050         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20051         
20052         this.language = this.language || 'en';
20053         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20054         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20055         
20056         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20057         this.isInline = false;
20058         this.isInput = true;
20059         this.component = this.el.select('.add-on', true).first() || false;
20060         this.component = (this.component && this.component.length === 0) ? false : this.component;
20061         this.hasInput = this.component && this.inputEL().length;
20062         
20063         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20064         
20065         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20066         
20067         this.picker().on('mousedown', this.onMousedown, this);
20068         this.picker().on('click', this.onClick, this);
20069         
20070         this.picker().addClass('datepicker-dropdown');
20071         
20072         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20073             v.setStyle('width', '189px');
20074         });
20075         
20076         this.fillMonths();
20077         
20078         this.update();
20079         
20080         if(this.isInline) {
20081             this.show();
20082         }
20083         
20084     },
20085     
20086     setValue: function(v, suppressEvent)
20087     {   
20088         var o = this.getValue();
20089         
20090         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20091         
20092         this.update();
20093
20094         if(suppressEvent !== true){
20095             this.fireEvent('select', this, o, v);
20096         }
20097         
20098     },
20099     
20100     getValue: function()
20101     {
20102         return this.value;
20103     },
20104     
20105     onClick: function(e) 
20106     {
20107         e.stopPropagation();
20108         e.preventDefault();
20109         
20110         var target = e.getTarget();
20111         
20112         if(target.nodeName.toLowerCase() === 'i'){
20113             target = Roo.get(target).dom.parentNode;
20114         }
20115         
20116         var nodeName = target.nodeName;
20117         var className = target.className;
20118         var html = target.innerHTML;
20119         
20120         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20121             return;
20122         }
20123         
20124         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20125         
20126         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20127         
20128         this.hide();
20129                         
20130     },
20131     
20132     picker : function()
20133     {
20134         return this.pickerEl;
20135     },
20136     
20137     fillMonths: function()
20138     {    
20139         var i = 0;
20140         var months = this.picker().select('>.datepicker-months td', true).first();
20141         
20142         months.dom.innerHTML = '';
20143         
20144         while (i < 12) {
20145             var month = {
20146                 tag: 'span',
20147                 cls: 'month',
20148                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20149             };
20150             
20151             months.createChild(month);
20152         }
20153         
20154     },
20155     
20156     update: function()
20157     {
20158         var _this = this;
20159         
20160         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20161             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20162         }
20163         
20164         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20165             e.removeClass('active');
20166             
20167             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20168                 e.addClass('active');
20169             }
20170         })
20171     },
20172     
20173     place: function()
20174     {
20175         if(this.isInline) {
20176             return;
20177         }
20178         
20179         this.picker().removeClass(['bottom', 'top']);
20180         
20181         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20182             /*
20183              * place to the top of element!
20184              *
20185              */
20186             
20187             this.picker().addClass('top');
20188             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20189             
20190             return;
20191         }
20192         
20193         this.picker().addClass('bottom');
20194         
20195         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20196     },
20197     
20198     onFocus : function()
20199     {
20200         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20201         this.show();
20202     },
20203     
20204     onBlur : function()
20205     {
20206         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20207         
20208         var d = this.inputEl().getValue();
20209         
20210         this.setValue(d);
20211                 
20212         this.hide();
20213     },
20214     
20215     show : function()
20216     {
20217         this.picker().show();
20218         this.picker().select('>.datepicker-months', true).first().show();
20219         this.update();
20220         this.place();
20221         
20222         this.fireEvent('show', this, this.date);
20223     },
20224     
20225     hide : function()
20226     {
20227         if(this.isInline) {
20228             return;
20229         }
20230         this.picker().hide();
20231         this.fireEvent('hide', this, this.date);
20232         
20233     },
20234     
20235     onMousedown: function(e)
20236     {
20237         e.stopPropagation();
20238         e.preventDefault();
20239     },
20240     
20241     keyup: function(e)
20242     {
20243         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20244         this.update();
20245     },
20246
20247     fireKey: function(e)
20248     {
20249         if (!this.picker().isVisible()){
20250             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20251                 this.show();
20252             }
20253             return;
20254         }
20255         
20256         var dir;
20257         
20258         switch(e.keyCode){
20259             case 27: // escape
20260                 this.hide();
20261                 e.preventDefault();
20262                 break;
20263             case 37: // left
20264             case 39: // right
20265                 dir = e.keyCode == 37 ? -1 : 1;
20266                 
20267                 this.vIndex = this.vIndex + dir;
20268                 
20269                 if(this.vIndex < 0){
20270                     this.vIndex = 0;
20271                 }
20272                 
20273                 if(this.vIndex > 11){
20274                     this.vIndex = 11;
20275                 }
20276                 
20277                 if(isNaN(this.vIndex)){
20278                     this.vIndex = 0;
20279                 }
20280                 
20281                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20282                 
20283                 break;
20284             case 38: // up
20285             case 40: // down
20286                 
20287                 dir = e.keyCode == 38 ? -1 : 1;
20288                 
20289                 this.vIndex = this.vIndex + dir * 4;
20290                 
20291                 if(this.vIndex < 0){
20292                     this.vIndex = 0;
20293                 }
20294                 
20295                 if(this.vIndex > 11){
20296                     this.vIndex = 11;
20297                 }
20298                 
20299                 if(isNaN(this.vIndex)){
20300                     this.vIndex = 0;
20301                 }
20302                 
20303                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20304                 break;
20305                 
20306             case 13: // enter
20307                 
20308                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20309                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20310                 }
20311                 
20312                 this.hide();
20313                 e.preventDefault();
20314                 break;
20315             case 9: // tab
20316                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20317                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20318                 }
20319                 this.hide();
20320                 break;
20321             case 16: // shift
20322             case 17: // ctrl
20323             case 18: // alt
20324                 break;
20325             default :
20326                 this.hide();
20327                 
20328         }
20329     },
20330     
20331     remove: function() 
20332     {
20333         this.picker().remove();
20334     }
20335    
20336 });
20337
20338 Roo.apply(Roo.bootstrap.MonthField,  {
20339     
20340     content : {
20341         tag: 'tbody',
20342         cn: [
20343         {
20344             tag: 'tr',
20345             cn: [
20346             {
20347                 tag: 'td',
20348                 colspan: '7'
20349             }
20350             ]
20351         }
20352         ]
20353     },
20354     
20355     dates:{
20356         en: {
20357             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20358             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20359         }
20360     }
20361 });
20362
20363 Roo.apply(Roo.bootstrap.MonthField,  {
20364   
20365     template : {
20366         tag: 'div',
20367         cls: 'datepicker dropdown-menu roo-dynamic',
20368         cn: [
20369             {
20370                 tag: 'div',
20371                 cls: 'datepicker-months',
20372                 cn: [
20373                 {
20374                     tag: 'table',
20375                     cls: 'table-condensed',
20376                     cn:[
20377                         Roo.bootstrap.DateField.content
20378                     ]
20379                 }
20380                 ]
20381             }
20382         ]
20383     }
20384 });
20385
20386  
20387
20388  
20389  /*
20390  * - LGPL
20391  *
20392  * CheckBox
20393  * 
20394  */
20395
20396 /**
20397  * @class Roo.bootstrap.CheckBox
20398  * @extends Roo.bootstrap.Input
20399  * Bootstrap CheckBox class
20400  * 
20401  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20402  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20403  * @cfg {String} boxLabel The text that appears beside the checkbox
20404  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20405  * @cfg {Boolean} checked initnal the element
20406  * @cfg {Boolean} inline inline the element (default false)
20407  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20408  * @cfg {String} tooltip label tooltip
20409  * 
20410  * @constructor
20411  * Create a new CheckBox
20412  * @param {Object} config The config object
20413  */
20414
20415 Roo.bootstrap.CheckBox = function(config){
20416     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20417    
20418     this.addEvents({
20419         /**
20420         * @event check
20421         * Fires when the element is checked or unchecked.
20422         * @param {Roo.bootstrap.CheckBox} this This input
20423         * @param {Boolean} checked The new checked value
20424         */
20425        check : true,
20426        /**
20427         * @event click
20428         * Fires when the element is click.
20429         * @param {Roo.bootstrap.CheckBox} this This input
20430         */
20431        click : true
20432     });
20433     
20434 };
20435
20436 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20437   
20438     inputType: 'checkbox',
20439     inputValue: 1,
20440     valueOff: 0,
20441     boxLabel: false,
20442     checked: false,
20443     weight : false,
20444     inline: false,
20445     tooltip : '',
20446     
20447     getAutoCreate : function()
20448     {
20449         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20450         
20451         var id = Roo.id();
20452         
20453         var cfg = {};
20454         
20455         cfg.cls = 'form-group ' + this.inputType; //input-group
20456         
20457         if(this.inline){
20458             cfg.cls += ' ' + this.inputType + '-inline';
20459         }
20460         
20461         var input =  {
20462             tag: 'input',
20463             id : id,
20464             type : this.inputType,
20465             value : this.inputValue,
20466             cls : 'roo-' + this.inputType, //'form-box',
20467             placeholder : this.placeholder || ''
20468             
20469         };
20470         
20471         if(this.inputType != 'radio'){
20472             var hidden =  {
20473                 tag: 'input',
20474                 type : 'hidden',
20475                 cls : 'roo-hidden-value',
20476                 value : this.checked ? this.inputValue : this.valueOff
20477             };
20478         }
20479         
20480             
20481         if (this.weight) { // Validity check?
20482             cfg.cls += " " + this.inputType + "-" + this.weight;
20483         }
20484         
20485         if (this.disabled) {
20486             input.disabled=true;
20487         }
20488         
20489         if(this.checked){
20490             input.checked = this.checked;
20491         }
20492         
20493         if (this.name) {
20494             
20495             input.name = this.name;
20496             
20497             if(this.inputType != 'radio'){
20498                 hidden.name = this.name;
20499                 input.name = '_hidden_' + this.name;
20500             }
20501         }
20502         
20503         if (this.size) {
20504             input.cls += ' input-' + this.size;
20505         }
20506         
20507         var settings=this;
20508         
20509         ['xs','sm','md','lg'].map(function(size){
20510             if (settings[size]) {
20511                 cfg.cls += ' col-' + size + '-' + settings[size];
20512             }
20513         });
20514         
20515         var inputblock = input;
20516          
20517         if (this.before || this.after) {
20518             
20519             inputblock = {
20520                 cls : 'input-group',
20521                 cn :  [] 
20522             };
20523             
20524             if (this.before) {
20525                 inputblock.cn.push({
20526                     tag :'span',
20527                     cls : 'input-group-addon',
20528                     html : this.before
20529                 });
20530             }
20531             
20532             inputblock.cn.push(input);
20533             
20534             if(this.inputType != 'radio'){
20535                 inputblock.cn.push(hidden);
20536             }
20537             
20538             if (this.after) {
20539                 inputblock.cn.push({
20540                     tag :'span',
20541                     cls : 'input-group-addon',
20542                     html : this.after
20543                 });
20544             }
20545             
20546         }
20547         
20548         if (align ==='left' && this.fieldLabel.length) {
20549 //                Roo.log("left and has label");
20550             cfg.cn = [
20551                 {
20552                     tag: 'label',
20553                     'for' :  id,
20554                     cls : 'control-label',
20555                     html : this.fieldLabel
20556                 },
20557                 {
20558                     cls : "", 
20559                     cn: [
20560                         inputblock
20561                     ]
20562                 }
20563             ];
20564             
20565             if(this.labelWidth > 12){
20566                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20567             }
20568             
20569             if(this.labelWidth < 13 && this.labelmd == 0){
20570                 this.labelmd = this.labelWidth;
20571             }
20572             
20573             if(this.labellg > 0){
20574                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20575                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20576             }
20577             
20578             if(this.labelmd > 0){
20579                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20580                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20581             }
20582             
20583             if(this.labelsm > 0){
20584                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20585                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20586             }
20587             
20588             if(this.labelxs > 0){
20589                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20590                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20591             }
20592             
20593         } else if ( this.fieldLabel.length) {
20594 //                Roo.log(" label");
20595                 cfg.cn = [
20596                    
20597                     {
20598                         tag: this.boxLabel ? 'span' : 'label',
20599                         'for': id,
20600                         cls: 'control-label box-input-label',
20601                         //cls : 'input-group-addon',
20602                         html : this.fieldLabel
20603                     },
20604                     
20605                     inputblock
20606                     
20607                 ];
20608
20609         } else {
20610             
20611 //                Roo.log(" no label && no align");
20612                 cfg.cn = [  inputblock ] ;
20613                 
20614                 
20615         }
20616         
20617         if(this.boxLabel){
20618              var boxLabelCfg = {
20619                 tag: 'label',
20620                 //'for': id, // box label is handled by onclick - so no for...
20621                 cls: 'box-label',
20622                 html: this.boxLabel
20623             };
20624             
20625             if(this.tooltip){
20626                 boxLabelCfg.tooltip = this.tooltip;
20627             }
20628              
20629             cfg.cn.push(boxLabelCfg);
20630         }
20631         
20632         if(this.inputType != 'radio'){
20633             cfg.cn.push(hidden);
20634         }
20635         
20636         return cfg;
20637         
20638     },
20639     
20640     /**
20641      * return the real input element.
20642      */
20643     inputEl: function ()
20644     {
20645         return this.el.select('input.roo-' + this.inputType,true).first();
20646     },
20647     hiddenEl: function ()
20648     {
20649         return this.el.select('input.roo-hidden-value',true).first();
20650     },
20651     
20652     labelEl: function()
20653     {
20654         return this.el.select('label.control-label',true).first();
20655     },
20656     /* depricated... */
20657     
20658     label: function()
20659     {
20660         return this.labelEl();
20661     },
20662     
20663     boxLabelEl: function()
20664     {
20665         return this.el.select('label.box-label',true).first();
20666     },
20667     
20668     initEvents : function()
20669     {
20670 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20671         
20672         this.inputEl().on('click', this.onClick,  this);
20673         
20674         if (this.boxLabel) { 
20675             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20676         }
20677         
20678         this.startValue = this.getValue();
20679         
20680         if(this.groupId){
20681             Roo.bootstrap.CheckBox.register(this);
20682         }
20683     },
20684     
20685     onClick : function(e)
20686     {   
20687         if(this.fireEvent('click', this, e) !== false){
20688             this.setChecked(!this.checked);
20689         }
20690         
20691     },
20692     
20693     setChecked : function(state,suppressEvent)
20694     {
20695         this.startValue = this.getValue();
20696
20697         if(this.inputType == 'radio'){
20698             
20699             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20700                 e.dom.checked = false;
20701             });
20702             
20703             this.inputEl().dom.checked = true;
20704             
20705             this.inputEl().dom.value = this.inputValue;
20706             
20707             if(suppressEvent !== true){
20708                 this.fireEvent('check', this, true);
20709             }
20710             
20711             this.validate();
20712             
20713             return;
20714         }
20715         
20716         this.checked = state;
20717         
20718         this.inputEl().dom.checked = state;
20719         
20720         
20721         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20722         
20723         if(suppressEvent !== true){
20724             this.fireEvent('check', this, state);
20725         }
20726         
20727         this.validate();
20728     },
20729     
20730     getValue : function()
20731     {
20732         if(this.inputType == 'radio'){
20733             return this.getGroupValue();
20734         }
20735         
20736         return this.hiddenEl().dom.value;
20737         
20738     },
20739     
20740     getGroupValue : function()
20741     {
20742         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20743             return '';
20744         }
20745         
20746         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20747     },
20748     
20749     setValue : function(v,suppressEvent)
20750     {
20751         if(this.inputType == 'radio'){
20752             this.setGroupValue(v, suppressEvent);
20753             return;
20754         }
20755         
20756         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20757         
20758         this.validate();
20759     },
20760     
20761     setGroupValue : function(v, suppressEvent)
20762     {
20763         this.startValue = this.getValue();
20764         
20765         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20766             e.dom.checked = false;
20767             
20768             if(e.dom.value == v){
20769                 e.dom.checked = true;
20770             }
20771         });
20772         
20773         if(suppressEvent !== true){
20774             this.fireEvent('check', this, true);
20775         }
20776
20777         this.validate();
20778         
20779         return;
20780     },
20781     
20782     validate : function()
20783     {
20784         if(this.getVisibilityEl().hasClass('hidden')){
20785             return true;
20786         }
20787         
20788         if(
20789                 this.disabled || 
20790                 (this.inputType == 'radio' && this.validateRadio()) ||
20791                 (this.inputType == 'checkbox' && this.validateCheckbox())
20792         ){
20793             this.markValid();
20794             return true;
20795         }
20796         
20797         this.markInvalid();
20798         return false;
20799     },
20800     
20801     validateRadio : function()
20802     {
20803         if(this.getVisibilityEl().hasClass('hidden')){
20804             return true;
20805         }
20806         
20807         if(this.allowBlank){
20808             return true;
20809         }
20810         
20811         var valid = false;
20812         
20813         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20814             if(!e.dom.checked){
20815                 return;
20816             }
20817             
20818             valid = true;
20819             
20820             return false;
20821         });
20822         
20823         return valid;
20824     },
20825     
20826     validateCheckbox : function()
20827     {
20828         if(!this.groupId){
20829             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20830             //return (this.getValue() == this.inputValue) ? true : false;
20831         }
20832         
20833         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20834         
20835         if(!group){
20836             return false;
20837         }
20838         
20839         var r = false;
20840         
20841         for(var i in group){
20842             if(group[i].el.isVisible(true)){
20843                 r = false;
20844                 break;
20845             }
20846             
20847             r = true;
20848         }
20849         
20850         for(var i in group){
20851             if(r){
20852                 break;
20853             }
20854             
20855             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20856         }
20857         
20858         return r;
20859     },
20860     
20861     /**
20862      * Mark this field as valid
20863      */
20864     markValid : function()
20865     {
20866         var _this = this;
20867         
20868         this.fireEvent('valid', this);
20869         
20870         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20871         
20872         if(this.groupId){
20873             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20874         }
20875         
20876         if(label){
20877             label.markValid();
20878         }
20879
20880         if(this.inputType == 'radio'){
20881             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20882                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20883                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20884             });
20885             
20886             return;
20887         }
20888
20889         if(!this.groupId){
20890             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20891             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20892             return;
20893         }
20894         
20895         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20896         
20897         if(!group){
20898             return;
20899         }
20900         
20901         for(var i in group){
20902             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20903             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20904         }
20905     },
20906     
20907      /**
20908      * Mark this field as invalid
20909      * @param {String} msg The validation message
20910      */
20911     markInvalid : function(msg)
20912     {
20913         if(this.allowBlank){
20914             return;
20915         }
20916         
20917         var _this = this;
20918         
20919         this.fireEvent('invalid', this, msg);
20920         
20921         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20922         
20923         if(this.groupId){
20924             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20925         }
20926         
20927         if(label){
20928             label.markInvalid();
20929         }
20930             
20931         if(this.inputType == 'radio'){
20932             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20933                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20934                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20935             });
20936             
20937             return;
20938         }
20939         
20940         if(!this.groupId){
20941             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20942             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20943             return;
20944         }
20945         
20946         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20947         
20948         if(!group){
20949             return;
20950         }
20951         
20952         for(var i in group){
20953             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20954             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20955         }
20956         
20957     },
20958     
20959     clearInvalid : function()
20960     {
20961         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20962         
20963         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20964         
20965         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20966         
20967         if (label && label.iconEl) {
20968             label.iconEl.removeClass(label.validClass);
20969             label.iconEl.removeClass(label.invalidClass);
20970         }
20971     },
20972     
20973     disable : function()
20974     {
20975         if(this.inputType != 'radio'){
20976             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20977             return;
20978         }
20979         
20980         var _this = this;
20981         
20982         if(this.rendered){
20983             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20984                 _this.getActionEl().addClass(this.disabledClass);
20985                 e.dom.disabled = true;
20986             });
20987         }
20988         
20989         this.disabled = true;
20990         this.fireEvent("disable", this);
20991         return this;
20992     },
20993
20994     enable : function()
20995     {
20996         if(this.inputType != 'radio'){
20997             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20998             return;
20999         }
21000         
21001         var _this = this;
21002         
21003         if(this.rendered){
21004             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21005                 _this.getActionEl().removeClass(this.disabledClass);
21006                 e.dom.disabled = false;
21007             });
21008         }
21009         
21010         this.disabled = false;
21011         this.fireEvent("enable", this);
21012         return this;
21013     },
21014     
21015     setBoxLabel : function(v)
21016     {
21017         this.boxLabel = v;
21018         
21019         if(this.rendered){
21020             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21021         }
21022     }
21023
21024 });
21025
21026 Roo.apply(Roo.bootstrap.CheckBox, {
21027     
21028     groups: {},
21029     
21030      /**
21031     * register a CheckBox Group
21032     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21033     */
21034     register : function(checkbox)
21035     {
21036         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21037             this.groups[checkbox.groupId] = {};
21038         }
21039         
21040         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21041             return;
21042         }
21043         
21044         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21045         
21046     },
21047     /**
21048     * fetch a CheckBox Group based on the group ID
21049     * @param {string} the group ID
21050     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21051     */
21052     get: function(groupId) {
21053         if (typeof(this.groups[groupId]) == 'undefined') {
21054             return false;
21055         }
21056         
21057         return this.groups[groupId] ;
21058     }
21059     
21060     
21061 });
21062 /*
21063  * - LGPL
21064  *
21065  * RadioItem
21066  * 
21067  */
21068
21069 /**
21070  * @class Roo.bootstrap.Radio
21071  * @extends Roo.bootstrap.Component
21072  * Bootstrap Radio class
21073  * @cfg {String} boxLabel - the label associated
21074  * @cfg {String} value - the value of radio
21075  * 
21076  * @constructor
21077  * Create a new Radio
21078  * @param {Object} config The config object
21079  */
21080 Roo.bootstrap.Radio = function(config){
21081     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21082     
21083 };
21084
21085 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21086     
21087     boxLabel : '',
21088     
21089     value : '',
21090     
21091     getAutoCreate : function()
21092     {
21093         var cfg = {
21094             tag : 'div',
21095             cls : 'form-group radio',
21096             cn : [
21097                 {
21098                     tag : 'label',
21099                     cls : 'box-label',
21100                     html : this.boxLabel
21101                 }
21102             ]
21103         };
21104         
21105         return cfg;
21106     },
21107     
21108     initEvents : function() 
21109     {
21110         this.parent().register(this);
21111         
21112         this.el.on('click', this.onClick, this);
21113         
21114     },
21115     
21116     onClick : function(e)
21117     {
21118         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21119             this.setChecked(true);
21120         }
21121     },
21122     
21123     setChecked : function(state, suppressEvent)
21124     {
21125         this.parent().setValue(this.value, suppressEvent);
21126         
21127     },
21128     
21129     setBoxLabel : function(v)
21130     {
21131         this.boxLabel = v;
21132         
21133         if(this.rendered){
21134             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21135         }
21136     }
21137     
21138 });
21139  
21140
21141  /*
21142  * - LGPL
21143  *
21144  * Input
21145  * 
21146  */
21147
21148 /**
21149  * @class Roo.bootstrap.SecurePass
21150  * @extends Roo.bootstrap.Input
21151  * Bootstrap SecurePass class
21152  *
21153  * 
21154  * @constructor
21155  * Create a new SecurePass
21156  * @param {Object} config The config object
21157  */
21158  
21159 Roo.bootstrap.SecurePass = function (config) {
21160     // these go here, so the translation tool can replace them..
21161     this.errors = {
21162         PwdEmpty: "Please type a password, and then retype it to confirm.",
21163         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21164         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21165         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21166         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21167         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21168         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21169         TooWeak: "Your password is Too Weak."
21170     },
21171     this.meterLabel = "Password strength:";
21172     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21173     this.meterClass = [
21174         "roo-password-meter-tooweak", 
21175         "roo-password-meter-weak", 
21176         "roo-password-meter-medium", 
21177         "roo-password-meter-strong", 
21178         "roo-password-meter-grey"
21179     ];
21180     
21181     this.errors = {};
21182     
21183     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21184 }
21185
21186 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21187     /**
21188      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21189      * {
21190      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21191      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21192      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21193      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21194      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21195      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21196      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21197      * })
21198      */
21199     // private
21200     
21201     meterWidth: 300,
21202     errorMsg :'',    
21203     errors: false,
21204     imageRoot: '/',
21205     /**
21206      * @cfg {String/Object} Label for the strength meter (defaults to
21207      * 'Password strength:')
21208      */
21209     // private
21210     meterLabel: '',
21211     /**
21212      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21213      * ['Weak', 'Medium', 'Strong'])
21214      */
21215     // private    
21216     pwdStrengths: false,    
21217     // private
21218     strength: 0,
21219     // private
21220     _lastPwd: null,
21221     // private
21222     kCapitalLetter: 0,
21223     kSmallLetter: 1,
21224     kDigit: 2,
21225     kPunctuation: 3,
21226     
21227     insecure: false,
21228     // private
21229     initEvents: function ()
21230     {
21231         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21232
21233         if (this.el.is('input[type=password]') && Roo.isSafari) {
21234             this.el.on('keydown', this.SafariOnKeyDown, this);
21235         }
21236
21237         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21238     },
21239     // private
21240     onRender: function (ct, position)
21241     {
21242         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21243         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21244         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21245
21246         this.trigger.createChild({
21247                    cn: [
21248                     {
21249                     //id: 'PwdMeter',
21250                     tag: 'div',
21251                     cls: 'roo-password-meter-grey col-xs-12',
21252                     style: {
21253                         //width: 0,
21254                         //width: this.meterWidth + 'px'                                                
21255                         }
21256                     },
21257                     {                            
21258                          cls: 'roo-password-meter-text'                          
21259                     }
21260                 ]            
21261         });
21262
21263          
21264         if (this.hideTrigger) {
21265             this.trigger.setDisplayed(false);
21266         }
21267         this.setSize(this.width || '', this.height || '');
21268     },
21269     // private
21270     onDestroy: function ()
21271     {
21272         if (this.trigger) {
21273             this.trigger.removeAllListeners();
21274             this.trigger.remove();
21275         }
21276         if (this.wrap) {
21277             this.wrap.remove();
21278         }
21279         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21280     },
21281     // private
21282     checkStrength: function ()
21283     {
21284         var pwd = this.inputEl().getValue();
21285         if (pwd == this._lastPwd) {
21286             return;
21287         }
21288
21289         var strength;
21290         if (this.ClientSideStrongPassword(pwd)) {
21291             strength = 3;
21292         } else if (this.ClientSideMediumPassword(pwd)) {
21293             strength = 2;
21294         } else if (this.ClientSideWeakPassword(pwd)) {
21295             strength = 1;
21296         } else {
21297             strength = 0;
21298         }
21299         
21300         Roo.log('strength1: ' + strength);
21301         
21302         //var pm = this.trigger.child('div/div/div').dom;
21303         var pm = this.trigger.child('div/div');
21304         pm.removeClass(this.meterClass);
21305         pm.addClass(this.meterClass[strength]);
21306                 
21307         
21308         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21309                 
21310         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21311         
21312         this._lastPwd = pwd;
21313     },
21314     reset: function ()
21315     {
21316         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21317         
21318         this._lastPwd = '';
21319         
21320         var pm = this.trigger.child('div/div');
21321         pm.removeClass(this.meterClass);
21322         pm.addClass('roo-password-meter-grey');        
21323         
21324         
21325         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21326         
21327         pt.innerHTML = '';
21328         this.inputEl().dom.type='password';
21329     },
21330     // private
21331     validateValue: function (value)
21332     {
21333         
21334         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21335             return false;
21336         }
21337         if (value.length == 0) {
21338             if (this.allowBlank) {
21339                 this.clearInvalid();
21340                 return true;
21341             }
21342
21343             this.markInvalid(this.errors.PwdEmpty);
21344             this.errorMsg = this.errors.PwdEmpty;
21345             return false;
21346         }
21347         
21348         if(this.insecure){
21349             return true;
21350         }
21351         
21352         if ('[\x21-\x7e]*'.match(value)) {
21353             this.markInvalid(this.errors.PwdBadChar);
21354             this.errorMsg = this.errors.PwdBadChar;
21355             return false;
21356         }
21357         if (value.length < 6) {
21358             this.markInvalid(this.errors.PwdShort);
21359             this.errorMsg = this.errors.PwdShort;
21360             return false;
21361         }
21362         if (value.length > 16) {
21363             this.markInvalid(this.errors.PwdLong);
21364             this.errorMsg = this.errors.PwdLong;
21365             return false;
21366         }
21367         var strength;
21368         if (this.ClientSideStrongPassword(value)) {
21369             strength = 3;
21370         } else if (this.ClientSideMediumPassword(value)) {
21371             strength = 2;
21372         } else if (this.ClientSideWeakPassword(value)) {
21373             strength = 1;
21374         } else {
21375             strength = 0;
21376         }
21377
21378         
21379         if (strength < 2) {
21380             //this.markInvalid(this.errors.TooWeak);
21381             this.errorMsg = this.errors.TooWeak;
21382             //return false;
21383         }
21384         
21385         
21386         console.log('strength2: ' + strength);
21387         
21388         //var pm = this.trigger.child('div/div/div').dom;
21389         
21390         var pm = this.trigger.child('div/div');
21391         pm.removeClass(this.meterClass);
21392         pm.addClass(this.meterClass[strength]);
21393                 
21394         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21395                 
21396         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21397         
21398         this.errorMsg = ''; 
21399         return true;
21400     },
21401     // private
21402     CharacterSetChecks: function (type)
21403     {
21404         this.type = type;
21405         this.fResult = false;
21406     },
21407     // private
21408     isctype: function (character, type)
21409     {
21410         switch (type) {  
21411             case this.kCapitalLetter:
21412                 if (character >= 'A' && character <= 'Z') {
21413                     return true;
21414                 }
21415                 break;
21416             
21417             case this.kSmallLetter:
21418                 if (character >= 'a' && character <= 'z') {
21419                     return true;
21420                 }
21421                 break;
21422             
21423             case this.kDigit:
21424                 if (character >= '0' && character <= '9') {
21425                     return true;
21426                 }
21427                 break;
21428             
21429             case this.kPunctuation:
21430                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21431                     return true;
21432                 }
21433                 break;
21434             
21435             default:
21436                 return false;
21437         }
21438
21439     },
21440     // private
21441     IsLongEnough: function (pwd, size)
21442     {
21443         return !(pwd == null || isNaN(size) || pwd.length < size);
21444     },
21445     // private
21446     SpansEnoughCharacterSets: function (word, nb)
21447     {
21448         if (!this.IsLongEnough(word, nb))
21449         {
21450             return false;
21451         }
21452
21453         var characterSetChecks = new Array(
21454             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21455             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21456         );
21457         
21458         for (var index = 0; index < word.length; ++index) {
21459             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21460                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21461                     characterSetChecks[nCharSet].fResult = true;
21462                     break;
21463                 }
21464             }
21465         }
21466
21467         var nCharSets = 0;
21468         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21469             if (characterSetChecks[nCharSet].fResult) {
21470                 ++nCharSets;
21471             }
21472         }
21473
21474         if (nCharSets < nb) {
21475             return false;
21476         }
21477         return true;
21478     },
21479     // private
21480     ClientSideStrongPassword: function (pwd)
21481     {
21482         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21483     },
21484     // private
21485     ClientSideMediumPassword: function (pwd)
21486     {
21487         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21488     },
21489     // private
21490     ClientSideWeakPassword: function (pwd)
21491     {
21492         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21493     }
21494           
21495 })//<script type="text/javascript">
21496
21497 /*
21498  * Based  Ext JS Library 1.1.1
21499  * Copyright(c) 2006-2007, Ext JS, LLC.
21500  * LGPL
21501  *
21502  */
21503  
21504 /**
21505  * @class Roo.HtmlEditorCore
21506  * @extends Roo.Component
21507  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21508  *
21509  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21510  */
21511
21512 Roo.HtmlEditorCore = function(config){
21513     
21514     
21515     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21516     
21517     
21518     this.addEvents({
21519         /**
21520          * @event initialize
21521          * Fires when the editor is fully initialized (including the iframe)
21522          * @param {Roo.HtmlEditorCore} this
21523          */
21524         initialize: true,
21525         /**
21526          * @event activate
21527          * Fires when the editor is first receives the focus. Any insertion must wait
21528          * until after this event.
21529          * @param {Roo.HtmlEditorCore} this
21530          */
21531         activate: true,
21532          /**
21533          * @event beforesync
21534          * Fires before the textarea is updated with content from the editor iframe. Return false
21535          * to cancel the sync.
21536          * @param {Roo.HtmlEditorCore} this
21537          * @param {String} html
21538          */
21539         beforesync: true,
21540          /**
21541          * @event beforepush
21542          * Fires before the iframe editor is updated with content from the textarea. Return false
21543          * to cancel the push.
21544          * @param {Roo.HtmlEditorCore} this
21545          * @param {String} html
21546          */
21547         beforepush: true,
21548          /**
21549          * @event sync
21550          * Fires when the textarea is updated with content from the editor iframe.
21551          * @param {Roo.HtmlEditorCore} this
21552          * @param {String} html
21553          */
21554         sync: true,
21555          /**
21556          * @event push
21557          * Fires when the iframe editor is updated with content from the textarea.
21558          * @param {Roo.HtmlEditorCore} this
21559          * @param {String} html
21560          */
21561         push: true,
21562         
21563         /**
21564          * @event editorevent
21565          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21566          * @param {Roo.HtmlEditorCore} this
21567          */
21568         editorevent: true
21569         
21570     });
21571     
21572     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21573     
21574     // defaults : white / black...
21575     this.applyBlacklists();
21576     
21577     
21578     
21579 };
21580
21581
21582 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21583
21584
21585      /**
21586      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21587      */
21588     
21589     owner : false,
21590     
21591      /**
21592      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21593      *                        Roo.resizable.
21594      */
21595     resizable : false,
21596      /**
21597      * @cfg {Number} height (in pixels)
21598      */   
21599     height: 300,
21600    /**
21601      * @cfg {Number} width (in pixels)
21602      */   
21603     width: 500,
21604     
21605     /**
21606      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21607      * 
21608      */
21609     stylesheets: false,
21610     
21611     // id of frame..
21612     frameId: false,
21613     
21614     // private properties
21615     validationEvent : false,
21616     deferHeight: true,
21617     initialized : false,
21618     activated : false,
21619     sourceEditMode : false,
21620     onFocus : Roo.emptyFn,
21621     iframePad:3,
21622     hideMode:'offsets',
21623     
21624     clearUp: true,
21625     
21626     // blacklist + whitelisted elements..
21627     black: false,
21628     white: false,
21629      
21630     bodyCls : '',
21631
21632     /**
21633      * Protected method that will not generally be called directly. It
21634      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21635      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21636      */
21637     getDocMarkup : function(){
21638         // body styles..
21639         var st = '';
21640         
21641         // inherit styels from page...?? 
21642         if (this.stylesheets === false) {
21643             
21644             Roo.get(document.head).select('style').each(function(node) {
21645                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21646             });
21647             
21648             Roo.get(document.head).select('link').each(function(node) { 
21649                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21650             });
21651             
21652         } else if (!this.stylesheets.length) {
21653                 // simple..
21654                 st = '<style type="text/css">' +
21655                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21656                    '</style>';
21657         } else { 
21658             st = '<style type="text/css">' +
21659                     this.stylesheets +
21660                 '</style>';
21661         }
21662         
21663         st +=  '<style type="text/css">' +
21664             'IMG { cursor: pointer } ' +
21665         '</style>';
21666
21667         var cls = 'roo-htmleditor-body';
21668         
21669         if(this.bodyCls.length){
21670             cls += ' ' + this.bodyCls;
21671         }
21672         
21673         return '<html><head>' + st  +
21674             //<style type="text/css">' +
21675             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21676             //'</style>' +
21677             ' </head><body class="' +  cls + '"></body></html>';
21678     },
21679
21680     // private
21681     onRender : function(ct, position)
21682     {
21683         var _t = this;
21684         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21685         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21686         
21687         
21688         this.el.dom.style.border = '0 none';
21689         this.el.dom.setAttribute('tabIndex', -1);
21690         this.el.addClass('x-hidden hide');
21691         
21692         
21693         
21694         if(Roo.isIE){ // fix IE 1px bogus margin
21695             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21696         }
21697        
21698         
21699         this.frameId = Roo.id();
21700         
21701          
21702         
21703         var iframe = this.owner.wrap.createChild({
21704             tag: 'iframe',
21705             cls: 'form-control', // bootstrap..
21706             id: this.frameId,
21707             name: this.frameId,
21708             frameBorder : 'no',
21709             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21710         }, this.el
21711         );
21712         
21713         
21714         this.iframe = iframe.dom;
21715
21716          this.assignDocWin();
21717         
21718         this.doc.designMode = 'on';
21719        
21720         this.doc.open();
21721         this.doc.write(this.getDocMarkup());
21722         this.doc.close();
21723
21724         
21725         var task = { // must defer to wait for browser to be ready
21726             run : function(){
21727                 //console.log("run task?" + this.doc.readyState);
21728                 this.assignDocWin();
21729                 if(this.doc.body || this.doc.readyState == 'complete'){
21730                     try {
21731                         this.doc.designMode="on";
21732                     } catch (e) {
21733                         return;
21734                     }
21735                     Roo.TaskMgr.stop(task);
21736                     this.initEditor.defer(10, this);
21737                 }
21738             },
21739             interval : 10,
21740             duration: 10000,
21741             scope: this
21742         };
21743         Roo.TaskMgr.start(task);
21744
21745     },
21746
21747     // private
21748     onResize : function(w, h)
21749     {
21750          Roo.log('resize: ' +w + ',' + h );
21751         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21752         if(!this.iframe){
21753             return;
21754         }
21755         if(typeof w == 'number'){
21756             
21757             this.iframe.style.width = w + 'px';
21758         }
21759         if(typeof h == 'number'){
21760             
21761             this.iframe.style.height = h + 'px';
21762             if(this.doc){
21763                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21764             }
21765         }
21766         
21767     },
21768
21769     /**
21770      * Toggles the editor between standard and source edit mode.
21771      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21772      */
21773     toggleSourceEdit : function(sourceEditMode){
21774         
21775         this.sourceEditMode = sourceEditMode === true;
21776         
21777         if(this.sourceEditMode){
21778  
21779             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21780             
21781         }else{
21782             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21783             //this.iframe.className = '';
21784             this.deferFocus();
21785         }
21786         //this.setSize(this.owner.wrap.getSize());
21787         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21788     },
21789
21790     
21791   
21792
21793     /**
21794      * Protected method that will not generally be called directly. If you need/want
21795      * custom HTML cleanup, this is the method you should override.
21796      * @param {String} html The HTML to be cleaned
21797      * return {String} The cleaned HTML
21798      */
21799     cleanHtml : function(html){
21800         html = String(html);
21801         if(html.length > 5){
21802             if(Roo.isSafari){ // strip safari nonsense
21803                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21804             }
21805         }
21806         if(html == '&nbsp;'){
21807             html = '';
21808         }
21809         return html;
21810     },
21811
21812     /**
21813      * HTML Editor -> Textarea
21814      * Protected method that will not generally be called directly. Syncs the contents
21815      * of the editor iframe with the textarea.
21816      */
21817     syncValue : function(){
21818         if(this.initialized){
21819             var bd = (this.doc.body || this.doc.documentElement);
21820             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21821             var html = bd.innerHTML;
21822             if(Roo.isSafari){
21823                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21824                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21825                 if(m && m[1]){
21826                     html = '<div style="'+m[0]+'">' + html + '</div>';
21827                 }
21828             }
21829             html = this.cleanHtml(html);
21830             // fix up the special chars.. normaly like back quotes in word...
21831             // however we do not want to do this with chinese..
21832             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21833                 var cc = b.charCodeAt();
21834                 if (
21835                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21836                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21837                     (cc >= 0xf900 && cc < 0xfb00 )
21838                 ) {
21839                         return b;
21840                 }
21841                 return "&#"+cc+";" 
21842             });
21843             if(this.owner.fireEvent('beforesync', this, html) !== false){
21844                 this.el.dom.value = html;
21845                 this.owner.fireEvent('sync', this, html);
21846             }
21847         }
21848     },
21849
21850     /**
21851      * Protected method that will not generally be called directly. Pushes the value of the textarea
21852      * into the iframe editor.
21853      */
21854     pushValue : function(){
21855         if(this.initialized){
21856             var v = this.el.dom.value.trim();
21857             
21858 //            if(v.length < 1){
21859 //                v = '&#160;';
21860 //            }
21861             
21862             if(this.owner.fireEvent('beforepush', this, v) !== false){
21863                 var d = (this.doc.body || this.doc.documentElement);
21864                 d.innerHTML = v;
21865                 this.cleanUpPaste();
21866                 this.el.dom.value = d.innerHTML;
21867                 this.owner.fireEvent('push', this, v);
21868             }
21869         }
21870     },
21871
21872     // private
21873     deferFocus : function(){
21874         this.focus.defer(10, this);
21875     },
21876
21877     // doc'ed in Field
21878     focus : function(){
21879         if(this.win && !this.sourceEditMode){
21880             this.win.focus();
21881         }else{
21882             this.el.focus();
21883         }
21884     },
21885     
21886     assignDocWin: function()
21887     {
21888         var iframe = this.iframe;
21889         
21890          if(Roo.isIE){
21891             this.doc = iframe.contentWindow.document;
21892             this.win = iframe.contentWindow;
21893         } else {
21894 //            if (!Roo.get(this.frameId)) {
21895 //                return;
21896 //            }
21897 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21898 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21899             
21900             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21901                 return;
21902             }
21903             
21904             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21905             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21906         }
21907     },
21908     
21909     // private
21910     initEditor : function(){
21911         //console.log("INIT EDITOR");
21912         this.assignDocWin();
21913         
21914         
21915         
21916         this.doc.designMode="on";
21917         this.doc.open();
21918         this.doc.write(this.getDocMarkup());
21919         this.doc.close();
21920         
21921         var dbody = (this.doc.body || this.doc.documentElement);
21922         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21923         // this copies styles from the containing element into thsi one..
21924         // not sure why we need all of this..
21925         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21926         
21927         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21928         //ss['background-attachment'] = 'fixed'; // w3c
21929         dbody.bgProperties = 'fixed'; // ie
21930         //Roo.DomHelper.applyStyles(dbody, ss);
21931         Roo.EventManager.on(this.doc, {
21932             //'mousedown': this.onEditorEvent,
21933             'mouseup': this.onEditorEvent,
21934             'dblclick': this.onEditorEvent,
21935             'click': this.onEditorEvent,
21936             'keyup': this.onEditorEvent,
21937             buffer:100,
21938             scope: this
21939         });
21940         if(Roo.isGecko){
21941             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21942         }
21943         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21944             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21945         }
21946         this.initialized = true;
21947
21948         this.owner.fireEvent('initialize', this);
21949         this.pushValue();
21950     },
21951
21952     // private
21953     onDestroy : function(){
21954         
21955         
21956         
21957         if(this.rendered){
21958             
21959             //for (var i =0; i < this.toolbars.length;i++) {
21960             //    // fixme - ask toolbars for heights?
21961             //    this.toolbars[i].onDestroy();
21962            // }
21963             
21964             //this.wrap.dom.innerHTML = '';
21965             //this.wrap.remove();
21966         }
21967     },
21968
21969     // private
21970     onFirstFocus : function(){
21971         
21972         this.assignDocWin();
21973         
21974         
21975         this.activated = true;
21976          
21977     
21978         if(Roo.isGecko){ // prevent silly gecko errors
21979             this.win.focus();
21980             var s = this.win.getSelection();
21981             if(!s.focusNode || s.focusNode.nodeType != 3){
21982                 var r = s.getRangeAt(0);
21983                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21984                 r.collapse(true);
21985                 this.deferFocus();
21986             }
21987             try{
21988                 this.execCmd('useCSS', true);
21989                 this.execCmd('styleWithCSS', false);
21990             }catch(e){}
21991         }
21992         this.owner.fireEvent('activate', this);
21993     },
21994
21995     // private
21996     adjustFont: function(btn){
21997         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21998         //if(Roo.isSafari){ // safari
21999         //    adjust *= 2;
22000        // }
22001         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22002         if(Roo.isSafari){ // safari
22003             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22004             v =  (v < 10) ? 10 : v;
22005             v =  (v > 48) ? 48 : v;
22006             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22007             
22008         }
22009         
22010         
22011         v = Math.max(1, v+adjust);
22012         
22013         this.execCmd('FontSize', v  );
22014     },
22015
22016     onEditorEvent : function(e)
22017     {
22018         this.owner.fireEvent('editorevent', this, e);
22019       //  this.updateToolbar();
22020         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22021     },
22022
22023     insertTag : function(tg)
22024     {
22025         // could be a bit smarter... -> wrap the current selected tRoo..
22026         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22027             
22028             range = this.createRange(this.getSelection());
22029             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22030             wrappingNode.appendChild(range.extractContents());
22031             range.insertNode(wrappingNode);
22032
22033             return;
22034             
22035             
22036             
22037         }
22038         this.execCmd("formatblock",   tg);
22039         
22040     },
22041     
22042     insertText : function(txt)
22043     {
22044         
22045         
22046         var range = this.createRange();
22047         range.deleteContents();
22048                //alert(Sender.getAttribute('label'));
22049                
22050         range.insertNode(this.doc.createTextNode(txt));
22051     } ,
22052     
22053      
22054
22055     /**
22056      * Executes a Midas editor command on the editor document and performs necessary focus and
22057      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22058      * @param {String} cmd The Midas command
22059      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22060      */
22061     relayCmd : function(cmd, value){
22062         this.win.focus();
22063         this.execCmd(cmd, value);
22064         this.owner.fireEvent('editorevent', this);
22065         //this.updateToolbar();
22066         this.owner.deferFocus();
22067     },
22068
22069     /**
22070      * Executes a Midas editor command directly on the editor document.
22071      * For visual commands, you should use {@link #relayCmd} instead.
22072      * <b>This should only be called after the editor is initialized.</b>
22073      * @param {String} cmd The Midas command
22074      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22075      */
22076     execCmd : function(cmd, value){
22077         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22078         this.syncValue();
22079     },
22080  
22081  
22082    
22083     /**
22084      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22085      * to insert tRoo.
22086      * @param {String} text | dom node.. 
22087      */
22088     insertAtCursor : function(text)
22089     {
22090         
22091         if(!this.activated){
22092             return;
22093         }
22094         /*
22095         if(Roo.isIE){
22096             this.win.focus();
22097             var r = this.doc.selection.createRange();
22098             if(r){
22099                 r.collapse(true);
22100                 r.pasteHTML(text);
22101                 this.syncValue();
22102                 this.deferFocus();
22103             
22104             }
22105             return;
22106         }
22107         */
22108         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22109             this.win.focus();
22110             
22111             
22112             // from jquery ui (MIT licenced)
22113             var range, node;
22114             var win = this.win;
22115             
22116             if (win.getSelection && win.getSelection().getRangeAt) {
22117                 range = win.getSelection().getRangeAt(0);
22118                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22119                 range.insertNode(node);
22120             } else if (win.document.selection && win.document.selection.createRange) {
22121                 // no firefox support
22122                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22123                 win.document.selection.createRange().pasteHTML(txt);
22124             } else {
22125                 // no firefox support
22126                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22127                 this.execCmd('InsertHTML', txt);
22128             } 
22129             
22130             this.syncValue();
22131             
22132             this.deferFocus();
22133         }
22134     },
22135  // private
22136     mozKeyPress : function(e){
22137         if(e.ctrlKey){
22138             var c = e.getCharCode(), cmd;
22139           
22140             if(c > 0){
22141                 c = String.fromCharCode(c).toLowerCase();
22142                 switch(c){
22143                     case 'b':
22144                         cmd = 'bold';
22145                         break;
22146                     case 'i':
22147                         cmd = 'italic';
22148                         break;
22149                     
22150                     case 'u':
22151                         cmd = 'underline';
22152                         break;
22153                     
22154                     case 'v':
22155                         this.cleanUpPaste.defer(100, this);
22156                         return;
22157                         
22158                 }
22159                 if(cmd){
22160                     this.win.focus();
22161                     this.execCmd(cmd);
22162                     this.deferFocus();
22163                     e.preventDefault();
22164                 }
22165                 
22166             }
22167         }
22168     },
22169
22170     // private
22171     fixKeys : function(){ // load time branching for fastest keydown performance
22172         if(Roo.isIE){
22173             return function(e){
22174                 var k = e.getKey(), r;
22175                 if(k == e.TAB){
22176                     e.stopEvent();
22177                     r = this.doc.selection.createRange();
22178                     if(r){
22179                         r.collapse(true);
22180                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22181                         this.deferFocus();
22182                     }
22183                     return;
22184                 }
22185                 
22186                 if(k == e.ENTER){
22187                     r = this.doc.selection.createRange();
22188                     if(r){
22189                         var target = r.parentElement();
22190                         if(!target || target.tagName.toLowerCase() != 'li'){
22191                             e.stopEvent();
22192                             r.pasteHTML('<br />');
22193                             r.collapse(false);
22194                             r.select();
22195                         }
22196                     }
22197                 }
22198                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22199                     this.cleanUpPaste.defer(100, this);
22200                     return;
22201                 }
22202                 
22203                 
22204             };
22205         }else if(Roo.isOpera){
22206             return function(e){
22207                 var k = e.getKey();
22208                 if(k == e.TAB){
22209                     e.stopEvent();
22210                     this.win.focus();
22211                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22212                     this.deferFocus();
22213                 }
22214                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22215                     this.cleanUpPaste.defer(100, this);
22216                     return;
22217                 }
22218                 
22219             };
22220         }else if(Roo.isSafari){
22221             return function(e){
22222                 var k = e.getKey();
22223                 
22224                 if(k == e.TAB){
22225                     e.stopEvent();
22226                     this.execCmd('InsertText','\t');
22227                     this.deferFocus();
22228                     return;
22229                 }
22230                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22231                     this.cleanUpPaste.defer(100, this);
22232                     return;
22233                 }
22234                 
22235              };
22236         }
22237     }(),
22238     
22239     getAllAncestors: function()
22240     {
22241         var p = this.getSelectedNode();
22242         var a = [];
22243         if (!p) {
22244             a.push(p); // push blank onto stack..
22245             p = this.getParentElement();
22246         }
22247         
22248         
22249         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22250             a.push(p);
22251             p = p.parentNode;
22252         }
22253         a.push(this.doc.body);
22254         return a;
22255     },
22256     lastSel : false,
22257     lastSelNode : false,
22258     
22259     
22260     getSelection : function() 
22261     {
22262         this.assignDocWin();
22263         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22264     },
22265     
22266     getSelectedNode: function() 
22267     {
22268         // this may only work on Gecko!!!
22269         
22270         // should we cache this!!!!
22271         
22272         
22273         
22274          
22275         var range = this.createRange(this.getSelection()).cloneRange();
22276         
22277         if (Roo.isIE) {
22278             var parent = range.parentElement();
22279             while (true) {
22280                 var testRange = range.duplicate();
22281                 testRange.moveToElementText(parent);
22282                 if (testRange.inRange(range)) {
22283                     break;
22284                 }
22285                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22286                     break;
22287                 }
22288                 parent = parent.parentElement;
22289             }
22290             return parent;
22291         }
22292         
22293         // is ancestor a text element.
22294         var ac =  range.commonAncestorContainer;
22295         if (ac.nodeType == 3) {
22296             ac = ac.parentNode;
22297         }
22298         
22299         var ar = ac.childNodes;
22300          
22301         var nodes = [];
22302         var other_nodes = [];
22303         var has_other_nodes = false;
22304         for (var i=0;i<ar.length;i++) {
22305             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22306                 continue;
22307             }
22308             // fullly contained node.
22309             
22310             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22311                 nodes.push(ar[i]);
22312                 continue;
22313             }
22314             
22315             // probably selected..
22316             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22317                 other_nodes.push(ar[i]);
22318                 continue;
22319             }
22320             // outer..
22321             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22322                 continue;
22323             }
22324             
22325             
22326             has_other_nodes = true;
22327         }
22328         if (!nodes.length && other_nodes.length) {
22329             nodes= other_nodes;
22330         }
22331         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22332             return false;
22333         }
22334         
22335         return nodes[0];
22336     },
22337     createRange: function(sel)
22338     {
22339         // this has strange effects when using with 
22340         // top toolbar - not sure if it's a great idea.
22341         //this.editor.contentWindow.focus();
22342         if (typeof sel != "undefined") {
22343             try {
22344                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22345             } catch(e) {
22346                 return this.doc.createRange();
22347             }
22348         } else {
22349             return this.doc.createRange();
22350         }
22351     },
22352     getParentElement: function()
22353     {
22354         
22355         this.assignDocWin();
22356         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22357         
22358         var range = this.createRange(sel);
22359          
22360         try {
22361             var p = range.commonAncestorContainer;
22362             while (p.nodeType == 3) { // text node
22363                 p = p.parentNode;
22364             }
22365             return p;
22366         } catch (e) {
22367             return null;
22368         }
22369     
22370     },
22371     /***
22372      *
22373      * Range intersection.. the hard stuff...
22374      *  '-1' = before
22375      *  '0' = hits..
22376      *  '1' = after.
22377      *         [ -- selected range --- ]
22378      *   [fail]                        [fail]
22379      *
22380      *    basically..
22381      *      if end is before start or  hits it. fail.
22382      *      if start is after end or hits it fail.
22383      *
22384      *   if either hits (but other is outside. - then it's not 
22385      *   
22386      *    
22387      **/
22388     
22389     
22390     // @see http://www.thismuchiknow.co.uk/?p=64.
22391     rangeIntersectsNode : function(range, node)
22392     {
22393         var nodeRange = node.ownerDocument.createRange();
22394         try {
22395             nodeRange.selectNode(node);
22396         } catch (e) {
22397             nodeRange.selectNodeContents(node);
22398         }
22399     
22400         var rangeStartRange = range.cloneRange();
22401         rangeStartRange.collapse(true);
22402     
22403         var rangeEndRange = range.cloneRange();
22404         rangeEndRange.collapse(false);
22405     
22406         var nodeStartRange = nodeRange.cloneRange();
22407         nodeStartRange.collapse(true);
22408     
22409         var nodeEndRange = nodeRange.cloneRange();
22410         nodeEndRange.collapse(false);
22411     
22412         return rangeStartRange.compareBoundaryPoints(
22413                  Range.START_TO_START, nodeEndRange) == -1 &&
22414                rangeEndRange.compareBoundaryPoints(
22415                  Range.START_TO_START, nodeStartRange) == 1;
22416         
22417          
22418     },
22419     rangeCompareNode : function(range, node)
22420     {
22421         var nodeRange = node.ownerDocument.createRange();
22422         try {
22423             nodeRange.selectNode(node);
22424         } catch (e) {
22425             nodeRange.selectNodeContents(node);
22426         }
22427         
22428         
22429         range.collapse(true);
22430     
22431         nodeRange.collapse(true);
22432      
22433         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22434         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22435          
22436         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22437         
22438         var nodeIsBefore   =  ss == 1;
22439         var nodeIsAfter    = ee == -1;
22440         
22441         if (nodeIsBefore && nodeIsAfter) {
22442             return 0; // outer
22443         }
22444         if (!nodeIsBefore && nodeIsAfter) {
22445             return 1; //right trailed.
22446         }
22447         
22448         if (nodeIsBefore && !nodeIsAfter) {
22449             return 2;  // left trailed.
22450         }
22451         // fully contined.
22452         return 3;
22453     },
22454
22455     // private? - in a new class?
22456     cleanUpPaste :  function()
22457     {
22458         // cleans up the whole document..
22459         Roo.log('cleanuppaste');
22460         
22461         this.cleanUpChildren(this.doc.body);
22462         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22463         if (clean != this.doc.body.innerHTML) {
22464             this.doc.body.innerHTML = clean;
22465         }
22466         
22467     },
22468     
22469     cleanWordChars : function(input) {// change the chars to hex code
22470         var he = Roo.HtmlEditorCore;
22471         
22472         var output = input;
22473         Roo.each(he.swapCodes, function(sw) { 
22474             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22475             
22476             output = output.replace(swapper, sw[1]);
22477         });
22478         
22479         return output;
22480     },
22481     
22482     
22483     cleanUpChildren : function (n)
22484     {
22485         if (!n.childNodes.length) {
22486             return;
22487         }
22488         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22489            this.cleanUpChild(n.childNodes[i]);
22490         }
22491     },
22492     
22493     
22494         
22495     
22496     cleanUpChild : function (node)
22497     {
22498         var ed = this;
22499         //console.log(node);
22500         if (node.nodeName == "#text") {
22501             // clean up silly Windows -- stuff?
22502             return; 
22503         }
22504         if (node.nodeName == "#comment") {
22505             node.parentNode.removeChild(node);
22506             // clean up silly Windows -- stuff?
22507             return; 
22508         }
22509         var lcname = node.tagName.toLowerCase();
22510         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22511         // whitelist of tags..
22512         
22513         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22514             // remove node.
22515             node.parentNode.removeChild(node);
22516             return;
22517             
22518         }
22519         
22520         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22521         
22522         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22523         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22524         
22525         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22526         //    remove_keep_children = true;
22527         //}
22528         
22529         if (remove_keep_children) {
22530             this.cleanUpChildren(node);
22531             // inserts everything just before this node...
22532             while (node.childNodes.length) {
22533                 var cn = node.childNodes[0];
22534                 node.removeChild(cn);
22535                 node.parentNode.insertBefore(cn, node);
22536             }
22537             node.parentNode.removeChild(node);
22538             return;
22539         }
22540         
22541         if (!node.attributes || !node.attributes.length) {
22542             this.cleanUpChildren(node);
22543             return;
22544         }
22545         
22546         function cleanAttr(n,v)
22547         {
22548             
22549             if (v.match(/^\./) || v.match(/^\//)) {
22550                 return;
22551             }
22552             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22553                 return;
22554             }
22555             if (v.match(/^#/)) {
22556                 return;
22557             }
22558 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22559             node.removeAttribute(n);
22560             
22561         }
22562         
22563         var cwhite = this.cwhite;
22564         var cblack = this.cblack;
22565             
22566         function cleanStyle(n,v)
22567         {
22568             if (v.match(/expression/)) { //XSS?? should we even bother..
22569                 node.removeAttribute(n);
22570                 return;
22571             }
22572             
22573             var parts = v.split(/;/);
22574             var clean = [];
22575             
22576             Roo.each(parts, function(p) {
22577                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22578                 if (!p.length) {
22579                     return true;
22580                 }
22581                 var l = p.split(':').shift().replace(/\s+/g,'');
22582                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22583                 
22584                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22585 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22586                     //node.removeAttribute(n);
22587                     return true;
22588                 }
22589                 //Roo.log()
22590                 // only allow 'c whitelisted system attributes'
22591                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22592 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22593                     //node.removeAttribute(n);
22594                     return true;
22595                 }
22596                 
22597                 
22598                  
22599                 
22600                 clean.push(p);
22601                 return true;
22602             });
22603             if (clean.length) { 
22604                 node.setAttribute(n, clean.join(';'));
22605             } else {
22606                 node.removeAttribute(n);
22607             }
22608             
22609         }
22610         
22611         
22612         for (var i = node.attributes.length-1; i > -1 ; i--) {
22613             var a = node.attributes[i];
22614             //console.log(a);
22615             
22616             if (a.name.toLowerCase().substr(0,2)=='on')  {
22617                 node.removeAttribute(a.name);
22618                 continue;
22619             }
22620             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22621                 node.removeAttribute(a.name);
22622                 continue;
22623             }
22624             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22625                 cleanAttr(a.name,a.value); // fixme..
22626                 continue;
22627             }
22628             if (a.name == 'style') {
22629                 cleanStyle(a.name,a.value);
22630                 continue;
22631             }
22632             /// clean up MS crap..
22633             // tecnically this should be a list of valid class'es..
22634             
22635             
22636             if (a.name == 'class') {
22637                 if (a.value.match(/^Mso/)) {
22638                     node.className = '';
22639                 }
22640                 
22641                 if (a.value.match(/^body$/)) {
22642                     node.className = '';
22643                 }
22644                 continue;
22645             }
22646             
22647             // style cleanup!?
22648             // class cleanup?
22649             
22650         }
22651         
22652         
22653         this.cleanUpChildren(node);
22654         
22655         
22656     },
22657     
22658     /**
22659      * Clean up MS wordisms...
22660      */
22661     cleanWord : function(node)
22662     {
22663         
22664         
22665         if (!node) {
22666             this.cleanWord(this.doc.body);
22667             return;
22668         }
22669         if (node.nodeName == "#text") {
22670             // clean up silly Windows -- stuff?
22671             return; 
22672         }
22673         if (node.nodeName == "#comment") {
22674             node.parentNode.removeChild(node);
22675             // clean up silly Windows -- stuff?
22676             return; 
22677         }
22678         
22679         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22680             node.parentNode.removeChild(node);
22681             return;
22682         }
22683         
22684         // remove - but keep children..
22685         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22686             while (node.childNodes.length) {
22687                 var cn = node.childNodes[0];
22688                 node.removeChild(cn);
22689                 node.parentNode.insertBefore(cn, node);
22690             }
22691             node.parentNode.removeChild(node);
22692             this.iterateChildren(node, this.cleanWord);
22693             return;
22694         }
22695         // clean styles
22696         if (node.className.length) {
22697             
22698             var cn = node.className.split(/\W+/);
22699             var cna = [];
22700             Roo.each(cn, function(cls) {
22701                 if (cls.match(/Mso[a-zA-Z]+/)) {
22702                     return;
22703                 }
22704                 cna.push(cls);
22705             });
22706             node.className = cna.length ? cna.join(' ') : '';
22707             if (!cna.length) {
22708                 node.removeAttribute("class");
22709             }
22710         }
22711         
22712         if (node.hasAttribute("lang")) {
22713             node.removeAttribute("lang");
22714         }
22715         
22716         if (node.hasAttribute("style")) {
22717             
22718             var styles = node.getAttribute("style").split(";");
22719             var nstyle = [];
22720             Roo.each(styles, function(s) {
22721                 if (!s.match(/:/)) {
22722                     return;
22723                 }
22724                 var kv = s.split(":");
22725                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22726                     return;
22727                 }
22728                 // what ever is left... we allow.
22729                 nstyle.push(s);
22730             });
22731             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22732             if (!nstyle.length) {
22733                 node.removeAttribute('style');
22734             }
22735         }
22736         this.iterateChildren(node, this.cleanWord);
22737         
22738         
22739         
22740     },
22741     /**
22742      * iterateChildren of a Node, calling fn each time, using this as the scole..
22743      * @param {DomNode} node node to iterate children of.
22744      * @param {Function} fn method of this class to call on each item.
22745      */
22746     iterateChildren : function(node, fn)
22747     {
22748         if (!node.childNodes.length) {
22749                 return;
22750         }
22751         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22752            fn.call(this, node.childNodes[i])
22753         }
22754     },
22755     
22756     
22757     /**
22758      * cleanTableWidths.
22759      *
22760      * Quite often pasting from word etc.. results in tables with column and widths.
22761      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22762      *
22763      */
22764     cleanTableWidths : function(node)
22765     {
22766          
22767          
22768         if (!node) {
22769             this.cleanTableWidths(this.doc.body);
22770             return;
22771         }
22772         
22773         // ignore list...
22774         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22775             return; 
22776         }
22777         Roo.log(node.tagName);
22778         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22779             this.iterateChildren(node, this.cleanTableWidths);
22780             return;
22781         }
22782         if (node.hasAttribute('width')) {
22783             node.removeAttribute('width');
22784         }
22785         
22786          
22787         if (node.hasAttribute("style")) {
22788             // pretty basic...
22789             
22790             var styles = node.getAttribute("style").split(";");
22791             var nstyle = [];
22792             Roo.each(styles, function(s) {
22793                 if (!s.match(/:/)) {
22794                     return;
22795                 }
22796                 var kv = s.split(":");
22797                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22798                     return;
22799                 }
22800                 // what ever is left... we allow.
22801                 nstyle.push(s);
22802             });
22803             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22804             if (!nstyle.length) {
22805                 node.removeAttribute('style');
22806             }
22807         }
22808         
22809         this.iterateChildren(node, this.cleanTableWidths);
22810         
22811         
22812     },
22813     
22814     
22815     
22816     
22817     domToHTML : function(currentElement, depth, nopadtext) {
22818         
22819         depth = depth || 0;
22820         nopadtext = nopadtext || false;
22821     
22822         if (!currentElement) {
22823             return this.domToHTML(this.doc.body);
22824         }
22825         
22826         //Roo.log(currentElement);
22827         var j;
22828         var allText = false;
22829         var nodeName = currentElement.nodeName;
22830         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22831         
22832         if  (nodeName == '#text') {
22833             
22834             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22835         }
22836         
22837         
22838         var ret = '';
22839         if (nodeName != 'BODY') {
22840              
22841             var i = 0;
22842             // Prints the node tagName, such as <A>, <IMG>, etc
22843             if (tagName) {
22844                 var attr = [];
22845                 for(i = 0; i < currentElement.attributes.length;i++) {
22846                     // quoting?
22847                     var aname = currentElement.attributes.item(i).name;
22848                     if (!currentElement.attributes.item(i).value.length) {
22849                         continue;
22850                     }
22851                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22852                 }
22853                 
22854                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22855             } 
22856             else {
22857                 
22858                 // eack
22859             }
22860         } else {
22861             tagName = false;
22862         }
22863         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22864             return ret;
22865         }
22866         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22867             nopadtext = true;
22868         }
22869         
22870         
22871         // Traverse the tree
22872         i = 0;
22873         var currentElementChild = currentElement.childNodes.item(i);
22874         var allText = true;
22875         var innerHTML  = '';
22876         lastnode = '';
22877         while (currentElementChild) {
22878             // Formatting code (indent the tree so it looks nice on the screen)
22879             var nopad = nopadtext;
22880             if (lastnode == 'SPAN') {
22881                 nopad  = true;
22882             }
22883             // text
22884             if  (currentElementChild.nodeName == '#text') {
22885                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22886                 toadd = nopadtext ? toadd : toadd.trim();
22887                 if (!nopad && toadd.length > 80) {
22888                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22889                 }
22890                 innerHTML  += toadd;
22891                 
22892                 i++;
22893                 currentElementChild = currentElement.childNodes.item(i);
22894                 lastNode = '';
22895                 continue;
22896             }
22897             allText = false;
22898             
22899             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22900                 
22901             // Recursively traverse the tree structure of the child node
22902             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22903             lastnode = currentElementChild.nodeName;
22904             i++;
22905             currentElementChild=currentElement.childNodes.item(i);
22906         }
22907         
22908         ret += innerHTML;
22909         
22910         if (!allText) {
22911                 // The remaining code is mostly for formatting the tree
22912             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22913         }
22914         
22915         
22916         if (tagName) {
22917             ret+= "</"+tagName+">";
22918         }
22919         return ret;
22920         
22921     },
22922         
22923     applyBlacklists : function()
22924     {
22925         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22926         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22927         
22928         this.white = [];
22929         this.black = [];
22930         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22931             if (b.indexOf(tag) > -1) {
22932                 return;
22933             }
22934             this.white.push(tag);
22935             
22936         }, this);
22937         
22938         Roo.each(w, function(tag) {
22939             if (b.indexOf(tag) > -1) {
22940                 return;
22941             }
22942             if (this.white.indexOf(tag) > -1) {
22943                 return;
22944             }
22945             this.white.push(tag);
22946             
22947         }, this);
22948         
22949         
22950         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22951             if (w.indexOf(tag) > -1) {
22952                 return;
22953             }
22954             this.black.push(tag);
22955             
22956         }, this);
22957         
22958         Roo.each(b, function(tag) {
22959             if (w.indexOf(tag) > -1) {
22960                 return;
22961             }
22962             if (this.black.indexOf(tag) > -1) {
22963                 return;
22964             }
22965             this.black.push(tag);
22966             
22967         }, this);
22968         
22969         
22970         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22971         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22972         
22973         this.cwhite = [];
22974         this.cblack = [];
22975         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22976             if (b.indexOf(tag) > -1) {
22977                 return;
22978             }
22979             this.cwhite.push(tag);
22980             
22981         }, this);
22982         
22983         Roo.each(w, function(tag) {
22984             if (b.indexOf(tag) > -1) {
22985                 return;
22986             }
22987             if (this.cwhite.indexOf(tag) > -1) {
22988                 return;
22989             }
22990             this.cwhite.push(tag);
22991             
22992         }, this);
22993         
22994         
22995         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22996             if (w.indexOf(tag) > -1) {
22997                 return;
22998             }
22999             this.cblack.push(tag);
23000             
23001         }, this);
23002         
23003         Roo.each(b, function(tag) {
23004             if (w.indexOf(tag) > -1) {
23005                 return;
23006             }
23007             if (this.cblack.indexOf(tag) > -1) {
23008                 return;
23009             }
23010             this.cblack.push(tag);
23011             
23012         }, this);
23013     },
23014     
23015     setStylesheets : function(stylesheets)
23016     {
23017         if(typeof(stylesheets) == 'string'){
23018             Roo.get(this.iframe.contentDocument.head).createChild({
23019                 tag : 'link',
23020                 rel : 'stylesheet',
23021                 type : 'text/css',
23022                 href : stylesheets
23023             });
23024             
23025             return;
23026         }
23027         var _this = this;
23028      
23029         Roo.each(stylesheets, function(s) {
23030             if(!s.length){
23031                 return;
23032             }
23033             
23034             Roo.get(_this.iframe.contentDocument.head).createChild({
23035                 tag : 'link',
23036                 rel : 'stylesheet',
23037                 type : 'text/css',
23038                 href : s
23039             });
23040         });
23041
23042         
23043     },
23044     
23045     removeStylesheets : function()
23046     {
23047         var _this = this;
23048         
23049         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23050             s.remove();
23051         });
23052     },
23053     
23054     setStyle : function(style)
23055     {
23056         Roo.get(this.iframe.contentDocument.head).createChild({
23057             tag : 'style',
23058             type : 'text/css',
23059             html : style
23060         });
23061
23062         return;
23063     }
23064     
23065     // hide stuff that is not compatible
23066     /**
23067      * @event blur
23068      * @hide
23069      */
23070     /**
23071      * @event change
23072      * @hide
23073      */
23074     /**
23075      * @event focus
23076      * @hide
23077      */
23078     /**
23079      * @event specialkey
23080      * @hide
23081      */
23082     /**
23083      * @cfg {String} fieldClass @hide
23084      */
23085     /**
23086      * @cfg {String} focusClass @hide
23087      */
23088     /**
23089      * @cfg {String} autoCreate @hide
23090      */
23091     /**
23092      * @cfg {String} inputType @hide
23093      */
23094     /**
23095      * @cfg {String} invalidClass @hide
23096      */
23097     /**
23098      * @cfg {String} invalidText @hide
23099      */
23100     /**
23101      * @cfg {String} msgFx @hide
23102      */
23103     /**
23104      * @cfg {String} validateOnBlur @hide
23105      */
23106 });
23107
23108 Roo.HtmlEditorCore.white = [
23109         'area', 'br', 'img', 'input', 'hr', 'wbr',
23110         
23111        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23112        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23113        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23114        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23115        'table',   'ul',         'xmp', 
23116        
23117        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23118       'thead',   'tr', 
23119      
23120       'dir', 'menu', 'ol', 'ul', 'dl',
23121        
23122       'embed',  'object'
23123 ];
23124
23125
23126 Roo.HtmlEditorCore.black = [
23127     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23128         'applet', // 
23129         'base',   'basefont', 'bgsound', 'blink',  'body', 
23130         'frame',  'frameset', 'head',    'html',   'ilayer', 
23131         'iframe', 'layer',  'link',     'meta',    'object',   
23132         'script', 'style' ,'title',  'xml' // clean later..
23133 ];
23134 Roo.HtmlEditorCore.clean = [
23135     'script', 'style', 'title', 'xml'
23136 ];
23137 Roo.HtmlEditorCore.remove = [
23138     'font'
23139 ];
23140 // attributes..
23141
23142 Roo.HtmlEditorCore.ablack = [
23143     'on'
23144 ];
23145     
23146 Roo.HtmlEditorCore.aclean = [ 
23147     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23148 ];
23149
23150 // protocols..
23151 Roo.HtmlEditorCore.pwhite= [
23152         'http',  'https',  'mailto'
23153 ];
23154
23155 // white listed style attributes.
23156 Roo.HtmlEditorCore.cwhite= [
23157       //  'text-align', /// default is to allow most things..
23158       
23159          
23160 //        'font-size'//??
23161 ];
23162
23163 // black listed style attributes.
23164 Roo.HtmlEditorCore.cblack= [
23165       //  'font-size' -- this can be set by the project 
23166 ];
23167
23168
23169 Roo.HtmlEditorCore.swapCodes   =[ 
23170     [    8211, "--" ], 
23171     [    8212, "--" ], 
23172     [    8216,  "'" ],  
23173     [    8217, "'" ],  
23174     [    8220, '"' ],  
23175     [    8221, '"' ],  
23176     [    8226, "*" ],  
23177     [    8230, "..." ]
23178 ]; 
23179
23180     /*
23181  * - LGPL
23182  *
23183  * HtmlEditor
23184  * 
23185  */
23186
23187 /**
23188  * @class Roo.bootstrap.HtmlEditor
23189  * @extends Roo.bootstrap.TextArea
23190  * Bootstrap HtmlEditor class
23191
23192  * @constructor
23193  * Create a new HtmlEditor
23194  * @param {Object} config The config object
23195  */
23196
23197 Roo.bootstrap.HtmlEditor = function(config){
23198     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23199     if (!this.toolbars) {
23200         this.toolbars = [];
23201     }
23202     
23203     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23204     this.addEvents({
23205             /**
23206              * @event initialize
23207              * Fires when the editor is fully initialized (including the iframe)
23208              * @param {HtmlEditor} this
23209              */
23210             initialize: true,
23211             /**
23212              * @event activate
23213              * Fires when the editor is first receives the focus. Any insertion must wait
23214              * until after this event.
23215              * @param {HtmlEditor} this
23216              */
23217             activate: true,
23218              /**
23219              * @event beforesync
23220              * Fires before the textarea is updated with content from the editor iframe. Return false
23221              * to cancel the sync.
23222              * @param {HtmlEditor} this
23223              * @param {String} html
23224              */
23225             beforesync: true,
23226              /**
23227              * @event beforepush
23228              * Fires before the iframe editor is updated with content from the textarea. Return false
23229              * to cancel the push.
23230              * @param {HtmlEditor} this
23231              * @param {String} html
23232              */
23233             beforepush: true,
23234              /**
23235              * @event sync
23236              * Fires when the textarea is updated with content from the editor iframe.
23237              * @param {HtmlEditor} this
23238              * @param {String} html
23239              */
23240             sync: true,
23241              /**
23242              * @event push
23243              * Fires when the iframe editor is updated with content from the textarea.
23244              * @param {HtmlEditor} this
23245              * @param {String} html
23246              */
23247             push: true,
23248              /**
23249              * @event editmodechange
23250              * Fires when the editor switches edit modes
23251              * @param {HtmlEditor} this
23252              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23253              */
23254             editmodechange: true,
23255             /**
23256              * @event editorevent
23257              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23258              * @param {HtmlEditor} this
23259              */
23260             editorevent: true,
23261             /**
23262              * @event firstfocus
23263              * Fires when on first focus - needed by toolbars..
23264              * @param {HtmlEditor} this
23265              */
23266             firstfocus: true,
23267             /**
23268              * @event autosave
23269              * Auto save the htmlEditor value as a file into Events
23270              * @param {HtmlEditor} this
23271              */
23272             autosave: true,
23273             /**
23274              * @event savedpreview
23275              * preview the saved version of htmlEditor
23276              * @param {HtmlEditor} this
23277              */
23278             savedpreview: true
23279         });
23280 };
23281
23282
23283 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23284     
23285     
23286       /**
23287      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23288      */
23289     toolbars : false,
23290     
23291      /**
23292     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23293     */
23294     btns : [],
23295    
23296      /**
23297      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23298      *                        Roo.resizable.
23299      */
23300     resizable : false,
23301      /**
23302      * @cfg {Number} height (in pixels)
23303      */   
23304     height: 300,
23305    /**
23306      * @cfg {Number} width (in pixels)
23307      */   
23308     width: false,
23309     
23310     /**
23311      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23312      * 
23313      */
23314     stylesheets: false,
23315     
23316     // id of frame..
23317     frameId: false,
23318     
23319     // private properties
23320     validationEvent : false,
23321     deferHeight: true,
23322     initialized : false,
23323     activated : false,
23324     
23325     onFocus : Roo.emptyFn,
23326     iframePad:3,
23327     hideMode:'offsets',
23328     
23329     tbContainer : false,
23330     
23331     bodyCls : '',
23332     
23333     toolbarContainer :function() {
23334         return this.wrap.select('.x-html-editor-tb',true).first();
23335     },
23336
23337     /**
23338      * Protected method that will not generally be called directly. It
23339      * is called when the editor creates its toolbar. Override this method if you need to
23340      * add custom toolbar buttons.
23341      * @param {HtmlEditor} editor
23342      */
23343     createToolbar : function(){
23344         Roo.log('renewing');
23345         Roo.log("create toolbars");
23346         
23347         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23348         this.toolbars[0].render(this.toolbarContainer());
23349         
23350         return;
23351         
23352 //        if (!editor.toolbars || !editor.toolbars.length) {
23353 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23354 //        }
23355 //        
23356 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23357 //            editor.toolbars[i] = Roo.factory(
23358 //                    typeof(editor.toolbars[i]) == 'string' ?
23359 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23360 //                Roo.bootstrap.HtmlEditor);
23361 //            editor.toolbars[i].init(editor);
23362 //        }
23363     },
23364
23365      
23366     // private
23367     onRender : function(ct, position)
23368     {
23369        // Roo.log("Call onRender: " + this.xtype);
23370         var _t = this;
23371         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23372       
23373         this.wrap = this.inputEl().wrap({
23374             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23375         });
23376         
23377         this.editorcore.onRender(ct, position);
23378          
23379         if (this.resizable) {
23380             this.resizeEl = new Roo.Resizable(this.wrap, {
23381                 pinned : true,
23382                 wrap: true,
23383                 dynamic : true,
23384                 minHeight : this.height,
23385                 height: this.height,
23386                 handles : this.resizable,
23387                 width: this.width,
23388                 listeners : {
23389                     resize : function(r, w, h) {
23390                         _t.onResize(w,h); // -something
23391                     }
23392                 }
23393             });
23394             
23395         }
23396         this.createToolbar(this);
23397        
23398         
23399         if(!this.width && this.resizable){
23400             this.setSize(this.wrap.getSize());
23401         }
23402         if (this.resizeEl) {
23403             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23404             // should trigger onReize..
23405         }
23406         
23407     },
23408
23409     // private
23410     onResize : function(w, h)
23411     {
23412         Roo.log('resize: ' +w + ',' + h );
23413         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23414         var ew = false;
23415         var eh = false;
23416         
23417         if(this.inputEl() ){
23418             if(typeof w == 'number'){
23419                 var aw = w - this.wrap.getFrameWidth('lr');
23420                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23421                 ew = aw;
23422             }
23423             if(typeof h == 'number'){
23424                  var tbh = -11;  // fixme it needs to tool bar size!
23425                 for (var i =0; i < this.toolbars.length;i++) {
23426                     // fixme - ask toolbars for heights?
23427                     tbh += this.toolbars[i].el.getHeight();
23428                     //if (this.toolbars[i].footer) {
23429                     //    tbh += this.toolbars[i].footer.el.getHeight();
23430                     //}
23431                 }
23432               
23433                 
23434                 
23435                 
23436                 
23437                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23438                 ah -= 5; // knock a few pixes off for look..
23439                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23440                 var eh = ah;
23441             }
23442         }
23443         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23444         this.editorcore.onResize(ew,eh);
23445         
23446     },
23447
23448     /**
23449      * Toggles the editor between standard and source edit mode.
23450      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23451      */
23452     toggleSourceEdit : function(sourceEditMode)
23453     {
23454         this.editorcore.toggleSourceEdit(sourceEditMode);
23455         
23456         if(this.editorcore.sourceEditMode){
23457             Roo.log('editor - showing textarea');
23458             
23459 //            Roo.log('in');
23460 //            Roo.log(this.syncValue());
23461             this.syncValue();
23462             this.inputEl().removeClass(['hide', 'x-hidden']);
23463             this.inputEl().dom.removeAttribute('tabIndex');
23464             this.inputEl().focus();
23465         }else{
23466             Roo.log('editor - hiding textarea');
23467 //            Roo.log('out')
23468 //            Roo.log(this.pushValue()); 
23469             this.pushValue();
23470             
23471             this.inputEl().addClass(['hide', 'x-hidden']);
23472             this.inputEl().dom.setAttribute('tabIndex', -1);
23473             //this.deferFocus();
23474         }
23475          
23476         if(this.resizable){
23477             this.setSize(this.wrap.getSize());
23478         }
23479         
23480         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23481     },
23482  
23483     // private (for BoxComponent)
23484     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23485
23486     // private (for BoxComponent)
23487     getResizeEl : function(){
23488         return this.wrap;
23489     },
23490
23491     // private (for BoxComponent)
23492     getPositionEl : function(){
23493         return this.wrap;
23494     },
23495
23496     // private
23497     initEvents : function(){
23498         this.originalValue = this.getValue();
23499     },
23500
23501 //    /**
23502 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23503 //     * @method
23504 //     */
23505 //    markInvalid : Roo.emptyFn,
23506 //    /**
23507 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23508 //     * @method
23509 //     */
23510 //    clearInvalid : Roo.emptyFn,
23511
23512     setValue : function(v){
23513         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23514         this.editorcore.pushValue();
23515     },
23516
23517      
23518     // private
23519     deferFocus : function(){
23520         this.focus.defer(10, this);
23521     },
23522
23523     // doc'ed in Field
23524     focus : function(){
23525         this.editorcore.focus();
23526         
23527     },
23528       
23529
23530     // private
23531     onDestroy : function(){
23532         
23533         
23534         
23535         if(this.rendered){
23536             
23537             for (var i =0; i < this.toolbars.length;i++) {
23538                 // fixme - ask toolbars for heights?
23539                 this.toolbars[i].onDestroy();
23540             }
23541             
23542             this.wrap.dom.innerHTML = '';
23543             this.wrap.remove();
23544         }
23545     },
23546
23547     // private
23548     onFirstFocus : function(){
23549         //Roo.log("onFirstFocus");
23550         this.editorcore.onFirstFocus();
23551          for (var i =0; i < this.toolbars.length;i++) {
23552             this.toolbars[i].onFirstFocus();
23553         }
23554         
23555     },
23556     
23557     // private
23558     syncValue : function()
23559     {   
23560         this.editorcore.syncValue();
23561     },
23562     
23563     pushValue : function()
23564     {   
23565         this.editorcore.pushValue();
23566     }
23567      
23568     
23569     // hide stuff that is not compatible
23570     /**
23571      * @event blur
23572      * @hide
23573      */
23574     /**
23575      * @event change
23576      * @hide
23577      */
23578     /**
23579      * @event focus
23580      * @hide
23581      */
23582     /**
23583      * @event specialkey
23584      * @hide
23585      */
23586     /**
23587      * @cfg {String} fieldClass @hide
23588      */
23589     /**
23590      * @cfg {String} focusClass @hide
23591      */
23592     /**
23593      * @cfg {String} autoCreate @hide
23594      */
23595     /**
23596      * @cfg {String} inputType @hide
23597      */
23598     /**
23599      * @cfg {String} invalidClass @hide
23600      */
23601     /**
23602      * @cfg {String} invalidText @hide
23603      */
23604     /**
23605      * @cfg {String} msgFx @hide
23606      */
23607     /**
23608      * @cfg {String} validateOnBlur @hide
23609      */
23610 });
23611  
23612     
23613    
23614    
23615    
23616       
23617 Roo.namespace('Roo.bootstrap.htmleditor');
23618 /**
23619  * @class Roo.bootstrap.HtmlEditorToolbar1
23620  * Basic Toolbar
23621  * 
23622  * Usage:
23623  *
23624  new Roo.bootstrap.HtmlEditor({
23625     ....
23626     toolbars : [
23627         new Roo.bootstrap.HtmlEditorToolbar1({
23628             disable : { fonts: 1 , format: 1, ..., ... , ...],
23629             btns : [ .... ]
23630         })
23631     }
23632      
23633  * 
23634  * @cfg {Object} disable List of elements to disable..
23635  * @cfg {Array} btns List of additional buttons.
23636  * 
23637  * 
23638  * NEEDS Extra CSS? 
23639  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23640  */
23641  
23642 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23643 {
23644     
23645     Roo.apply(this, config);
23646     
23647     // default disabled, based on 'good practice'..
23648     this.disable = this.disable || {};
23649     Roo.applyIf(this.disable, {
23650         fontSize : true,
23651         colors : true,
23652         specialElements : true
23653     });
23654     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23655     
23656     this.editor = config.editor;
23657     this.editorcore = config.editor.editorcore;
23658     
23659     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23660     
23661     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23662     // dont call parent... till later.
23663 }
23664 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23665      
23666     bar : true,
23667     
23668     editor : false,
23669     editorcore : false,
23670     
23671     
23672     formats : [
23673         "p" ,  
23674         "h1","h2","h3","h4","h5","h6", 
23675         "pre", "code", 
23676         "abbr", "acronym", "address", "cite", "samp", "var",
23677         'div','span'
23678     ],
23679     
23680     onRender : function(ct, position)
23681     {
23682        // Roo.log("Call onRender: " + this.xtype);
23683         
23684        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23685        Roo.log(this.el);
23686        this.el.dom.style.marginBottom = '0';
23687        var _this = this;
23688        var editorcore = this.editorcore;
23689        var editor= this.editor;
23690        
23691        var children = [];
23692        var btn = function(id,cmd , toggle, handler, html){
23693        
23694             var  event = toggle ? 'toggle' : 'click';
23695        
23696             var a = {
23697                 size : 'sm',
23698                 xtype: 'Button',
23699                 xns: Roo.bootstrap,
23700                 glyphicon : id,
23701                 cmd : id || cmd,
23702                 enableToggle:toggle !== false,
23703                 html : html || '',
23704                 pressed : toggle ? false : null,
23705                 listeners : {}
23706             };
23707             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23708                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23709             };
23710             children.push(a);
23711             return a;
23712        }
23713        
23714     //    var cb_box = function...
23715         
23716         var style = {
23717                 xtype: 'Button',
23718                 size : 'sm',
23719                 xns: Roo.bootstrap,
23720                 glyphicon : 'font',
23721                 //html : 'submit'
23722                 menu : {
23723                     xtype: 'Menu',
23724                     xns: Roo.bootstrap,
23725                     items:  []
23726                 }
23727         };
23728         Roo.each(this.formats, function(f) {
23729             style.menu.items.push({
23730                 xtype :'MenuItem',
23731                 xns: Roo.bootstrap,
23732                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23733                 tagname : f,
23734                 listeners : {
23735                     click : function()
23736                     {
23737                         editorcore.insertTag(this.tagname);
23738                         editor.focus();
23739                     }
23740                 }
23741                 
23742             });
23743         });
23744         children.push(style);   
23745         
23746         btn('bold',false,true);
23747         btn('italic',false,true);
23748         btn('align-left', 'justifyleft',true);
23749         btn('align-center', 'justifycenter',true);
23750         btn('align-right' , 'justifyright',true);
23751         btn('link', false, false, function(btn) {
23752             //Roo.log("create link?");
23753             var url = prompt(this.createLinkText, this.defaultLinkValue);
23754             if(url && url != 'http:/'+'/'){
23755                 this.editorcore.relayCmd('createlink', url);
23756             }
23757         }),
23758         btn('list','insertunorderedlist',true);
23759         btn('pencil', false,true, function(btn){
23760                 Roo.log(this);
23761                 this.toggleSourceEdit(btn.pressed);
23762         });
23763         
23764         if (this.editor.btns.length > 0) {
23765             for (var i = 0; i<this.editor.btns.length; i++) {
23766                 children.push(this.editor.btns[i]);
23767             }
23768         }
23769         
23770         /*
23771         var cog = {
23772                 xtype: 'Button',
23773                 size : 'sm',
23774                 xns: Roo.bootstrap,
23775                 glyphicon : 'cog',
23776                 //html : 'submit'
23777                 menu : {
23778                     xtype: 'Menu',
23779                     xns: Roo.bootstrap,
23780                     items:  []
23781                 }
23782         };
23783         
23784         cog.menu.items.push({
23785             xtype :'MenuItem',
23786             xns: Roo.bootstrap,
23787             html : Clean styles,
23788             tagname : f,
23789             listeners : {
23790                 click : function()
23791                 {
23792                     editorcore.insertTag(this.tagname);
23793                     editor.focus();
23794                 }
23795             }
23796             
23797         });
23798        */
23799         
23800          
23801        this.xtype = 'NavSimplebar';
23802         
23803         for(var i=0;i< children.length;i++) {
23804             
23805             this.buttons.add(this.addxtypeChild(children[i]));
23806             
23807         }
23808         
23809         editor.on('editorevent', this.updateToolbar, this);
23810     },
23811     onBtnClick : function(id)
23812     {
23813        this.editorcore.relayCmd(id);
23814        this.editorcore.focus();
23815     },
23816     
23817     /**
23818      * Protected method that will not generally be called directly. It triggers
23819      * a toolbar update by reading the markup state of the current selection in the editor.
23820      */
23821     updateToolbar: function(){
23822
23823         if(!this.editorcore.activated){
23824             this.editor.onFirstFocus(); // is this neeed?
23825             return;
23826         }
23827
23828         var btns = this.buttons; 
23829         var doc = this.editorcore.doc;
23830         btns.get('bold').setActive(doc.queryCommandState('bold'));
23831         btns.get('italic').setActive(doc.queryCommandState('italic'));
23832         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23833         
23834         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23835         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23836         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23837         
23838         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23839         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23840          /*
23841         
23842         var ans = this.editorcore.getAllAncestors();
23843         if (this.formatCombo) {
23844             
23845             
23846             var store = this.formatCombo.store;
23847             this.formatCombo.setValue("");
23848             for (var i =0; i < ans.length;i++) {
23849                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23850                     // select it..
23851                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23852                     break;
23853                 }
23854             }
23855         }
23856         
23857         
23858         
23859         // hides menus... - so this cant be on a menu...
23860         Roo.bootstrap.MenuMgr.hideAll();
23861         */
23862         Roo.bootstrap.MenuMgr.hideAll();
23863         //this.editorsyncValue();
23864     },
23865     onFirstFocus: function() {
23866         this.buttons.each(function(item){
23867            item.enable();
23868         });
23869     },
23870     toggleSourceEdit : function(sourceEditMode){
23871         
23872           
23873         if(sourceEditMode){
23874             Roo.log("disabling buttons");
23875            this.buttons.each( function(item){
23876                 if(item.cmd != 'pencil'){
23877                     item.disable();
23878                 }
23879             });
23880           
23881         }else{
23882             Roo.log("enabling buttons");
23883             if(this.editorcore.initialized){
23884                 this.buttons.each( function(item){
23885                     item.enable();
23886                 });
23887             }
23888             
23889         }
23890         Roo.log("calling toggole on editor");
23891         // tell the editor that it's been pressed..
23892         this.editor.toggleSourceEdit(sourceEditMode);
23893        
23894     }
23895 });
23896
23897
23898
23899
23900
23901 /**
23902  * @class Roo.bootstrap.Table.AbstractSelectionModel
23903  * @extends Roo.util.Observable
23904  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23905  * implemented by descendant classes.  This class should not be directly instantiated.
23906  * @constructor
23907  */
23908 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23909     this.locked = false;
23910     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23911 };
23912
23913
23914 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23915     /** @ignore Called by the grid automatically. Do not call directly. */
23916     init : function(grid){
23917         this.grid = grid;
23918         this.initEvents();
23919     },
23920
23921     /**
23922      * Locks the selections.
23923      */
23924     lock : function(){
23925         this.locked = true;
23926     },
23927
23928     /**
23929      * Unlocks the selections.
23930      */
23931     unlock : function(){
23932         this.locked = false;
23933     },
23934
23935     /**
23936      * Returns true if the selections are locked.
23937      * @return {Boolean}
23938      */
23939     isLocked : function(){
23940         return this.locked;
23941     }
23942 });
23943 /**
23944  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23945  * @class Roo.bootstrap.Table.RowSelectionModel
23946  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23947  * It supports multiple selections and keyboard selection/navigation. 
23948  * @constructor
23949  * @param {Object} config
23950  */
23951
23952 Roo.bootstrap.Table.RowSelectionModel = function(config){
23953     Roo.apply(this, config);
23954     this.selections = new Roo.util.MixedCollection(false, function(o){
23955         return o.id;
23956     });
23957
23958     this.last = false;
23959     this.lastActive = false;
23960
23961     this.addEvents({
23962         /**
23963              * @event selectionchange
23964              * Fires when the selection changes
23965              * @param {SelectionModel} this
23966              */
23967             "selectionchange" : true,
23968         /**
23969              * @event afterselectionchange
23970              * Fires after the selection changes (eg. by key press or clicking)
23971              * @param {SelectionModel} this
23972              */
23973             "afterselectionchange" : true,
23974         /**
23975              * @event beforerowselect
23976              * Fires when a row is selected being selected, return false to cancel.
23977              * @param {SelectionModel} this
23978              * @param {Number} rowIndex The selected index
23979              * @param {Boolean} keepExisting False if other selections will be cleared
23980              */
23981             "beforerowselect" : true,
23982         /**
23983              * @event rowselect
23984              * Fires when a row is selected.
23985              * @param {SelectionModel} this
23986              * @param {Number} rowIndex The selected index
23987              * @param {Roo.data.Record} r The record
23988              */
23989             "rowselect" : true,
23990         /**
23991              * @event rowdeselect
23992              * Fires when a row is deselected.
23993              * @param {SelectionModel} this
23994              * @param {Number} rowIndex The selected index
23995              */
23996         "rowdeselect" : true
23997     });
23998     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23999     this.locked = false;
24000  };
24001
24002 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24003     /**
24004      * @cfg {Boolean} singleSelect
24005      * True to allow selection of only one row at a time (defaults to false)
24006      */
24007     singleSelect : false,
24008
24009     // private
24010     initEvents : function()
24011     {
24012
24013         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24014         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24015         //}else{ // allow click to work like normal
24016          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24017         //}
24018         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24019         this.grid.on("rowclick", this.handleMouseDown, this);
24020         
24021         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24022             "up" : function(e){
24023                 if(!e.shiftKey){
24024                     this.selectPrevious(e.shiftKey);
24025                 }else if(this.last !== false && this.lastActive !== false){
24026                     var last = this.last;
24027                     this.selectRange(this.last,  this.lastActive-1);
24028                     this.grid.getView().focusRow(this.lastActive);
24029                     if(last !== false){
24030                         this.last = last;
24031                     }
24032                 }else{
24033                     this.selectFirstRow();
24034                 }
24035                 this.fireEvent("afterselectionchange", this);
24036             },
24037             "down" : function(e){
24038                 if(!e.shiftKey){
24039                     this.selectNext(e.shiftKey);
24040                 }else if(this.last !== false && this.lastActive !== false){
24041                     var last = this.last;
24042                     this.selectRange(this.last,  this.lastActive+1);
24043                     this.grid.getView().focusRow(this.lastActive);
24044                     if(last !== false){
24045                         this.last = last;
24046                     }
24047                 }else{
24048                     this.selectFirstRow();
24049                 }
24050                 this.fireEvent("afterselectionchange", this);
24051             },
24052             scope: this
24053         });
24054         this.grid.store.on('load', function(){
24055             this.selections.clear();
24056         },this);
24057         /*
24058         var view = this.grid.view;
24059         view.on("refresh", this.onRefresh, this);
24060         view.on("rowupdated", this.onRowUpdated, this);
24061         view.on("rowremoved", this.onRemove, this);
24062         */
24063     },
24064
24065     // private
24066     onRefresh : function()
24067     {
24068         var ds = this.grid.store, i, v = this.grid.view;
24069         var s = this.selections;
24070         s.each(function(r){
24071             if((i = ds.indexOfId(r.id)) != -1){
24072                 v.onRowSelect(i);
24073             }else{
24074                 s.remove(r);
24075             }
24076         });
24077     },
24078
24079     // private
24080     onRemove : function(v, index, r){
24081         this.selections.remove(r);
24082     },
24083
24084     // private
24085     onRowUpdated : function(v, index, r){
24086         if(this.isSelected(r)){
24087             v.onRowSelect(index);
24088         }
24089     },
24090
24091     /**
24092      * Select records.
24093      * @param {Array} records The records to select
24094      * @param {Boolean} keepExisting (optional) True to keep existing selections
24095      */
24096     selectRecords : function(records, keepExisting)
24097     {
24098         if(!keepExisting){
24099             this.clearSelections();
24100         }
24101             var ds = this.grid.store;
24102         for(var i = 0, len = records.length; i < len; i++){
24103             this.selectRow(ds.indexOf(records[i]), true);
24104         }
24105     },
24106
24107     /**
24108      * Gets the number of selected rows.
24109      * @return {Number}
24110      */
24111     getCount : function(){
24112         return this.selections.length;
24113     },
24114
24115     /**
24116      * Selects the first row in the grid.
24117      */
24118     selectFirstRow : function(){
24119         this.selectRow(0);
24120     },
24121
24122     /**
24123      * Select the last row.
24124      * @param {Boolean} keepExisting (optional) True to keep existing selections
24125      */
24126     selectLastRow : function(keepExisting){
24127         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24128         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24129     },
24130
24131     /**
24132      * Selects the row immediately following the last selected row.
24133      * @param {Boolean} keepExisting (optional) True to keep existing selections
24134      */
24135     selectNext : function(keepExisting)
24136     {
24137             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24138             this.selectRow(this.last+1, keepExisting);
24139             this.grid.getView().focusRow(this.last);
24140         }
24141     },
24142
24143     /**
24144      * Selects the row that precedes the last selected row.
24145      * @param {Boolean} keepExisting (optional) True to keep existing selections
24146      */
24147     selectPrevious : function(keepExisting){
24148         if(this.last){
24149             this.selectRow(this.last-1, keepExisting);
24150             this.grid.getView().focusRow(this.last);
24151         }
24152     },
24153
24154     /**
24155      * Returns the selected records
24156      * @return {Array} Array of selected records
24157      */
24158     getSelections : function(){
24159         return [].concat(this.selections.items);
24160     },
24161
24162     /**
24163      * Returns the first selected record.
24164      * @return {Record}
24165      */
24166     getSelected : function(){
24167         return this.selections.itemAt(0);
24168     },
24169
24170
24171     /**
24172      * Clears all selections.
24173      */
24174     clearSelections : function(fast)
24175     {
24176         if(this.locked) {
24177             return;
24178         }
24179         if(fast !== true){
24180                 var ds = this.grid.store;
24181             var s = this.selections;
24182             s.each(function(r){
24183                 this.deselectRow(ds.indexOfId(r.id));
24184             }, this);
24185             s.clear();
24186         }else{
24187             this.selections.clear();
24188         }
24189         this.last = false;
24190     },
24191
24192
24193     /**
24194      * Selects all rows.
24195      */
24196     selectAll : function(){
24197         if(this.locked) {
24198             return;
24199         }
24200         this.selections.clear();
24201         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24202             this.selectRow(i, true);
24203         }
24204     },
24205
24206     /**
24207      * Returns True if there is a selection.
24208      * @return {Boolean}
24209      */
24210     hasSelection : function(){
24211         return this.selections.length > 0;
24212     },
24213
24214     /**
24215      * Returns True if the specified row is selected.
24216      * @param {Number/Record} record The record or index of the record to check
24217      * @return {Boolean}
24218      */
24219     isSelected : function(index){
24220             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24221         return (r && this.selections.key(r.id) ? true : false);
24222     },
24223
24224     /**
24225      * Returns True if the specified record id is selected.
24226      * @param {String} id The id of record to check
24227      * @return {Boolean}
24228      */
24229     isIdSelected : function(id){
24230         return (this.selections.key(id) ? true : false);
24231     },
24232
24233
24234     // private
24235     handleMouseDBClick : function(e, t){
24236         
24237     },
24238     // private
24239     handleMouseDown : function(e, t)
24240     {
24241             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24242         if(this.isLocked() || rowIndex < 0 ){
24243             return;
24244         };
24245         if(e.shiftKey && this.last !== false){
24246             var last = this.last;
24247             this.selectRange(last, rowIndex, e.ctrlKey);
24248             this.last = last; // reset the last
24249             t.focus();
24250     
24251         }else{
24252             var isSelected = this.isSelected(rowIndex);
24253             //Roo.log("select row:" + rowIndex);
24254             if(isSelected){
24255                 this.deselectRow(rowIndex);
24256             } else {
24257                         this.selectRow(rowIndex, true);
24258             }
24259     
24260             /*
24261                 if(e.button !== 0 && isSelected){
24262                 alert('rowIndex 2: ' + rowIndex);
24263                     view.focusRow(rowIndex);
24264                 }else if(e.ctrlKey && isSelected){
24265                     this.deselectRow(rowIndex);
24266                 }else if(!isSelected){
24267                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24268                     view.focusRow(rowIndex);
24269                 }
24270             */
24271         }
24272         this.fireEvent("afterselectionchange", this);
24273     },
24274     // private
24275     handleDragableRowClick :  function(grid, rowIndex, e) 
24276     {
24277         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24278             this.selectRow(rowIndex, false);
24279             grid.view.focusRow(rowIndex);
24280              this.fireEvent("afterselectionchange", this);
24281         }
24282     },
24283     
24284     /**
24285      * Selects multiple rows.
24286      * @param {Array} rows Array of the indexes of the row to select
24287      * @param {Boolean} keepExisting (optional) True to keep existing selections
24288      */
24289     selectRows : function(rows, keepExisting){
24290         if(!keepExisting){
24291             this.clearSelections();
24292         }
24293         for(var i = 0, len = rows.length; i < len; i++){
24294             this.selectRow(rows[i], true);
24295         }
24296     },
24297
24298     /**
24299      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24300      * @param {Number} startRow The index of the first row in the range
24301      * @param {Number} endRow The index of the last row in the range
24302      * @param {Boolean} keepExisting (optional) True to retain existing selections
24303      */
24304     selectRange : function(startRow, endRow, keepExisting){
24305         if(this.locked) {
24306             return;
24307         }
24308         if(!keepExisting){
24309             this.clearSelections();
24310         }
24311         if(startRow <= endRow){
24312             for(var i = startRow; i <= endRow; i++){
24313                 this.selectRow(i, true);
24314             }
24315         }else{
24316             for(var i = startRow; i >= endRow; i--){
24317                 this.selectRow(i, true);
24318             }
24319         }
24320     },
24321
24322     /**
24323      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24324      * @param {Number} startRow The index of the first row in the range
24325      * @param {Number} endRow The index of the last row in the range
24326      */
24327     deselectRange : function(startRow, endRow, preventViewNotify){
24328         if(this.locked) {
24329             return;
24330         }
24331         for(var i = startRow; i <= endRow; i++){
24332             this.deselectRow(i, preventViewNotify);
24333         }
24334     },
24335
24336     /**
24337      * Selects a row.
24338      * @param {Number} row The index of the row to select
24339      * @param {Boolean} keepExisting (optional) True to keep existing selections
24340      */
24341     selectRow : function(index, keepExisting, preventViewNotify)
24342     {
24343             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24344             return;
24345         }
24346         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24347             if(!keepExisting || this.singleSelect){
24348                 this.clearSelections();
24349             }
24350             
24351             var r = this.grid.store.getAt(index);
24352             //console.log('selectRow - record id :' + r.id);
24353             
24354             this.selections.add(r);
24355             this.last = this.lastActive = index;
24356             if(!preventViewNotify){
24357                 var proxy = new Roo.Element(
24358                                 this.grid.getRowDom(index)
24359                 );
24360                 proxy.addClass('bg-info info');
24361             }
24362             this.fireEvent("rowselect", this, index, r);
24363             this.fireEvent("selectionchange", this);
24364         }
24365     },
24366
24367     /**
24368      * Deselects a row.
24369      * @param {Number} row The index of the row to deselect
24370      */
24371     deselectRow : function(index, preventViewNotify)
24372     {
24373         if(this.locked) {
24374             return;
24375         }
24376         if(this.last == index){
24377             this.last = false;
24378         }
24379         if(this.lastActive == index){
24380             this.lastActive = false;
24381         }
24382         
24383         var r = this.grid.store.getAt(index);
24384         if (!r) {
24385             return;
24386         }
24387         
24388         this.selections.remove(r);
24389         //.console.log('deselectRow - record id :' + r.id);
24390         if(!preventViewNotify){
24391         
24392             var proxy = new Roo.Element(
24393                 this.grid.getRowDom(index)
24394             );
24395             proxy.removeClass('bg-info info');
24396         }
24397         this.fireEvent("rowdeselect", this, index);
24398         this.fireEvent("selectionchange", this);
24399     },
24400
24401     // private
24402     restoreLast : function(){
24403         if(this._last){
24404             this.last = this._last;
24405         }
24406     },
24407
24408     // private
24409     acceptsNav : function(row, col, cm){
24410         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24411     },
24412
24413     // private
24414     onEditorKey : function(field, e){
24415         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24416         if(k == e.TAB){
24417             e.stopEvent();
24418             ed.completeEdit();
24419             if(e.shiftKey){
24420                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24421             }else{
24422                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24423             }
24424         }else if(k == e.ENTER && !e.ctrlKey){
24425             e.stopEvent();
24426             ed.completeEdit();
24427             if(e.shiftKey){
24428                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24429             }else{
24430                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24431             }
24432         }else if(k == e.ESC){
24433             ed.cancelEdit();
24434         }
24435         if(newCell){
24436             g.startEditing(newCell[0], newCell[1]);
24437         }
24438     }
24439 });
24440 /*
24441  * Based on:
24442  * Ext JS Library 1.1.1
24443  * Copyright(c) 2006-2007, Ext JS, LLC.
24444  *
24445  * Originally Released Under LGPL - original licence link has changed is not relivant.
24446  *
24447  * Fork - LGPL
24448  * <script type="text/javascript">
24449  */
24450  
24451 /**
24452  * @class Roo.bootstrap.PagingToolbar
24453  * @extends Roo.bootstrap.NavSimplebar
24454  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24455  * @constructor
24456  * Create a new PagingToolbar
24457  * @param {Object} config The config object
24458  * @param {Roo.data.Store} store
24459  */
24460 Roo.bootstrap.PagingToolbar = function(config)
24461 {
24462     // old args format still supported... - xtype is prefered..
24463         // created from xtype...
24464     
24465     this.ds = config.dataSource;
24466     
24467     if (config.store && !this.ds) {
24468         this.store= Roo.factory(config.store, Roo.data);
24469         this.ds = this.store;
24470         this.ds.xmodule = this.xmodule || false;
24471     }
24472     
24473     this.toolbarItems = [];
24474     if (config.items) {
24475         this.toolbarItems = config.items;
24476     }
24477     
24478     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24479     
24480     this.cursor = 0;
24481     
24482     if (this.ds) { 
24483         this.bind(this.ds);
24484     }
24485     
24486     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24487     
24488 };
24489
24490 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24491     /**
24492      * @cfg {Roo.data.Store} dataSource
24493      * The underlying data store providing the paged data
24494      */
24495     /**
24496      * @cfg {String/HTMLElement/Element} container
24497      * container The id or element that will contain the toolbar
24498      */
24499     /**
24500      * @cfg {Boolean} displayInfo
24501      * True to display the displayMsg (defaults to false)
24502      */
24503     /**
24504      * @cfg {Number} pageSize
24505      * The number of records to display per page (defaults to 20)
24506      */
24507     pageSize: 20,
24508     /**
24509      * @cfg {String} displayMsg
24510      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24511      */
24512     displayMsg : 'Displaying {0} - {1} of {2}',
24513     /**
24514      * @cfg {String} emptyMsg
24515      * The message to display when no records are found (defaults to "No data to display")
24516      */
24517     emptyMsg : 'No data to display',
24518     /**
24519      * Customizable piece of the default paging text (defaults to "Page")
24520      * @type String
24521      */
24522     beforePageText : "Page",
24523     /**
24524      * Customizable piece of the default paging text (defaults to "of %0")
24525      * @type String
24526      */
24527     afterPageText : "of {0}",
24528     /**
24529      * Customizable piece of the default paging text (defaults to "First Page")
24530      * @type String
24531      */
24532     firstText : "First Page",
24533     /**
24534      * Customizable piece of the default paging text (defaults to "Previous Page")
24535      * @type String
24536      */
24537     prevText : "Previous Page",
24538     /**
24539      * Customizable piece of the default paging text (defaults to "Next Page")
24540      * @type String
24541      */
24542     nextText : "Next Page",
24543     /**
24544      * Customizable piece of the default paging text (defaults to "Last Page")
24545      * @type String
24546      */
24547     lastText : "Last Page",
24548     /**
24549      * Customizable piece of the default paging text (defaults to "Refresh")
24550      * @type String
24551      */
24552     refreshText : "Refresh",
24553
24554     buttons : false,
24555     // private
24556     onRender : function(ct, position) 
24557     {
24558         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24559         this.navgroup.parentId = this.id;
24560         this.navgroup.onRender(this.el, null);
24561         // add the buttons to the navgroup
24562         
24563         if(this.displayInfo){
24564             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24565             this.displayEl = this.el.select('.x-paging-info', true).first();
24566 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24567 //            this.displayEl = navel.el.select('span',true).first();
24568         }
24569         
24570         var _this = this;
24571         
24572         if(this.buttons){
24573             Roo.each(_this.buttons, function(e){ // this might need to use render????
24574                Roo.factory(e).render(_this.el);
24575             });
24576         }
24577             
24578         Roo.each(_this.toolbarItems, function(e) {
24579             _this.navgroup.addItem(e);
24580         });
24581         
24582         
24583         this.first = this.navgroup.addItem({
24584             tooltip: this.firstText,
24585             cls: "prev",
24586             icon : 'fa fa-backward',
24587             disabled: true,
24588             preventDefault: true,
24589             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24590         });
24591         
24592         this.prev =  this.navgroup.addItem({
24593             tooltip: this.prevText,
24594             cls: "prev",
24595             icon : 'fa fa-step-backward',
24596             disabled: true,
24597             preventDefault: true,
24598             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24599         });
24600     //this.addSeparator();
24601         
24602         
24603         var field = this.navgroup.addItem( {
24604             tagtype : 'span',
24605             cls : 'x-paging-position',
24606             
24607             html : this.beforePageText  +
24608                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24609                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24610          } ); //?? escaped?
24611         
24612         this.field = field.el.select('input', true).first();
24613         this.field.on("keydown", this.onPagingKeydown, this);
24614         this.field.on("focus", function(){this.dom.select();});
24615     
24616     
24617         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24618         //this.field.setHeight(18);
24619         //this.addSeparator();
24620         this.next = this.navgroup.addItem({
24621             tooltip: this.nextText,
24622             cls: "next",
24623             html : ' <i class="fa fa-step-forward">',
24624             disabled: true,
24625             preventDefault: true,
24626             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24627         });
24628         this.last = this.navgroup.addItem({
24629             tooltip: this.lastText,
24630             icon : 'fa fa-forward',
24631             cls: "next",
24632             disabled: true,
24633             preventDefault: true,
24634             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24635         });
24636     //this.addSeparator();
24637         this.loading = this.navgroup.addItem({
24638             tooltip: this.refreshText,
24639             icon: 'fa fa-refresh',
24640             preventDefault: true,
24641             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24642         });
24643         
24644     },
24645
24646     // private
24647     updateInfo : function(){
24648         if(this.displayEl){
24649             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24650             var msg = count == 0 ?
24651                 this.emptyMsg :
24652                 String.format(
24653                     this.displayMsg,
24654                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24655                 );
24656             this.displayEl.update(msg);
24657         }
24658     },
24659
24660     // private
24661     onLoad : function(ds, r, o)
24662     {
24663         this.cursor = o.params.start ? o.params.start : 0;
24664         
24665         var d = this.getPageData(),
24666             ap = d.activePage,
24667             ps = d.pages;
24668         
24669         
24670         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24671         this.field.dom.value = ap;
24672         this.first.setDisabled(ap == 1);
24673         this.prev.setDisabled(ap == 1);
24674         this.next.setDisabled(ap == ps);
24675         this.last.setDisabled(ap == ps);
24676         this.loading.enable();
24677         this.updateInfo();
24678     },
24679
24680     // private
24681     getPageData : function(){
24682         var total = this.ds.getTotalCount();
24683         return {
24684             total : total,
24685             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24686             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24687         };
24688     },
24689
24690     // private
24691     onLoadError : function(){
24692         this.loading.enable();
24693     },
24694
24695     // private
24696     onPagingKeydown : function(e){
24697         var k = e.getKey();
24698         var d = this.getPageData();
24699         if(k == e.RETURN){
24700             var v = this.field.dom.value, pageNum;
24701             if(!v || isNaN(pageNum = parseInt(v, 10))){
24702                 this.field.dom.value = d.activePage;
24703                 return;
24704             }
24705             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24706             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24707             e.stopEvent();
24708         }
24709         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))
24710         {
24711           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24712           this.field.dom.value = pageNum;
24713           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24714           e.stopEvent();
24715         }
24716         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24717         {
24718           var v = this.field.dom.value, pageNum; 
24719           var increment = (e.shiftKey) ? 10 : 1;
24720           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24721                 increment *= -1;
24722           }
24723           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24724             this.field.dom.value = d.activePage;
24725             return;
24726           }
24727           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24728           {
24729             this.field.dom.value = parseInt(v, 10) + increment;
24730             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24731             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24732           }
24733           e.stopEvent();
24734         }
24735     },
24736
24737     // private
24738     beforeLoad : function(){
24739         if(this.loading){
24740             this.loading.disable();
24741         }
24742     },
24743
24744     // private
24745     onClick : function(which){
24746         
24747         var ds = this.ds;
24748         if (!ds) {
24749             return;
24750         }
24751         
24752         switch(which){
24753             case "first":
24754                 ds.load({params:{start: 0, limit: this.pageSize}});
24755             break;
24756             case "prev":
24757                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24758             break;
24759             case "next":
24760                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24761             break;
24762             case "last":
24763                 var total = ds.getTotalCount();
24764                 var extra = total % this.pageSize;
24765                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24766                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24767             break;
24768             case "refresh":
24769                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24770             break;
24771         }
24772     },
24773
24774     /**
24775      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24776      * @param {Roo.data.Store} store The data store to unbind
24777      */
24778     unbind : function(ds){
24779         ds.un("beforeload", this.beforeLoad, this);
24780         ds.un("load", this.onLoad, this);
24781         ds.un("loadexception", this.onLoadError, this);
24782         ds.un("remove", this.updateInfo, this);
24783         ds.un("add", this.updateInfo, this);
24784         this.ds = undefined;
24785     },
24786
24787     /**
24788      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24789      * @param {Roo.data.Store} store The data store to bind
24790      */
24791     bind : function(ds){
24792         ds.on("beforeload", this.beforeLoad, this);
24793         ds.on("load", this.onLoad, this);
24794         ds.on("loadexception", this.onLoadError, this);
24795         ds.on("remove", this.updateInfo, this);
24796         ds.on("add", this.updateInfo, this);
24797         this.ds = ds;
24798     }
24799 });/*
24800  * - LGPL
24801  *
24802  * element
24803  * 
24804  */
24805
24806 /**
24807  * @class Roo.bootstrap.MessageBar
24808  * @extends Roo.bootstrap.Component
24809  * Bootstrap MessageBar class
24810  * @cfg {String} html contents of the MessageBar
24811  * @cfg {String} weight (info | success | warning | danger) default info
24812  * @cfg {String} beforeClass insert the bar before the given class
24813  * @cfg {Boolean} closable (true | false) default false
24814  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24815  * 
24816  * @constructor
24817  * Create a new Element
24818  * @param {Object} config The config object
24819  */
24820
24821 Roo.bootstrap.MessageBar = function(config){
24822     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24823 };
24824
24825 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24826     
24827     html: '',
24828     weight: 'info',
24829     closable: false,
24830     fixed: false,
24831     beforeClass: 'bootstrap-sticky-wrap',
24832     
24833     getAutoCreate : function(){
24834         
24835         var cfg = {
24836             tag: 'div',
24837             cls: 'alert alert-dismissable alert-' + this.weight,
24838             cn: [
24839                 {
24840                     tag: 'span',
24841                     cls: 'message',
24842                     html: this.html || ''
24843                 }
24844             ]
24845         };
24846         
24847         if(this.fixed){
24848             cfg.cls += ' alert-messages-fixed';
24849         }
24850         
24851         if(this.closable){
24852             cfg.cn.push({
24853                 tag: 'button',
24854                 cls: 'close',
24855                 html: 'x'
24856             });
24857         }
24858         
24859         return cfg;
24860     },
24861     
24862     onRender : function(ct, position)
24863     {
24864         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24865         
24866         if(!this.el){
24867             var cfg = Roo.apply({},  this.getAutoCreate());
24868             cfg.id = Roo.id();
24869             
24870             if (this.cls) {
24871                 cfg.cls += ' ' + this.cls;
24872             }
24873             if (this.style) {
24874                 cfg.style = this.style;
24875             }
24876             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24877             
24878             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24879         }
24880         
24881         this.el.select('>button.close').on('click', this.hide, this);
24882         
24883     },
24884     
24885     show : function()
24886     {
24887         if (!this.rendered) {
24888             this.render();
24889         }
24890         
24891         this.el.show();
24892         
24893         this.fireEvent('show', this);
24894         
24895     },
24896     
24897     hide : function()
24898     {
24899         if (!this.rendered) {
24900             this.render();
24901         }
24902         
24903         this.el.hide();
24904         
24905         this.fireEvent('hide', this);
24906     },
24907     
24908     update : function()
24909     {
24910 //        var e = this.el.dom.firstChild;
24911 //        
24912 //        if(this.closable){
24913 //            e = e.nextSibling;
24914 //        }
24915 //        
24916 //        e.data = this.html || '';
24917
24918         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24919     }
24920    
24921 });
24922
24923  
24924
24925      /*
24926  * - LGPL
24927  *
24928  * Graph
24929  * 
24930  */
24931
24932
24933 /**
24934  * @class Roo.bootstrap.Graph
24935  * @extends Roo.bootstrap.Component
24936  * Bootstrap Graph class
24937 > Prameters
24938  -sm {number} sm 4
24939  -md {number} md 5
24940  @cfg {String} graphtype  bar | vbar | pie
24941  @cfg {number} g_x coodinator | centre x (pie)
24942  @cfg {number} g_y coodinator | centre y (pie)
24943  @cfg {number} g_r radius (pie)
24944  @cfg {number} g_height height of the chart (respected by all elements in the set)
24945  @cfg {number} g_width width of the chart (respected by all elements in the set)
24946  @cfg {Object} title The title of the chart
24947     
24948  -{Array}  values
24949  -opts (object) options for the chart 
24950      o {
24951      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24952      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24953      o vgutter (number)
24954      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.
24955      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24956      o to
24957      o stretch (boolean)
24958      o }
24959  -opts (object) options for the pie
24960      o{
24961      o cut
24962      o startAngle (number)
24963      o endAngle (number)
24964      } 
24965  *
24966  * @constructor
24967  * Create a new Input
24968  * @param {Object} config The config object
24969  */
24970
24971 Roo.bootstrap.Graph = function(config){
24972     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24973     
24974     this.addEvents({
24975         // img events
24976         /**
24977          * @event click
24978          * The img click event for the img.
24979          * @param {Roo.EventObject} e
24980          */
24981         "click" : true
24982     });
24983 };
24984
24985 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24986     
24987     sm: 4,
24988     md: 5,
24989     graphtype: 'bar',
24990     g_height: 250,
24991     g_width: 400,
24992     g_x: 50,
24993     g_y: 50,
24994     g_r: 30,
24995     opts:{
24996         //g_colors: this.colors,
24997         g_type: 'soft',
24998         g_gutter: '20%'
24999
25000     },
25001     title : false,
25002
25003     getAutoCreate : function(){
25004         
25005         var cfg = {
25006             tag: 'div',
25007             html : null
25008         };
25009         
25010         
25011         return  cfg;
25012     },
25013
25014     onRender : function(ct,position){
25015         
25016         
25017         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25018         
25019         if (typeof(Raphael) == 'undefined') {
25020             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25021             return;
25022         }
25023         
25024         this.raphael = Raphael(this.el.dom);
25025         
25026                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25027                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25028                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25029                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25030                 /*
25031                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25032                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25033                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25034                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25035                 
25036                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25037                 r.barchart(330, 10, 300, 220, data1);
25038                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25039                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25040                 */
25041                 
25042                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25043                 // r.barchart(30, 30, 560, 250,  xdata, {
25044                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25045                 //     axis : "0 0 1 1",
25046                 //     axisxlabels :  xdata
25047                 //     //yvalues : cols,
25048                    
25049                 // });
25050 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25051 //        
25052 //        this.load(null,xdata,{
25053 //                axis : "0 0 1 1",
25054 //                axisxlabels :  xdata
25055 //                });
25056
25057     },
25058
25059     load : function(graphtype,xdata,opts)
25060     {
25061         this.raphael.clear();
25062         if(!graphtype) {
25063             graphtype = this.graphtype;
25064         }
25065         if(!opts){
25066             opts = this.opts;
25067         }
25068         var r = this.raphael,
25069             fin = function () {
25070                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25071             },
25072             fout = function () {
25073                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25074             },
25075             pfin = function() {
25076                 this.sector.stop();
25077                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25078
25079                 if (this.label) {
25080                     this.label[0].stop();
25081                     this.label[0].attr({ r: 7.5 });
25082                     this.label[1].attr({ "font-weight": 800 });
25083                 }
25084             },
25085             pfout = function() {
25086                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25087
25088                 if (this.label) {
25089                     this.label[0].animate({ r: 5 }, 500, "bounce");
25090                     this.label[1].attr({ "font-weight": 400 });
25091                 }
25092             };
25093
25094         switch(graphtype){
25095             case 'bar':
25096                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25097                 break;
25098             case 'hbar':
25099                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25100                 break;
25101             case 'pie':
25102 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25103 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25104 //            
25105                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25106                 
25107                 break;
25108
25109         }
25110         
25111         if(this.title){
25112             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25113         }
25114         
25115     },
25116     
25117     setTitle: function(o)
25118     {
25119         this.title = o;
25120     },
25121     
25122     initEvents: function() {
25123         
25124         if(!this.href){
25125             this.el.on('click', this.onClick, this);
25126         }
25127     },
25128     
25129     onClick : function(e)
25130     {
25131         Roo.log('img onclick');
25132         this.fireEvent('click', this, e);
25133     }
25134    
25135 });
25136
25137  
25138 /*
25139  * - LGPL
25140  *
25141  * numberBox
25142  * 
25143  */
25144 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25145
25146 /**
25147  * @class Roo.bootstrap.dash.NumberBox
25148  * @extends Roo.bootstrap.Component
25149  * Bootstrap NumberBox class
25150  * @cfg {String} headline Box headline
25151  * @cfg {String} content Box content
25152  * @cfg {String} icon Box icon
25153  * @cfg {String} footer Footer text
25154  * @cfg {String} fhref Footer href
25155  * 
25156  * @constructor
25157  * Create a new NumberBox
25158  * @param {Object} config The config object
25159  */
25160
25161
25162 Roo.bootstrap.dash.NumberBox = function(config){
25163     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25164     
25165 };
25166
25167 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25168     
25169     headline : '',
25170     content : '',
25171     icon : '',
25172     footer : '',
25173     fhref : '',
25174     ficon : '',
25175     
25176     getAutoCreate : function(){
25177         
25178         var cfg = {
25179             tag : 'div',
25180             cls : 'small-box ',
25181             cn : [
25182                 {
25183                     tag : 'div',
25184                     cls : 'inner',
25185                     cn :[
25186                         {
25187                             tag : 'h3',
25188                             cls : 'roo-headline',
25189                             html : this.headline
25190                         },
25191                         {
25192                             tag : 'p',
25193                             cls : 'roo-content',
25194                             html : this.content
25195                         }
25196                     ]
25197                 }
25198             ]
25199         };
25200         
25201         if(this.icon){
25202             cfg.cn.push({
25203                 tag : 'div',
25204                 cls : 'icon',
25205                 cn :[
25206                     {
25207                         tag : 'i',
25208                         cls : 'ion ' + this.icon
25209                     }
25210                 ]
25211             });
25212         }
25213         
25214         if(this.footer){
25215             var footer = {
25216                 tag : 'a',
25217                 cls : 'small-box-footer',
25218                 href : this.fhref || '#',
25219                 html : this.footer
25220             };
25221             
25222             cfg.cn.push(footer);
25223             
25224         }
25225         
25226         return  cfg;
25227     },
25228
25229     onRender : function(ct,position){
25230         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25231
25232
25233        
25234                 
25235     },
25236
25237     setHeadline: function (value)
25238     {
25239         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25240     },
25241     
25242     setFooter: function (value, href)
25243     {
25244         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25245         
25246         if(href){
25247             this.el.select('a.small-box-footer',true).first().attr('href', href);
25248         }
25249         
25250     },
25251
25252     setContent: function (value)
25253     {
25254         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25255     },
25256
25257     initEvents: function() 
25258     {   
25259         
25260     }
25261     
25262 });
25263
25264  
25265 /*
25266  * - LGPL
25267  *
25268  * TabBox
25269  * 
25270  */
25271 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25272
25273 /**
25274  * @class Roo.bootstrap.dash.TabBox
25275  * @extends Roo.bootstrap.Component
25276  * Bootstrap TabBox class
25277  * @cfg {String} title Title of the TabBox
25278  * @cfg {String} icon Icon of the TabBox
25279  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25280  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25281  * 
25282  * @constructor
25283  * Create a new TabBox
25284  * @param {Object} config The config object
25285  */
25286
25287
25288 Roo.bootstrap.dash.TabBox = function(config){
25289     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25290     this.addEvents({
25291         // raw events
25292         /**
25293          * @event addpane
25294          * When a pane is added
25295          * @param {Roo.bootstrap.dash.TabPane} pane
25296          */
25297         "addpane" : true,
25298         /**
25299          * @event activatepane
25300          * When a pane is activated
25301          * @param {Roo.bootstrap.dash.TabPane} pane
25302          */
25303         "activatepane" : true
25304         
25305          
25306     });
25307     
25308     this.panes = [];
25309 };
25310
25311 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25312
25313     title : '',
25314     icon : false,
25315     showtabs : true,
25316     tabScrollable : false,
25317     
25318     getChildContainer : function()
25319     {
25320         return this.el.select('.tab-content', true).first();
25321     },
25322     
25323     getAutoCreate : function(){
25324         
25325         var header = {
25326             tag: 'li',
25327             cls: 'pull-left header',
25328             html: this.title,
25329             cn : []
25330         };
25331         
25332         if(this.icon){
25333             header.cn.push({
25334                 tag: 'i',
25335                 cls: 'fa ' + this.icon
25336             });
25337         }
25338         
25339         var h = {
25340             tag: 'ul',
25341             cls: 'nav nav-tabs pull-right',
25342             cn: [
25343                 header
25344             ]
25345         };
25346         
25347         if(this.tabScrollable){
25348             h = {
25349                 tag: 'div',
25350                 cls: 'tab-header',
25351                 cn: [
25352                     {
25353                         tag: 'ul',
25354                         cls: 'nav nav-tabs pull-right',
25355                         cn: [
25356                             header
25357                         ]
25358                     }
25359                 ]
25360             };
25361         }
25362         
25363         var cfg = {
25364             tag: 'div',
25365             cls: 'nav-tabs-custom',
25366             cn: [
25367                 h,
25368                 {
25369                     tag: 'div',
25370                     cls: 'tab-content no-padding',
25371                     cn: []
25372                 }
25373             ]
25374         };
25375
25376         return  cfg;
25377     },
25378     initEvents : function()
25379     {
25380         //Roo.log('add add pane handler');
25381         this.on('addpane', this.onAddPane, this);
25382     },
25383      /**
25384      * Updates the box title
25385      * @param {String} html to set the title to.
25386      */
25387     setTitle : function(value)
25388     {
25389         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25390     },
25391     onAddPane : function(pane)
25392     {
25393         this.panes.push(pane);
25394         //Roo.log('addpane');
25395         //Roo.log(pane);
25396         // tabs are rendere left to right..
25397         if(!this.showtabs){
25398             return;
25399         }
25400         
25401         var ctr = this.el.select('.nav-tabs', true).first();
25402          
25403          
25404         var existing = ctr.select('.nav-tab',true);
25405         var qty = existing.getCount();;
25406         
25407         
25408         var tab = ctr.createChild({
25409             tag : 'li',
25410             cls : 'nav-tab' + (qty ? '' : ' active'),
25411             cn : [
25412                 {
25413                     tag : 'a',
25414                     href:'#',
25415                     html : pane.title
25416                 }
25417             ]
25418         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25419         pane.tab = tab;
25420         
25421         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25422         if (!qty) {
25423             pane.el.addClass('active');
25424         }
25425         
25426                 
25427     },
25428     onTabClick : function(ev,un,ob,pane)
25429     {
25430         //Roo.log('tab - prev default');
25431         ev.preventDefault();
25432         
25433         
25434         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25435         pane.tab.addClass('active');
25436         //Roo.log(pane.title);
25437         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25438         // technically we should have a deactivate event.. but maybe add later.
25439         // and it should not de-activate the selected tab...
25440         this.fireEvent('activatepane', pane);
25441         pane.el.addClass('active');
25442         pane.fireEvent('activate');
25443         
25444         
25445     },
25446     
25447     getActivePane : function()
25448     {
25449         var r = false;
25450         Roo.each(this.panes, function(p) {
25451             if(p.el.hasClass('active')){
25452                 r = p;
25453                 return false;
25454             }
25455             
25456             return;
25457         });
25458         
25459         return r;
25460     }
25461     
25462     
25463 });
25464
25465  
25466 /*
25467  * - LGPL
25468  *
25469  * Tab pane
25470  * 
25471  */
25472 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25473 /**
25474  * @class Roo.bootstrap.TabPane
25475  * @extends Roo.bootstrap.Component
25476  * Bootstrap TabPane class
25477  * @cfg {Boolean} active (false | true) Default false
25478  * @cfg {String} title title of panel
25479
25480  * 
25481  * @constructor
25482  * Create a new TabPane
25483  * @param {Object} config The config object
25484  */
25485
25486 Roo.bootstrap.dash.TabPane = function(config){
25487     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25488     
25489     this.addEvents({
25490         // raw events
25491         /**
25492          * @event activate
25493          * When a pane is activated
25494          * @param {Roo.bootstrap.dash.TabPane} pane
25495          */
25496         "activate" : true
25497          
25498     });
25499 };
25500
25501 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25502     
25503     active : false,
25504     title : '',
25505     
25506     // the tabBox that this is attached to.
25507     tab : false,
25508      
25509     getAutoCreate : function() 
25510     {
25511         var cfg = {
25512             tag: 'div',
25513             cls: 'tab-pane'
25514         };
25515         
25516         if(this.active){
25517             cfg.cls += ' active';
25518         }
25519         
25520         return cfg;
25521     },
25522     initEvents  : function()
25523     {
25524         //Roo.log('trigger add pane handler');
25525         this.parent().fireEvent('addpane', this)
25526     },
25527     
25528      /**
25529      * Updates the tab title 
25530      * @param {String} html to set the title to.
25531      */
25532     setTitle: function(str)
25533     {
25534         if (!this.tab) {
25535             return;
25536         }
25537         this.title = str;
25538         this.tab.select('a', true).first().dom.innerHTML = str;
25539         
25540     }
25541     
25542     
25543     
25544 });
25545
25546  
25547
25548
25549  /*
25550  * - LGPL
25551  *
25552  * menu
25553  * 
25554  */
25555 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25556
25557 /**
25558  * @class Roo.bootstrap.menu.Menu
25559  * @extends Roo.bootstrap.Component
25560  * Bootstrap Menu class - container for Menu
25561  * @cfg {String} html Text of the menu
25562  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25563  * @cfg {String} icon Font awesome icon
25564  * @cfg {String} pos Menu align to (top | bottom) default bottom
25565  * 
25566  * 
25567  * @constructor
25568  * Create a new Menu
25569  * @param {Object} config The config object
25570  */
25571
25572
25573 Roo.bootstrap.menu.Menu = function(config){
25574     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25575     
25576     this.addEvents({
25577         /**
25578          * @event beforeshow
25579          * Fires before this menu is displayed
25580          * @param {Roo.bootstrap.menu.Menu} this
25581          */
25582         beforeshow : true,
25583         /**
25584          * @event beforehide
25585          * Fires before this menu is hidden
25586          * @param {Roo.bootstrap.menu.Menu} this
25587          */
25588         beforehide : true,
25589         /**
25590          * @event show
25591          * Fires after this menu is displayed
25592          * @param {Roo.bootstrap.menu.Menu} this
25593          */
25594         show : true,
25595         /**
25596          * @event hide
25597          * Fires after this menu is hidden
25598          * @param {Roo.bootstrap.menu.Menu} this
25599          */
25600         hide : true,
25601         /**
25602          * @event click
25603          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25604          * @param {Roo.bootstrap.menu.Menu} this
25605          * @param {Roo.EventObject} e
25606          */
25607         click : true
25608     });
25609     
25610 };
25611
25612 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25613     
25614     submenu : false,
25615     html : '',
25616     weight : 'default',
25617     icon : false,
25618     pos : 'bottom',
25619     
25620     
25621     getChildContainer : function() {
25622         if(this.isSubMenu){
25623             return this.el;
25624         }
25625         
25626         return this.el.select('ul.dropdown-menu', true).first();  
25627     },
25628     
25629     getAutoCreate : function()
25630     {
25631         var text = [
25632             {
25633                 tag : 'span',
25634                 cls : 'roo-menu-text',
25635                 html : this.html
25636             }
25637         ];
25638         
25639         if(this.icon){
25640             text.unshift({
25641                 tag : 'i',
25642                 cls : 'fa ' + this.icon
25643             })
25644         }
25645         
25646         
25647         var cfg = {
25648             tag : 'div',
25649             cls : 'btn-group',
25650             cn : [
25651                 {
25652                     tag : 'button',
25653                     cls : 'dropdown-button btn btn-' + this.weight,
25654                     cn : text
25655                 },
25656                 {
25657                     tag : 'button',
25658                     cls : 'dropdown-toggle btn btn-' + this.weight,
25659                     cn : [
25660                         {
25661                             tag : 'span',
25662                             cls : 'caret'
25663                         }
25664                     ]
25665                 },
25666                 {
25667                     tag : 'ul',
25668                     cls : 'dropdown-menu'
25669                 }
25670             ]
25671             
25672         };
25673         
25674         if(this.pos == 'top'){
25675             cfg.cls += ' dropup';
25676         }
25677         
25678         if(this.isSubMenu){
25679             cfg = {
25680                 tag : 'ul',
25681                 cls : 'dropdown-menu'
25682             }
25683         }
25684         
25685         return cfg;
25686     },
25687     
25688     onRender : function(ct, position)
25689     {
25690         this.isSubMenu = ct.hasClass('dropdown-submenu');
25691         
25692         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25693     },
25694     
25695     initEvents : function() 
25696     {
25697         if(this.isSubMenu){
25698             return;
25699         }
25700         
25701         this.hidden = true;
25702         
25703         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25704         this.triggerEl.on('click', this.onTriggerPress, this);
25705         
25706         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25707         this.buttonEl.on('click', this.onClick, this);
25708         
25709     },
25710     
25711     list : function()
25712     {
25713         if(this.isSubMenu){
25714             return this.el;
25715         }
25716         
25717         return this.el.select('ul.dropdown-menu', true).first();
25718     },
25719     
25720     onClick : function(e)
25721     {
25722         this.fireEvent("click", this, e);
25723     },
25724     
25725     onTriggerPress  : function(e)
25726     {   
25727         if (this.isVisible()) {
25728             this.hide();
25729         } else {
25730             this.show();
25731         }
25732     },
25733     
25734     isVisible : function(){
25735         return !this.hidden;
25736     },
25737     
25738     show : function()
25739     {
25740         this.fireEvent("beforeshow", this);
25741         
25742         this.hidden = false;
25743         this.el.addClass('open');
25744         
25745         Roo.get(document).on("mouseup", this.onMouseUp, this);
25746         
25747         this.fireEvent("show", this);
25748         
25749         
25750     },
25751     
25752     hide : function()
25753     {
25754         this.fireEvent("beforehide", this);
25755         
25756         this.hidden = true;
25757         this.el.removeClass('open');
25758         
25759         Roo.get(document).un("mouseup", this.onMouseUp);
25760         
25761         this.fireEvent("hide", this);
25762     },
25763     
25764     onMouseUp : function()
25765     {
25766         this.hide();
25767     }
25768     
25769 });
25770
25771  
25772  /*
25773  * - LGPL
25774  *
25775  * menu item
25776  * 
25777  */
25778 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25779
25780 /**
25781  * @class Roo.bootstrap.menu.Item
25782  * @extends Roo.bootstrap.Component
25783  * Bootstrap MenuItem class
25784  * @cfg {Boolean} submenu (true | false) default false
25785  * @cfg {String} html text of the item
25786  * @cfg {String} href the link
25787  * @cfg {Boolean} disable (true | false) default false
25788  * @cfg {Boolean} preventDefault (true | false) default true
25789  * @cfg {String} icon Font awesome icon
25790  * @cfg {String} pos Submenu align to (left | right) default right 
25791  * 
25792  * 
25793  * @constructor
25794  * Create a new Item
25795  * @param {Object} config The config object
25796  */
25797
25798
25799 Roo.bootstrap.menu.Item = function(config){
25800     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25801     this.addEvents({
25802         /**
25803          * @event mouseover
25804          * Fires when the mouse is hovering over this menu
25805          * @param {Roo.bootstrap.menu.Item} this
25806          * @param {Roo.EventObject} e
25807          */
25808         mouseover : true,
25809         /**
25810          * @event mouseout
25811          * Fires when the mouse exits this menu
25812          * @param {Roo.bootstrap.menu.Item} this
25813          * @param {Roo.EventObject} e
25814          */
25815         mouseout : true,
25816         // raw events
25817         /**
25818          * @event click
25819          * The raw click event for the entire grid.
25820          * @param {Roo.EventObject} e
25821          */
25822         click : true
25823     });
25824 };
25825
25826 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25827     
25828     submenu : false,
25829     href : '',
25830     html : '',
25831     preventDefault: true,
25832     disable : false,
25833     icon : false,
25834     pos : 'right',
25835     
25836     getAutoCreate : function()
25837     {
25838         var text = [
25839             {
25840                 tag : 'span',
25841                 cls : 'roo-menu-item-text',
25842                 html : this.html
25843             }
25844         ];
25845         
25846         if(this.icon){
25847             text.unshift({
25848                 tag : 'i',
25849                 cls : 'fa ' + this.icon
25850             })
25851         }
25852         
25853         var cfg = {
25854             tag : 'li',
25855             cn : [
25856                 {
25857                     tag : 'a',
25858                     href : this.href || '#',
25859                     cn : text
25860                 }
25861             ]
25862         };
25863         
25864         if(this.disable){
25865             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25866         }
25867         
25868         if(this.submenu){
25869             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25870             
25871             if(this.pos == 'left'){
25872                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25873             }
25874         }
25875         
25876         return cfg;
25877     },
25878     
25879     initEvents : function() 
25880     {
25881         this.el.on('mouseover', this.onMouseOver, this);
25882         this.el.on('mouseout', this.onMouseOut, this);
25883         
25884         this.el.select('a', true).first().on('click', this.onClick, this);
25885         
25886     },
25887     
25888     onClick : function(e)
25889     {
25890         if(this.preventDefault){
25891             e.preventDefault();
25892         }
25893         
25894         this.fireEvent("click", this, e);
25895     },
25896     
25897     onMouseOver : function(e)
25898     {
25899         if(this.submenu && this.pos == 'left'){
25900             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25901         }
25902         
25903         this.fireEvent("mouseover", this, e);
25904     },
25905     
25906     onMouseOut : function(e)
25907     {
25908         this.fireEvent("mouseout", this, e);
25909     }
25910 });
25911
25912  
25913
25914  /*
25915  * - LGPL
25916  *
25917  * menu separator
25918  * 
25919  */
25920 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25921
25922 /**
25923  * @class Roo.bootstrap.menu.Separator
25924  * @extends Roo.bootstrap.Component
25925  * Bootstrap Separator class
25926  * 
25927  * @constructor
25928  * Create a new Separator
25929  * @param {Object} config The config object
25930  */
25931
25932
25933 Roo.bootstrap.menu.Separator = function(config){
25934     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25935 };
25936
25937 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25938     
25939     getAutoCreate : function(){
25940         var cfg = {
25941             tag : 'li',
25942             cls: 'divider'
25943         };
25944         
25945         return cfg;
25946     }
25947    
25948 });
25949
25950  
25951
25952  /*
25953  * - LGPL
25954  *
25955  * Tooltip
25956  * 
25957  */
25958
25959 /**
25960  * @class Roo.bootstrap.Tooltip
25961  * Bootstrap Tooltip class
25962  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25963  * to determine which dom element triggers the tooltip.
25964  * 
25965  * It needs to add support for additional attributes like tooltip-position
25966  * 
25967  * @constructor
25968  * Create a new Toolti
25969  * @param {Object} config The config object
25970  */
25971
25972 Roo.bootstrap.Tooltip = function(config){
25973     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25974     
25975     this.alignment = Roo.bootstrap.Tooltip.alignment;
25976     
25977     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25978         this.alignment = config.alignment;
25979     }
25980     
25981 };
25982
25983 Roo.apply(Roo.bootstrap.Tooltip, {
25984     /**
25985      * @function init initialize tooltip monitoring.
25986      * @static
25987      */
25988     currentEl : false,
25989     currentTip : false,
25990     currentRegion : false,
25991     
25992     //  init : delay?
25993     
25994     init : function()
25995     {
25996         Roo.get(document).on('mouseover', this.enter ,this);
25997         Roo.get(document).on('mouseout', this.leave, this);
25998          
25999         
26000         this.currentTip = new Roo.bootstrap.Tooltip();
26001     },
26002     
26003     enter : function(ev)
26004     {
26005         var dom = ev.getTarget();
26006         
26007         //Roo.log(['enter',dom]);
26008         var el = Roo.fly(dom);
26009         if (this.currentEl) {
26010             //Roo.log(dom);
26011             //Roo.log(this.currentEl);
26012             //Roo.log(this.currentEl.contains(dom));
26013             if (this.currentEl == el) {
26014                 return;
26015             }
26016             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26017                 return;
26018             }
26019
26020         }
26021         
26022         if (this.currentTip.el) {
26023             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26024         }    
26025         //Roo.log(ev);
26026         
26027         if(!el || el.dom == document){
26028             return;
26029         }
26030         
26031         var bindEl = el;
26032         
26033         // you can not look for children, as if el is the body.. then everythign is the child..
26034         if (!el.attr('tooltip')) { //
26035             if (!el.select("[tooltip]").elements.length) {
26036                 return;
26037             }
26038             // is the mouse over this child...?
26039             bindEl = el.select("[tooltip]").first();
26040             var xy = ev.getXY();
26041             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26042                 //Roo.log("not in region.");
26043                 return;
26044             }
26045             //Roo.log("child element over..");
26046             
26047         }
26048         this.currentEl = bindEl;
26049         this.currentTip.bind(bindEl);
26050         this.currentRegion = Roo.lib.Region.getRegion(dom);
26051         this.currentTip.enter();
26052         
26053     },
26054     leave : function(ev)
26055     {
26056         var dom = ev.getTarget();
26057         //Roo.log(['leave',dom]);
26058         if (!this.currentEl) {
26059             return;
26060         }
26061         
26062         
26063         if (dom != this.currentEl.dom) {
26064             return;
26065         }
26066         var xy = ev.getXY();
26067         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26068             return;
26069         }
26070         // only activate leave if mouse cursor is outside... bounding box..
26071         
26072         
26073         
26074         
26075         if (this.currentTip) {
26076             this.currentTip.leave();
26077         }
26078         //Roo.log('clear currentEl');
26079         this.currentEl = false;
26080         
26081         
26082     },
26083     alignment : {
26084         'left' : ['r-l', [-2,0], 'right'],
26085         'right' : ['l-r', [2,0], 'left'],
26086         'bottom' : ['t-b', [0,2], 'top'],
26087         'top' : [ 'b-t', [0,-2], 'bottom']
26088     }
26089     
26090 });
26091
26092
26093 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26094     
26095     
26096     bindEl : false,
26097     
26098     delay : null, // can be { show : 300 , hide: 500}
26099     
26100     timeout : null,
26101     
26102     hoverState : null, //???
26103     
26104     placement : 'bottom', 
26105     
26106     alignment : false,
26107     
26108     getAutoCreate : function(){
26109     
26110         var cfg = {
26111            cls : 'tooltip',
26112            role : 'tooltip',
26113            cn : [
26114                 {
26115                     cls : 'tooltip-arrow'
26116                 },
26117                 {
26118                     cls : 'tooltip-inner'
26119                 }
26120            ]
26121         };
26122         
26123         return cfg;
26124     },
26125     bind : function(el)
26126     {
26127         this.bindEl = el;
26128     },
26129       
26130     
26131     enter : function () {
26132        
26133         if (this.timeout != null) {
26134             clearTimeout(this.timeout);
26135         }
26136         
26137         this.hoverState = 'in';
26138          //Roo.log("enter - show");
26139         if (!this.delay || !this.delay.show) {
26140             this.show();
26141             return;
26142         }
26143         var _t = this;
26144         this.timeout = setTimeout(function () {
26145             if (_t.hoverState == 'in') {
26146                 _t.show();
26147             }
26148         }, this.delay.show);
26149     },
26150     leave : function()
26151     {
26152         clearTimeout(this.timeout);
26153     
26154         this.hoverState = 'out';
26155          if (!this.delay || !this.delay.hide) {
26156             this.hide();
26157             return;
26158         }
26159        
26160         var _t = this;
26161         this.timeout = setTimeout(function () {
26162             //Roo.log("leave - timeout");
26163             
26164             if (_t.hoverState == 'out') {
26165                 _t.hide();
26166                 Roo.bootstrap.Tooltip.currentEl = false;
26167             }
26168         }, delay);
26169     },
26170     
26171     show : function (msg)
26172     {
26173         if (!this.el) {
26174             this.render(document.body);
26175         }
26176         // set content.
26177         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26178         
26179         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26180         
26181         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26182         
26183         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26184         
26185         var placement = typeof this.placement == 'function' ?
26186             this.placement.call(this, this.el, on_el) :
26187             this.placement;
26188             
26189         var autoToken = /\s?auto?\s?/i;
26190         var autoPlace = autoToken.test(placement);
26191         if (autoPlace) {
26192             placement = placement.replace(autoToken, '') || 'top';
26193         }
26194         
26195         //this.el.detach()
26196         //this.el.setXY([0,0]);
26197         this.el.show();
26198         //this.el.dom.style.display='block';
26199         
26200         //this.el.appendTo(on_el);
26201         
26202         var p = this.getPosition();
26203         var box = this.el.getBox();
26204         
26205         if (autoPlace) {
26206             // fixme..
26207         }
26208         
26209         var align = this.alignment[placement];
26210         
26211         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26212         
26213         if(placement == 'top' || placement == 'bottom'){
26214             if(xy[0] < 0){
26215                 placement = 'right';
26216             }
26217             
26218             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26219                 placement = 'left';
26220             }
26221             
26222             var scroll = Roo.select('body', true).first().getScroll();
26223             
26224             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26225                 placement = 'top';
26226             }
26227             
26228             align = this.alignment[placement];
26229         }
26230         
26231         this.el.alignTo(this.bindEl, align[0],align[1]);
26232         //var arrow = this.el.select('.arrow',true).first();
26233         //arrow.set(align[2], 
26234         
26235         this.el.addClass(placement);
26236         
26237         this.el.addClass('in fade');
26238         
26239         this.hoverState = null;
26240         
26241         if (this.el.hasClass('fade')) {
26242             // fade it?
26243         }
26244         
26245     },
26246     hide : function()
26247     {
26248          
26249         if (!this.el) {
26250             return;
26251         }
26252         //this.el.setXY([0,0]);
26253         this.el.removeClass('in');
26254         //this.el.hide();
26255         
26256     }
26257     
26258 });
26259  
26260
26261  /*
26262  * - LGPL
26263  *
26264  * Location Picker
26265  * 
26266  */
26267
26268 /**
26269  * @class Roo.bootstrap.LocationPicker
26270  * @extends Roo.bootstrap.Component
26271  * Bootstrap LocationPicker class
26272  * @cfg {Number} latitude Position when init default 0
26273  * @cfg {Number} longitude Position when init default 0
26274  * @cfg {Number} zoom default 15
26275  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26276  * @cfg {Boolean} mapTypeControl default false
26277  * @cfg {Boolean} disableDoubleClickZoom default false
26278  * @cfg {Boolean} scrollwheel default true
26279  * @cfg {Boolean} streetViewControl default false
26280  * @cfg {Number} radius default 0
26281  * @cfg {String} locationName
26282  * @cfg {Boolean} draggable default true
26283  * @cfg {Boolean} enableAutocomplete default false
26284  * @cfg {Boolean} enableReverseGeocode default true
26285  * @cfg {String} markerTitle
26286  * 
26287  * @constructor
26288  * Create a new LocationPicker
26289  * @param {Object} config The config object
26290  */
26291
26292
26293 Roo.bootstrap.LocationPicker = function(config){
26294     
26295     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26296     
26297     this.addEvents({
26298         /**
26299          * @event initial
26300          * Fires when the picker initialized.
26301          * @param {Roo.bootstrap.LocationPicker} this
26302          * @param {Google Location} location
26303          */
26304         initial : true,
26305         /**
26306          * @event positionchanged
26307          * Fires when the picker position changed.
26308          * @param {Roo.bootstrap.LocationPicker} this
26309          * @param {Google Location} location
26310          */
26311         positionchanged : true,
26312         /**
26313          * @event resize
26314          * Fires when the map resize.
26315          * @param {Roo.bootstrap.LocationPicker} this
26316          */
26317         resize : true,
26318         /**
26319          * @event show
26320          * Fires when the map show.
26321          * @param {Roo.bootstrap.LocationPicker} this
26322          */
26323         show : true,
26324         /**
26325          * @event hide
26326          * Fires when the map hide.
26327          * @param {Roo.bootstrap.LocationPicker} this
26328          */
26329         hide : true,
26330         /**
26331          * @event mapClick
26332          * Fires when click the map.
26333          * @param {Roo.bootstrap.LocationPicker} this
26334          * @param {Map event} e
26335          */
26336         mapClick : true,
26337         /**
26338          * @event mapRightClick
26339          * Fires when right click the map.
26340          * @param {Roo.bootstrap.LocationPicker} this
26341          * @param {Map event} e
26342          */
26343         mapRightClick : true,
26344         /**
26345          * @event markerClick
26346          * Fires when click the marker.
26347          * @param {Roo.bootstrap.LocationPicker} this
26348          * @param {Map event} e
26349          */
26350         markerClick : true,
26351         /**
26352          * @event markerRightClick
26353          * Fires when right click the marker.
26354          * @param {Roo.bootstrap.LocationPicker} this
26355          * @param {Map event} e
26356          */
26357         markerRightClick : true,
26358         /**
26359          * @event OverlayViewDraw
26360          * Fires when OverlayView Draw
26361          * @param {Roo.bootstrap.LocationPicker} this
26362          */
26363         OverlayViewDraw : true,
26364         /**
26365          * @event OverlayViewOnAdd
26366          * Fires when OverlayView Draw
26367          * @param {Roo.bootstrap.LocationPicker} this
26368          */
26369         OverlayViewOnAdd : true,
26370         /**
26371          * @event OverlayViewOnRemove
26372          * Fires when OverlayView Draw
26373          * @param {Roo.bootstrap.LocationPicker} this
26374          */
26375         OverlayViewOnRemove : true,
26376         /**
26377          * @event OverlayViewShow
26378          * Fires when OverlayView Draw
26379          * @param {Roo.bootstrap.LocationPicker} this
26380          * @param {Pixel} cpx
26381          */
26382         OverlayViewShow : true,
26383         /**
26384          * @event OverlayViewHide
26385          * Fires when OverlayView Draw
26386          * @param {Roo.bootstrap.LocationPicker} this
26387          */
26388         OverlayViewHide : true,
26389         /**
26390          * @event loadexception
26391          * Fires when load google lib failed.
26392          * @param {Roo.bootstrap.LocationPicker} this
26393          */
26394         loadexception : true
26395     });
26396         
26397 };
26398
26399 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26400     
26401     gMapContext: false,
26402     
26403     latitude: 0,
26404     longitude: 0,
26405     zoom: 15,
26406     mapTypeId: false,
26407     mapTypeControl: false,
26408     disableDoubleClickZoom: false,
26409     scrollwheel: true,
26410     streetViewControl: false,
26411     radius: 0,
26412     locationName: '',
26413     draggable: true,
26414     enableAutocomplete: false,
26415     enableReverseGeocode: true,
26416     markerTitle: '',
26417     
26418     getAutoCreate: function()
26419     {
26420
26421         var cfg = {
26422             tag: 'div',
26423             cls: 'roo-location-picker'
26424         };
26425         
26426         return cfg
26427     },
26428     
26429     initEvents: function(ct, position)
26430     {       
26431         if(!this.el.getWidth() || this.isApplied()){
26432             return;
26433         }
26434         
26435         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26436         
26437         this.initial();
26438     },
26439     
26440     initial: function()
26441     {
26442         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26443             this.fireEvent('loadexception', this);
26444             return;
26445         }
26446         
26447         if(!this.mapTypeId){
26448             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26449         }
26450         
26451         this.gMapContext = this.GMapContext();
26452         
26453         this.initOverlayView();
26454         
26455         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26456         
26457         var _this = this;
26458                 
26459         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26460             _this.setPosition(_this.gMapContext.marker.position);
26461         });
26462         
26463         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26464             _this.fireEvent('mapClick', this, event);
26465             
26466         });
26467
26468         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26469             _this.fireEvent('mapRightClick', this, event);
26470             
26471         });
26472         
26473         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26474             _this.fireEvent('markerClick', this, event);
26475             
26476         });
26477
26478         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26479             _this.fireEvent('markerRightClick', this, event);
26480             
26481         });
26482         
26483         this.setPosition(this.gMapContext.location);
26484         
26485         this.fireEvent('initial', this, this.gMapContext.location);
26486     },
26487     
26488     initOverlayView: function()
26489     {
26490         var _this = this;
26491         
26492         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26493             
26494             draw: function()
26495             {
26496                 _this.fireEvent('OverlayViewDraw', _this);
26497             },
26498             
26499             onAdd: function()
26500             {
26501                 _this.fireEvent('OverlayViewOnAdd', _this);
26502             },
26503             
26504             onRemove: function()
26505             {
26506                 _this.fireEvent('OverlayViewOnRemove', _this);
26507             },
26508             
26509             show: function(cpx)
26510             {
26511                 _this.fireEvent('OverlayViewShow', _this, cpx);
26512             },
26513             
26514             hide: function()
26515             {
26516                 _this.fireEvent('OverlayViewHide', _this);
26517             }
26518             
26519         });
26520     },
26521     
26522     fromLatLngToContainerPixel: function(event)
26523     {
26524         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26525     },
26526     
26527     isApplied: function() 
26528     {
26529         return this.getGmapContext() == false ? false : true;
26530     },
26531     
26532     getGmapContext: function() 
26533     {
26534         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26535     },
26536     
26537     GMapContext: function() 
26538     {
26539         var position = new google.maps.LatLng(this.latitude, this.longitude);
26540         
26541         var _map = new google.maps.Map(this.el.dom, {
26542             center: position,
26543             zoom: this.zoom,
26544             mapTypeId: this.mapTypeId,
26545             mapTypeControl: this.mapTypeControl,
26546             disableDoubleClickZoom: this.disableDoubleClickZoom,
26547             scrollwheel: this.scrollwheel,
26548             streetViewControl: this.streetViewControl,
26549             locationName: this.locationName,
26550             draggable: this.draggable,
26551             enableAutocomplete: this.enableAutocomplete,
26552             enableReverseGeocode: this.enableReverseGeocode
26553         });
26554         
26555         var _marker = new google.maps.Marker({
26556             position: position,
26557             map: _map,
26558             title: this.markerTitle,
26559             draggable: this.draggable
26560         });
26561         
26562         return {
26563             map: _map,
26564             marker: _marker,
26565             circle: null,
26566             location: position,
26567             radius: this.radius,
26568             locationName: this.locationName,
26569             addressComponents: {
26570                 formatted_address: null,
26571                 addressLine1: null,
26572                 addressLine2: null,
26573                 streetName: null,
26574                 streetNumber: null,
26575                 city: null,
26576                 district: null,
26577                 state: null,
26578                 stateOrProvince: null
26579             },
26580             settings: this,
26581             domContainer: this.el.dom,
26582             geodecoder: new google.maps.Geocoder()
26583         };
26584     },
26585     
26586     drawCircle: function(center, radius, options) 
26587     {
26588         if (this.gMapContext.circle != null) {
26589             this.gMapContext.circle.setMap(null);
26590         }
26591         if (radius > 0) {
26592             radius *= 1;
26593             options = Roo.apply({}, options, {
26594                 strokeColor: "#0000FF",
26595                 strokeOpacity: .35,
26596                 strokeWeight: 2,
26597                 fillColor: "#0000FF",
26598                 fillOpacity: .2
26599             });
26600             
26601             options.map = this.gMapContext.map;
26602             options.radius = radius;
26603             options.center = center;
26604             this.gMapContext.circle = new google.maps.Circle(options);
26605             return this.gMapContext.circle;
26606         }
26607         
26608         return null;
26609     },
26610     
26611     setPosition: function(location) 
26612     {
26613         this.gMapContext.location = location;
26614         this.gMapContext.marker.setPosition(location);
26615         this.gMapContext.map.panTo(location);
26616         this.drawCircle(location, this.gMapContext.radius, {});
26617         
26618         var _this = this;
26619         
26620         if (this.gMapContext.settings.enableReverseGeocode) {
26621             this.gMapContext.geodecoder.geocode({
26622                 latLng: this.gMapContext.location
26623             }, function(results, status) {
26624                 
26625                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26626                     _this.gMapContext.locationName = results[0].formatted_address;
26627                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26628                     
26629                     _this.fireEvent('positionchanged', this, location);
26630                 }
26631             });
26632             
26633             return;
26634         }
26635         
26636         this.fireEvent('positionchanged', this, location);
26637     },
26638     
26639     resize: function()
26640     {
26641         google.maps.event.trigger(this.gMapContext.map, "resize");
26642         
26643         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26644         
26645         this.fireEvent('resize', this);
26646     },
26647     
26648     setPositionByLatLng: function(latitude, longitude)
26649     {
26650         this.setPosition(new google.maps.LatLng(latitude, longitude));
26651     },
26652     
26653     getCurrentPosition: function() 
26654     {
26655         return {
26656             latitude: this.gMapContext.location.lat(),
26657             longitude: this.gMapContext.location.lng()
26658         };
26659     },
26660     
26661     getAddressName: function() 
26662     {
26663         return this.gMapContext.locationName;
26664     },
26665     
26666     getAddressComponents: function() 
26667     {
26668         return this.gMapContext.addressComponents;
26669     },
26670     
26671     address_component_from_google_geocode: function(address_components) 
26672     {
26673         var result = {};
26674         
26675         for (var i = 0; i < address_components.length; i++) {
26676             var component = address_components[i];
26677             if (component.types.indexOf("postal_code") >= 0) {
26678                 result.postalCode = component.short_name;
26679             } else if (component.types.indexOf("street_number") >= 0) {
26680                 result.streetNumber = component.short_name;
26681             } else if (component.types.indexOf("route") >= 0) {
26682                 result.streetName = component.short_name;
26683             } else if (component.types.indexOf("neighborhood") >= 0) {
26684                 result.city = component.short_name;
26685             } else if (component.types.indexOf("locality") >= 0) {
26686                 result.city = component.short_name;
26687             } else if (component.types.indexOf("sublocality") >= 0) {
26688                 result.district = component.short_name;
26689             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26690                 result.stateOrProvince = component.short_name;
26691             } else if (component.types.indexOf("country") >= 0) {
26692                 result.country = component.short_name;
26693             }
26694         }
26695         
26696         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26697         result.addressLine2 = "";
26698         return result;
26699     },
26700     
26701     setZoomLevel: function(zoom)
26702     {
26703         this.gMapContext.map.setZoom(zoom);
26704     },
26705     
26706     show: function()
26707     {
26708         if(!this.el){
26709             return;
26710         }
26711         
26712         this.el.show();
26713         
26714         this.resize();
26715         
26716         this.fireEvent('show', this);
26717     },
26718     
26719     hide: function()
26720     {
26721         if(!this.el){
26722             return;
26723         }
26724         
26725         this.el.hide();
26726         
26727         this.fireEvent('hide', this);
26728     }
26729     
26730 });
26731
26732 Roo.apply(Roo.bootstrap.LocationPicker, {
26733     
26734     OverlayView : function(map, options)
26735     {
26736         options = options || {};
26737         
26738         this.setMap(map);
26739     }
26740     
26741     
26742 });/*
26743  * - LGPL
26744  *
26745  * Alert
26746  * 
26747  */
26748
26749 /**
26750  * @class Roo.bootstrap.Alert
26751  * @extends Roo.bootstrap.Component
26752  * Bootstrap Alert class
26753  * @cfg {String} title The title of alert
26754  * @cfg {String} html The content of alert
26755  * @cfg {String} weight (  success | info | warning | danger )
26756  * @cfg {String} faicon font-awesomeicon
26757  * 
26758  * @constructor
26759  * Create a new alert
26760  * @param {Object} config The config object
26761  */
26762
26763
26764 Roo.bootstrap.Alert = function(config){
26765     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26766     
26767 };
26768
26769 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26770     
26771     title: '',
26772     html: '',
26773     weight: false,
26774     faicon: false,
26775     
26776     getAutoCreate : function()
26777     {
26778         
26779         var cfg = {
26780             tag : 'div',
26781             cls : 'alert',
26782             cn : [
26783                 {
26784                     tag : 'i',
26785                     cls : 'roo-alert-icon'
26786                     
26787                 },
26788                 {
26789                     tag : 'b',
26790                     cls : 'roo-alert-title',
26791                     html : this.title
26792                 },
26793                 {
26794                     tag : 'span',
26795                     cls : 'roo-alert-text',
26796                     html : this.html
26797                 }
26798             ]
26799         };
26800         
26801         if(this.faicon){
26802             cfg.cn[0].cls += ' fa ' + this.faicon;
26803         }
26804         
26805         if(this.weight){
26806             cfg.cls += ' alert-' + this.weight;
26807         }
26808         
26809         return cfg;
26810     },
26811     
26812     initEvents: function() 
26813     {
26814         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26815     },
26816     
26817     setTitle : function(str)
26818     {
26819         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26820     },
26821     
26822     setText : function(str)
26823     {
26824         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26825     },
26826     
26827     setWeight : function(weight)
26828     {
26829         if(this.weight){
26830             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26831         }
26832         
26833         this.weight = weight;
26834         
26835         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26836     },
26837     
26838     setIcon : function(icon)
26839     {
26840         if(this.faicon){
26841             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26842         }
26843         
26844         this.faicon = icon;
26845         
26846         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26847     },
26848     
26849     hide: function() 
26850     {
26851         this.el.hide();   
26852     },
26853     
26854     show: function() 
26855     {  
26856         this.el.show();   
26857     }
26858     
26859 });
26860
26861  
26862 /*
26863 * Licence: LGPL
26864 */
26865
26866 /**
26867  * @class Roo.bootstrap.UploadCropbox
26868  * @extends Roo.bootstrap.Component
26869  * Bootstrap UploadCropbox class
26870  * @cfg {String} emptyText show when image has been loaded
26871  * @cfg {String} rotateNotify show when image too small to rotate
26872  * @cfg {Number} errorTimeout default 3000
26873  * @cfg {Number} minWidth default 300
26874  * @cfg {Number} minHeight default 300
26875  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26876  * @cfg {Boolean} isDocument (true|false) default false
26877  * @cfg {String} url action url
26878  * @cfg {String} paramName default 'imageUpload'
26879  * @cfg {String} method default POST
26880  * @cfg {Boolean} loadMask (true|false) default true
26881  * @cfg {Boolean} loadingText default 'Loading...'
26882  * 
26883  * @constructor
26884  * Create a new UploadCropbox
26885  * @param {Object} config The config object
26886  */
26887
26888 Roo.bootstrap.UploadCropbox = function(config){
26889     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26890     
26891     this.addEvents({
26892         /**
26893          * @event beforeselectfile
26894          * Fire before select file
26895          * @param {Roo.bootstrap.UploadCropbox} this
26896          */
26897         "beforeselectfile" : true,
26898         /**
26899          * @event initial
26900          * Fire after initEvent
26901          * @param {Roo.bootstrap.UploadCropbox} this
26902          */
26903         "initial" : true,
26904         /**
26905          * @event crop
26906          * Fire after initEvent
26907          * @param {Roo.bootstrap.UploadCropbox} this
26908          * @param {String} data
26909          */
26910         "crop" : true,
26911         /**
26912          * @event prepare
26913          * Fire when preparing the file data
26914          * @param {Roo.bootstrap.UploadCropbox} this
26915          * @param {Object} file
26916          */
26917         "prepare" : true,
26918         /**
26919          * @event exception
26920          * Fire when get exception
26921          * @param {Roo.bootstrap.UploadCropbox} this
26922          * @param {XMLHttpRequest} xhr
26923          */
26924         "exception" : true,
26925         /**
26926          * @event beforeloadcanvas
26927          * Fire before load the canvas
26928          * @param {Roo.bootstrap.UploadCropbox} this
26929          * @param {String} src
26930          */
26931         "beforeloadcanvas" : true,
26932         /**
26933          * @event trash
26934          * Fire when trash image
26935          * @param {Roo.bootstrap.UploadCropbox} this
26936          */
26937         "trash" : true,
26938         /**
26939          * @event download
26940          * Fire when download the image
26941          * @param {Roo.bootstrap.UploadCropbox} this
26942          */
26943         "download" : true,
26944         /**
26945          * @event footerbuttonclick
26946          * Fire when footerbuttonclick
26947          * @param {Roo.bootstrap.UploadCropbox} this
26948          * @param {String} type
26949          */
26950         "footerbuttonclick" : true,
26951         /**
26952          * @event resize
26953          * Fire when resize
26954          * @param {Roo.bootstrap.UploadCropbox} this
26955          */
26956         "resize" : true,
26957         /**
26958          * @event rotate
26959          * Fire when rotate the image
26960          * @param {Roo.bootstrap.UploadCropbox} this
26961          * @param {String} pos
26962          */
26963         "rotate" : true,
26964         /**
26965          * @event inspect
26966          * Fire when inspect the file
26967          * @param {Roo.bootstrap.UploadCropbox} this
26968          * @param {Object} file
26969          */
26970         "inspect" : true,
26971         /**
26972          * @event upload
26973          * Fire when xhr upload the file
26974          * @param {Roo.bootstrap.UploadCropbox} this
26975          * @param {Object} data
26976          */
26977         "upload" : true,
26978         /**
26979          * @event arrange
26980          * Fire when arrange the file data
26981          * @param {Roo.bootstrap.UploadCropbox} this
26982          * @param {Object} formData
26983          */
26984         "arrange" : true
26985     });
26986     
26987     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26988 };
26989
26990 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26991     
26992     emptyText : 'Click to upload image',
26993     rotateNotify : 'Image is too small to rotate',
26994     errorTimeout : 3000,
26995     scale : 0,
26996     baseScale : 1,
26997     rotate : 0,
26998     dragable : false,
26999     pinching : false,
27000     mouseX : 0,
27001     mouseY : 0,
27002     cropData : false,
27003     minWidth : 300,
27004     minHeight : 300,
27005     file : false,
27006     exif : {},
27007     baseRotate : 1,
27008     cropType : 'image/jpeg',
27009     buttons : false,
27010     canvasLoaded : false,
27011     isDocument : false,
27012     method : 'POST',
27013     paramName : 'imageUpload',
27014     loadMask : true,
27015     loadingText : 'Loading...',
27016     maskEl : false,
27017     
27018     getAutoCreate : function()
27019     {
27020         var cfg = {
27021             tag : 'div',
27022             cls : 'roo-upload-cropbox',
27023             cn : [
27024                 {
27025                     tag : 'input',
27026                     cls : 'roo-upload-cropbox-selector',
27027                     type : 'file'
27028                 },
27029                 {
27030                     tag : 'div',
27031                     cls : 'roo-upload-cropbox-body',
27032                     style : 'cursor:pointer',
27033                     cn : [
27034                         {
27035                             tag : 'div',
27036                             cls : 'roo-upload-cropbox-preview'
27037                         },
27038                         {
27039                             tag : 'div',
27040                             cls : 'roo-upload-cropbox-thumb'
27041                         },
27042                         {
27043                             tag : 'div',
27044                             cls : 'roo-upload-cropbox-empty-notify',
27045                             html : this.emptyText
27046                         },
27047                         {
27048                             tag : 'div',
27049                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27050                             html : this.rotateNotify
27051                         }
27052                     ]
27053                 },
27054                 {
27055                     tag : 'div',
27056                     cls : 'roo-upload-cropbox-footer',
27057                     cn : {
27058                         tag : 'div',
27059                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27060                         cn : []
27061                     }
27062                 }
27063             ]
27064         };
27065         
27066         return cfg;
27067     },
27068     
27069     onRender : function(ct, position)
27070     {
27071         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27072         
27073         if (this.buttons.length) {
27074             
27075             Roo.each(this.buttons, function(bb) {
27076                 
27077                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27078                 
27079                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27080                 
27081             }, this);
27082         }
27083         
27084         if(this.loadMask){
27085             this.maskEl = this.el;
27086         }
27087     },
27088     
27089     initEvents : function()
27090     {
27091         this.urlAPI = (window.createObjectURL && window) || 
27092                                 (window.URL && URL.revokeObjectURL && URL) || 
27093                                 (window.webkitURL && webkitURL);
27094                         
27095         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27096         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27097         
27098         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27099         this.selectorEl.hide();
27100         
27101         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27102         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27103         
27104         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27105         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27106         this.thumbEl.hide();
27107         
27108         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27109         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27110         
27111         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27112         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27113         this.errorEl.hide();
27114         
27115         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27116         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27117         this.footerEl.hide();
27118         
27119         this.setThumbBoxSize();
27120         
27121         this.bind();
27122         
27123         this.resize();
27124         
27125         this.fireEvent('initial', this);
27126     },
27127
27128     bind : function()
27129     {
27130         var _this = this;
27131         
27132         window.addEventListener("resize", function() { _this.resize(); } );
27133         
27134         this.bodyEl.on('click', this.beforeSelectFile, this);
27135         
27136         if(Roo.isTouch){
27137             this.bodyEl.on('touchstart', this.onTouchStart, this);
27138             this.bodyEl.on('touchmove', this.onTouchMove, this);
27139             this.bodyEl.on('touchend', this.onTouchEnd, this);
27140         }
27141         
27142         if(!Roo.isTouch){
27143             this.bodyEl.on('mousedown', this.onMouseDown, this);
27144             this.bodyEl.on('mousemove', this.onMouseMove, this);
27145             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27146             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27147             Roo.get(document).on('mouseup', this.onMouseUp, this);
27148         }
27149         
27150         this.selectorEl.on('change', this.onFileSelected, this);
27151     },
27152     
27153     reset : function()
27154     {    
27155         this.scale = 0;
27156         this.baseScale = 1;
27157         this.rotate = 0;
27158         this.baseRotate = 1;
27159         this.dragable = false;
27160         this.pinching = false;
27161         this.mouseX = 0;
27162         this.mouseY = 0;
27163         this.cropData = false;
27164         this.notifyEl.dom.innerHTML = this.emptyText;
27165         
27166         this.selectorEl.dom.value = '';
27167         
27168     },
27169     
27170     resize : function()
27171     {
27172         if(this.fireEvent('resize', this) != false){
27173             this.setThumbBoxPosition();
27174             this.setCanvasPosition();
27175         }
27176     },
27177     
27178     onFooterButtonClick : function(e, el, o, type)
27179     {
27180         switch (type) {
27181             case 'rotate-left' :
27182                 this.onRotateLeft(e);
27183                 break;
27184             case 'rotate-right' :
27185                 this.onRotateRight(e);
27186                 break;
27187             case 'picture' :
27188                 this.beforeSelectFile(e);
27189                 break;
27190             case 'trash' :
27191                 this.trash(e);
27192                 break;
27193             case 'crop' :
27194                 this.crop(e);
27195                 break;
27196             case 'download' :
27197                 this.download(e);
27198                 break;
27199             default :
27200                 break;
27201         }
27202         
27203         this.fireEvent('footerbuttonclick', this, type);
27204     },
27205     
27206     beforeSelectFile : function(e)
27207     {
27208         e.preventDefault();
27209         
27210         if(this.fireEvent('beforeselectfile', this) != false){
27211             this.selectorEl.dom.click();
27212         }
27213     },
27214     
27215     onFileSelected : function(e)
27216     {
27217         e.preventDefault();
27218         
27219         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27220             return;
27221         }
27222         
27223         var file = this.selectorEl.dom.files[0];
27224         
27225         if(this.fireEvent('inspect', this, file) != false){
27226             this.prepare(file);
27227         }
27228         
27229     },
27230     
27231     trash : function(e)
27232     {
27233         this.fireEvent('trash', this);
27234     },
27235     
27236     download : function(e)
27237     {
27238         this.fireEvent('download', this);
27239     },
27240     
27241     loadCanvas : function(src)
27242     {   
27243         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27244             
27245             this.reset();
27246             
27247             this.imageEl = document.createElement('img');
27248             
27249             var _this = this;
27250             
27251             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27252             
27253             this.imageEl.src = src;
27254         }
27255     },
27256     
27257     onLoadCanvas : function()
27258     {   
27259         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27260         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27261         
27262         this.bodyEl.un('click', this.beforeSelectFile, this);
27263         
27264         this.notifyEl.hide();
27265         this.thumbEl.show();
27266         this.footerEl.show();
27267         
27268         this.baseRotateLevel();
27269         
27270         if(this.isDocument){
27271             this.setThumbBoxSize();
27272         }
27273         
27274         this.setThumbBoxPosition();
27275         
27276         this.baseScaleLevel();
27277         
27278         this.draw();
27279         
27280         this.resize();
27281         
27282         this.canvasLoaded = true;
27283         
27284         if(this.loadMask){
27285             this.maskEl.unmask();
27286         }
27287         
27288     },
27289     
27290     setCanvasPosition : function()
27291     {   
27292         if(!this.canvasEl){
27293             return;
27294         }
27295         
27296         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27297         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27298         
27299         this.previewEl.setLeft(pw);
27300         this.previewEl.setTop(ph);
27301         
27302     },
27303     
27304     onMouseDown : function(e)
27305     {   
27306         e.stopEvent();
27307         
27308         this.dragable = true;
27309         this.pinching = false;
27310         
27311         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27312             this.dragable = false;
27313             return;
27314         }
27315         
27316         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27317         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27318         
27319     },
27320     
27321     onMouseMove : function(e)
27322     {   
27323         e.stopEvent();
27324         
27325         if(!this.canvasLoaded){
27326             return;
27327         }
27328         
27329         if (!this.dragable){
27330             return;
27331         }
27332         
27333         var minX = Math.ceil(this.thumbEl.getLeft(true));
27334         var minY = Math.ceil(this.thumbEl.getTop(true));
27335         
27336         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27337         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27338         
27339         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27340         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27341         
27342         x = x - this.mouseX;
27343         y = y - this.mouseY;
27344         
27345         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27346         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27347         
27348         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27349         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27350         
27351         this.previewEl.setLeft(bgX);
27352         this.previewEl.setTop(bgY);
27353         
27354         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27355         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27356     },
27357     
27358     onMouseUp : function(e)
27359     {   
27360         e.stopEvent();
27361         
27362         this.dragable = false;
27363     },
27364     
27365     onMouseWheel : function(e)
27366     {   
27367         e.stopEvent();
27368         
27369         this.startScale = this.scale;
27370         
27371         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27372         
27373         if(!this.zoomable()){
27374             this.scale = this.startScale;
27375             return;
27376         }
27377         
27378         this.draw();
27379         
27380         return;
27381     },
27382     
27383     zoomable : function()
27384     {
27385         var minScale = this.thumbEl.getWidth() / this.minWidth;
27386         
27387         if(this.minWidth < this.minHeight){
27388             minScale = this.thumbEl.getHeight() / this.minHeight;
27389         }
27390         
27391         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27392         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27393         
27394         if(
27395                 this.isDocument &&
27396                 (this.rotate == 0 || this.rotate == 180) && 
27397                 (
27398                     width > this.imageEl.OriginWidth || 
27399                     height > this.imageEl.OriginHeight ||
27400                     (width < this.minWidth && height < this.minHeight)
27401                 )
27402         ){
27403             return false;
27404         }
27405         
27406         if(
27407                 this.isDocument &&
27408                 (this.rotate == 90 || this.rotate == 270) && 
27409                 (
27410                     width > this.imageEl.OriginWidth || 
27411                     height > this.imageEl.OriginHeight ||
27412                     (width < this.minHeight && height < this.minWidth)
27413                 )
27414         ){
27415             return false;
27416         }
27417         
27418         if(
27419                 !this.isDocument &&
27420                 (this.rotate == 0 || this.rotate == 180) && 
27421                 (
27422                     width < this.minWidth || 
27423                     width > this.imageEl.OriginWidth || 
27424                     height < this.minHeight || 
27425                     height > this.imageEl.OriginHeight
27426                 )
27427         ){
27428             return false;
27429         }
27430         
27431         if(
27432                 !this.isDocument &&
27433                 (this.rotate == 90 || this.rotate == 270) && 
27434                 (
27435                     width < this.minHeight || 
27436                     width > this.imageEl.OriginWidth || 
27437                     height < this.minWidth || 
27438                     height > this.imageEl.OriginHeight
27439                 )
27440         ){
27441             return false;
27442         }
27443         
27444         return true;
27445         
27446     },
27447     
27448     onRotateLeft : function(e)
27449     {   
27450         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27451             
27452             var minScale = this.thumbEl.getWidth() / this.minWidth;
27453             
27454             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27455             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27456             
27457             this.startScale = this.scale;
27458             
27459             while (this.getScaleLevel() < minScale){
27460             
27461                 this.scale = this.scale + 1;
27462                 
27463                 if(!this.zoomable()){
27464                     break;
27465                 }
27466                 
27467                 if(
27468                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27469                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27470                 ){
27471                     continue;
27472                 }
27473                 
27474                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27475
27476                 this.draw();
27477                 
27478                 return;
27479             }
27480             
27481             this.scale = this.startScale;
27482             
27483             this.onRotateFail();
27484             
27485             return false;
27486         }
27487         
27488         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27489
27490         if(this.isDocument){
27491             this.setThumbBoxSize();
27492             this.setThumbBoxPosition();
27493             this.setCanvasPosition();
27494         }
27495         
27496         this.draw();
27497         
27498         this.fireEvent('rotate', this, 'left');
27499         
27500     },
27501     
27502     onRotateRight : function(e)
27503     {
27504         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27505             
27506             var minScale = this.thumbEl.getWidth() / this.minWidth;
27507         
27508             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27509             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27510             
27511             this.startScale = this.scale;
27512             
27513             while (this.getScaleLevel() < minScale){
27514             
27515                 this.scale = this.scale + 1;
27516                 
27517                 if(!this.zoomable()){
27518                     break;
27519                 }
27520                 
27521                 if(
27522                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27523                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27524                 ){
27525                     continue;
27526                 }
27527                 
27528                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27529
27530                 this.draw();
27531                 
27532                 return;
27533             }
27534             
27535             this.scale = this.startScale;
27536             
27537             this.onRotateFail();
27538             
27539             return false;
27540         }
27541         
27542         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27543
27544         if(this.isDocument){
27545             this.setThumbBoxSize();
27546             this.setThumbBoxPosition();
27547             this.setCanvasPosition();
27548         }
27549         
27550         this.draw();
27551         
27552         this.fireEvent('rotate', this, 'right');
27553     },
27554     
27555     onRotateFail : function()
27556     {
27557         this.errorEl.show(true);
27558         
27559         var _this = this;
27560         
27561         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27562     },
27563     
27564     draw : function()
27565     {
27566         this.previewEl.dom.innerHTML = '';
27567         
27568         var canvasEl = document.createElement("canvas");
27569         
27570         var contextEl = canvasEl.getContext("2d");
27571         
27572         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27573         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27574         var center = this.imageEl.OriginWidth / 2;
27575         
27576         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27577             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27578             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27579             center = this.imageEl.OriginHeight / 2;
27580         }
27581         
27582         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27583         
27584         contextEl.translate(center, center);
27585         contextEl.rotate(this.rotate * Math.PI / 180);
27586
27587         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27588         
27589         this.canvasEl = document.createElement("canvas");
27590         
27591         this.contextEl = this.canvasEl.getContext("2d");
27592         
27593         switch (this.rotate) {
27594             case 0 :
27595                 
27596                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27597                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27598                 
27599                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27600                 
27601                 break;
27602             case 90 : 
27603                 
27604                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27605                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27606                 
27607                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27608                     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);
27609                     break;
27610                 }
27611                 
27612                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27613                 
27614                 break;
27615             case 180 :
27616                 
27617                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27618                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27619                 
27620                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27621                     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);
27622                     break;
27623                 }
27624                 
27625                 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);
27626                 
27627                 break;
27628             case 270 :
27629                 
27630                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27631                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27632         
27633                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27634                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27635                     break;
27636                 }
27637                 
27638                 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);
27639                 
27640                 break;
27641             default : 
27642                 break;
27643         }
27644         
27645         this.previewEl.appendChild(this.canvasEl);
27646         
27647         this.setCanvasPosition();
27648     },
27649     
27650     crop : function()
27651     {
27652         if(!this.canvasLoaded){
27653             return;
27654         }
27655         
27656         var imageCanvas = document.createElement("canvas");
27657         
27658         var imageContext = imageCanvas.getContext("2d");
27659         
27660         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27661         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27662         
27663         var center = imageCanvas.width / 2;
27664         
27665         imageContext.translate(center, center);
27666         
27667         imageContext.rotate(this.rotate * Math.PI / 180);
27668         
27669         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27670         
27671         var canvas = document.createElement("canvas");
27672         
27673         var context = canvas.getContext("2d");
27674                 
27675         canvas.width = this.minWidth;
27676         canvas.height = this.minHeight;
27677
27678         switch (this.rotate) {
27679             case 0 :
27680                 
27681                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27682                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27683                 
27684                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27685                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27686                 
27687                 var targetWidth = this.minWidth - 2 * x;
27688                 var targetHeight = this.minHeight - 2 * y;
27689                 
27690                 var scale = 1;
27691                 
27692                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27693                     scale = targetWidth / width;
27694                 }
27695                 
27696                 if(x > 0 && y == 0){
27697                     scale = targetHeight / height;
27698                 }
27699                 
27700                 if(x > 0 && y > 0){
27701                     scale = targetWidth / width;
27702                     
27703                     if(width < height){
27704                         scale = targetHeight / height;
27705                     }
27706                 }
27707                 
27708                 context.scale(scale, scale);
27709                 
27710                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27711                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27712
27713                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27714                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27715
27716                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27717                 
27718                 break;
27719             case 90 : 
27720                 
27721                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27722                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27723                 
27724                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27725                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27726                 
27727                 var targetWidth = this.minWidth - 2 * x;
27728                 var targetHeight = this.minHeight - 2 * y;
27729                 
27730                 var scale = 1;
27731                 
27732                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27733                     scale = targetWidth / width;
27734                 }
27735                 
27736                 if(x > 0 && y == 0){
27737                     scale = targetHeight / height;
27738                 }
27739                 
27740                 if(x > 0 && y > 0){
27741                     scale = targetWidth / width;
27742                     
27743                     if(width < height){
27744                         scale = targetHeight / height;
27745                     }
27746                 }
27747                 
27748                 context.scale(scale, scale);
27749                 
27750                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27751                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27752
27753                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27754                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27755                 
27756                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27757                 
27758                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27759                 
27760                 break;
27761             case 180 :
27762                 
27763                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27764                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27765                 
27766                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27767                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27768                 
27769                 var targetWidth = this.minWidth - 2 * x;
27770                 var targetHeight = this.minHeight - 2 * y;
27771                 
27772                 var scale = 1;
27773                 
27774                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27775                     scale = targetWidth / width;
27776                 }
27777                 
27778                 if(x > 0 && y == 0){
27779                     scale = targetHeight / height;
27780                 }
27781                 
27782                 if(x > 0 && y > 0){
27783                     scale = targetWidth / width;
27784                     
27785                     if(width < height){
27786                         scale = targetHeight / height;
27787                     }
27788                 }
27789                 
27790                 context.scale(scale, scale);
27791                 
27792                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27793                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27794
27795                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27796                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27797
27798                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27799                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27800                 
27801                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27802                 
27803                 break;
27804             case 270 :
27805                 
27806                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27807                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27808                 
27809                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27810                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27811                 
27812                 var targetWidth = this.minWidth - 2 * x;
27813                 var targetHeight = this.minHeight - 2 * y;
27814                 
27815                 var scale = 1;
27816                 
27817                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27818                     scale = targetWidth / width;
27819                 }
27820                 
27821                 if(x > 0 && y == 0){
27822                     scale = targetHeight / height;
27823                 }
27824                 
27825                 if(x > 0 && y > 0){
27826                     scale = targetWidth / width;
27827                     
27828                     if(width < height){
27829                         scale = targetHeight / height;
27830                     }
27831                 }
27832                 
27833                 context.scale(scale, scale);
27834                 
27835                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27836                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27837
27838                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27839                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27840                 
27841                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27842                 
27843                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27844                 
27845                 break;
27846             default : 
27847                 break;
27848         }
27849         
27850         this.cropData = canvas.toDataURL(this.cropType);
27851         
27852         if(this.fireEvent('crop', this, this.cropData) !== false){
27853             this.process(this.file, this.cropData);
27854         }
27855         
27856         return;
27857         
27858     },
27859     
27860     setThumbBoxSize : function()
27861     {
27862         var width, height;
27863         
27864         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27865             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27866             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27867             
27868             this.minWidth = width;
27869             this.minHeight = height;
27870             
27871             if(this.rotate == 90 || this.rotate == 270){
27872                 this.minWidth = height;
27873                 this.minHeight = width;
27874             }
27875         }
27876         
27877         height = 300;
27878         width = Math.ceil(this.minWidth * height / this.minHeight);
27879         
27880         if(this.minWidth > this.minHeight){
27881             width = 300;
27882             height = Math.ceil(this.minHeight * width / this.minWidth);
27883         }
27884         
27885         this.thumbEl.setStyle({
27886             width : width + 'px',
27887             height : height + 'px'
27888         });
27889
27890         return;
27891             
27892     },
27893     
27894     setThumbBoxPosition : function()
27895     {
27896         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27897         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27898         
27899         this.thumbEl.setLeft(x);
27900         this.thumbEl.setTop(y);
27901         
27902     },
27903     
27904     baseRotateLevel : function()
27905     {
27906         this.baseRotate = 1;
27907         
27908         if(
27909                 typeof(this.exif) != 'undefined' &&
27910                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27911                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27912         ){
27913             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27914         }
27915         
27916         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27917         
27918     },
27919     
27920     baseScaleLevel : function()
27921     {
27922         var width, height;
27923         
27924         if(this.isDocument){
27925             
27926             if(this.baseRotate == 6 || this.baseRotate == 8){
27927             
27928                 height = this.thumbEl.getHeight();
27929                 this.baseScale = height / this.imageEl.OriginWidth;
27930
27931                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27932                     width = this.thumbEl.getWidth();
27933                     this.baseScale = width / this.imageEl.OriginHeight;
27934                 }
27935
27936                 return;
27937             }
27938
27939             height = this.thumbEl.getHeight();
27940             this.baseScale = height / this.imageEl.OriginHeight;
27941
27942             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27943                 width = this.thumbEl.getWidth();
27944                 this.baseScale = width / this.imageEl.OriginWidth;
27945             }
27946
27947             return;
27948         }
27949         
27950         if(this.baseRotate == 6 || this.baseRotate == 8){
27951             
27952             width = this.thumbEl.getHeight();
27953             this.baseScale = width / this.imageEl.OriginHeight;
27954             
27955             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27956                 height = this.thumbEl.getWidth();
27957                 this.baseScale = height / this.imageEl.OriginHeight;
27958             }
27959             
27960             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27961                 height = this.thumbEl.getWidth();
27962                 this.baseScale = height / this.imageEl.OriginHeight;
27963                 
27964                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27965                     width = this.thumbEl.getHeight();
27966                     this.baseScale = width / this.imageEl.OriginWidth;
27967                 }
27968             }
27969             
27970             return;
27971         }
27972         
27973         width = this.thumbEl.getWidth();
27974         this.baseScale = width / this.imageEl.OriginWidth;
27975         
27976         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27977             height = this.thumbEl.getHeight();
27978             this.baseScale = height / this.imageEl.OriginHeight;
27979         }
27980         
27981         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27982             
27983             height = this.thumbEl.getHeight();
27984             this.baseScale = height / this.imageEl.OriginHeight;
27985             
27986             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27987                 width = this.thumbEl.getWidth();
27988                 this.baseScale = width / this.imageEl.OriginWidth;
27989             }
27990             
27991         }
27992         
27993         return;
27994     },
27995     
27996     getScaleLevel : function()
27997     {
27998         return this.baseScale * Math.pow(1.1, this.scale);
27999     },
28000     
28001     onTouchStart : function(e)
28002     {
28003         if(!this.canvasLoaded){
28004             this.beforeSelectFile(e);
28005             return;
28006         }
28007         
28008         var touches = e.browserEvent.touches;
28009         
28010         if(!touches){
28011             return;
28012         }
28013         
28014         if(touches.length == 1){
28015             this.onMouseDown(e);
28016             return;
28017         }
28018         
28019         if(touches.length != 2){
28020             return;
28021         }
28022         
28023         var coords = [];
28024         
28025         for(var i = 0, finger; finger = touches[i]; i++){
28026             coords.push(finger.pageX, finger.pageY);
28027         }
28028         
28029         var x = Math.pow(coords[0] - coords[2], 2);
28030         var y = Math.pow(coords[1] - coords[3], 2);
28031         
28032         this.startDistance = Math.sqrt(x + y);
28033         
28034         this.startScale = this.scale;
28035         
28036         this.pinching = true;
28037         this.dragable = false;
28038         
28039     },
28040     
28041     onTouchMove : function(e)
28042     {
28043         if(!this.pinching && !this.dragable){
28044             return;
28045         }
28046         
28047         var touches = e.browserEvent.touches;
28048         
28049         if(!touches){
28050             return;
28051         }
28052         
28053         if(this.dragable){
28054             this.onMouseMove(e);
28055             return;
28056         }
28057         
28058         var coords = [];
28059         
28060         for(var i = 0, finger; finger = touches[i]; i++){
28061             coords.push(finger.pageX, finger.pageY);
28062         }
28063         
28064         var x = Math.pow(coords[0] - coords[2], 2);
28065         var y = Math.pow(coords[1] - coords[3], 2);
28066         
28067         this.endDistance = Math.sqrt(x + y);
28068         
28069         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28070         
28071         if(!this.zoomable()){
28072             this.scale = this.startScale;
28073             return;
28074         }
28075         
28076         this.draw();
28077         
28078     },
28079     
28080     onTouchEnd : function(e)
28081     {
28082         this.pinching = false;
28083         this.dragable = false;
28084         
28085     },
28086     
28087     process : function(file, crop)
28088     {
28089         if(this.loadMask){
28090             this.maskEl.mask(this.loadingText);
28091         }
28092         
28093         this.xhr = new XMLHttpRequest();
28094         
28095         file.xhr = this.xhr;
28096
28097         this.xhr.open(this.method, this.url, true);
28098         
28099         var headers = {
28100             "Accept": "application/json",
28101             "Cache-Control": "no-cache",
28102             "X-Requested-With": "XMLHttpRequest"
28103         };
28104         
28105         for (var headerName in headers) {
28106             var headerValue = headers[headerName];
28107             if (headerValue) {
28108                 this.xhr.setRequestHeader(headerName, headerValue);
28109             }
28110         }
28111         
28112         var _this = this;
28113         
28114         this.xhr.onload = function()
28115         {
28116             _this.xhrOnLoad(_this.xhr);
28117         }
28118         
28119         this.xhr.onerror = function()
28120         {
28121             _this.xhrOnError(_this.xhr);
28122         }
28123         
28124         var formData = new FormData();
28125
28126         formData.append('returnHTML', 'NO');
28127         
28128         if(crop){
28129             formData.append('crop', crop);
28130         }
28131         
28132         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28133             formData.append(this.paramName, file, file.name);
28134         }
28135         
28136         if(typeof(file.filename) != 'undefined'){
28137             formData.append('filename', file.filename);
28138         }
28139         
28140         if(typeof(file.mimetype) != 'undefined'){
28141             formData.append('mimetype', file.mimetype);
28142         }
28143         
28144         if(this.fireEvent('arrange', this, formData) != false){
28145             this.xhr.send(formData);
28146         };
28147     },
28148     
28149     xhrOnLoad : function(xhr)
28150     {
28151         if(this.loadMask){
28152             this.maskEl.unmask();
28153         }
28154         
28155         if (xhr.readyState !== 4) {
28156             this.fireEvent('exception', this, xhr);
28157             return;
28158         }
28159
28160         var response = Roo.decode(xhr.responseText);
28161         
28162         if(!response.success){
28163             this.fireEvent('exception', this, xhr);
28164             return;
28165         }
28166         
28167         var response = Roo.decode(xhr.responseText);
28168         
28169         this.fireEvent('upload', this, response);
28170         
28171     },
28172     
28173     xhrOnError : function()
28174     {
28175         if(this.loadMask){
28176             this.maskEl.unmask();
28177         }
28178         
28179         Roo.log('xhr on error');
28180         
28181         var response = Roo.decode(xhr.responseText);
28182           
28183         Roo.log(response);
28184         
28185     },
28186     
28187     prepare : function(file)
28188     {   
28189         if(this.loadMask){
28190             this.maskEl.mask(this.loadingText);
28191         }
28192         
28193         this.file = false;
28194         this.exif = {};
28195         
28196         if(typeof(file) === 'string'){
28197             this.loadCanvas(file);
28198             return;
28199         }
28200         
28201         if(!file || !this.urlAPI){
28202             return;
28203         }
28204         
28205         this.file = file;
28206         this.cropType = file.type;
28207         
28208         var _this = this;
28209         
28210         if(this.fireEvent('prepare', this, this.file) != false){
28211             
28212             var reader = new FileReader();
28213             
28214             reader.onload = function (e) {
28215                 if (e.target.error) {
28216                     Roo.log(e.target.error);
28217                     return;
28218                 }
28219                 
28220                 var buffer = e.target.result,
28221                     dataView = new DataView(buffer),
28222                     offset = 2,
28223                     maxOffset = dataView.byteLength - 4,
28224                     markerBytes,
28225                     markerLength;
28226                 
28227                 if (dataView.getUint16(0) === 0xffd8) {
28228                     while (offset < maxOffset) {
28229                         markerBytes = dataView.getUint16(offset);
28230                         
28231                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28232                             markerLength = dataView.getUint16(offset + 2) + 2;
28233                             if (offset + markerLength > dataView.byteLength) {
28234                                 Roo.log('Invalid meta data: Invalid segment size.');
28235                                 break;
28236                             }
28237                             
28238                             if(markerBytes == 0xffe1){
28239                                 _this.parseExifData(
28240                                     dataView,
28241                                     offset,
28242                                     markerLength
28243                                 );
28244                             }
28245                             
28246                             offset += markerLength;
28247                             
28248                             continue;
28249                         }
28250                         
28251                         break;
28252                     }
28253                     
28254                 }
28255                 
28256                 var url = _this.urlAPI.createObjectURL(_this.file);
28257                 
28258                 _this.loadCanvas(url);
28259                 
28260                 return;
28261             }
28262             
28263             reader.readAsArrayBuffer(this.file);
28264             
28265         }
28266         
28267     },
28268     
28269     parseExifData : function(dataView, offset, length)
28270     {
28271         var tiffOffset = offset + 10,
28272             littleEndian,
28273             dirOffset;
28274     
28275         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28276             // No Exif data, might be XMP data instead
28277             return;
28278         }
28279         
28280         // Check for the ASCII code for "Exif" (0x45786966):
28281         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28282             // No Exif data, might be XMP data instead
28283             return;
28284         }
28285         if (tiffOffset + 8 > dataView.byteLength) {
28286             Roo.log('Invalid Exif data: Invalid segment size.');
28287             return;
28288         }
28289         // Check for the two null bytes:
28290         if (dataView.getUint16(offset + 8) !== 0x0000) {
28291             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28292             return;
28293         }
28294         // Check the byte alignment:
28295         switch (dataView.getUint16(tiffOffset)) {
28296         case 0x4949:
28297             littleEndian = true;
28298             break;
28299         case 0x4D4D:
28300             littleEndian = false;
28301             break;
28302         default:
28303             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28304             return;
28305         }
28306         // Check for the TIFF tag marker (0x002A):
28307         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28308             Roo.log('Invalid Exif data: Missing TIFF marker.');
28309             return;
28310         }
28311         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28312         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28313         
28314         this.parseExifTags(
28315             dataView,
28316             tiffOffset,
28317             tiffOffset + dirOffset,
28318             littleEndian
28319         );
28320     },
28321     
28322     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28323     {
28324         var tagsNumber,
28325             dirEndOffset,
28326             i;
28327         if (dirOffset + 6 > dataView.byteLength) {
28328             Roo.log('Invalid Exif data: Invalid directory offset.');
28329             return;
28330         }
28331         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28332         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28333         if (dirEndOffset + 4 > dataView.byteLength) {
28334             Roo.log('Invalid Exif data: Invalid directory size.');
28335             return;
28336         }
28337         for (i = 0; i < tagsNumber; i += 1) {
28338             this.parseExifTag(
28339                 dataView,
28340                 tiffOffset,
28341                 dirOffset + 2 + 12 * i, // tag offset
28342                 littleEndian
28343             );
28344         }
28345         // Return the offset to the next directory:
28346         return dataView.getUint32(dirEndOffset, littleEndian);
28347     },
28348     
28349     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28350     {
28351         var tag = dataView.getUint16(offset, littleEndian);
28352         
28353         this.exif[tag] = this.getExifValue(
28354             dataView,
28355             tiffOffset,
28356             offset,
28357             dataView.getUint16(offset + 2, littleEndian), // tag type
28358             dataView.getUint32(offset + 4, littleEndian), // tag length
28359             littleEndian
28360         );
28361     },
28362     
28363     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28364     {
28365         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28366             tagSize,
28367             dataOffset,
28368             values,
28369             i,
28370             str,
28371             c;
28372     
28373         if (!tagType) {
28374             Roo.log('Invalid Exif data: Invalid tag type.');
28375             return;
28376         }
28377         
28378         tagSize = tagType.size * length;
28379         // Determine if the value is contained in the dataOffset bytes,
28380         // or if the value at the dataOffset is a pointer to the actual data:
28381         dataOffset = tagSize > 4 ?
28382                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28383         if (dataOffset + tagSize > dataView.byteLength) {
28384             Roo.log('Invalid Exif data: Invalid data offset.');
28385             return;
28386         }
28387         if (length === 1) {
28388             return tagType.getValue(dataView, dataOffset, littleEndian);
28389         }
28390         values = [];
28391         for (i = 0; i < length; i += 1) {
28392             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28393         }
28394         
28395         if (tagType.ascii) {
28396             str = '';
28397             // Concatenate the chars:
28398             for (i = 0; i < values.length; i += 1) {
28399                 c = values[i];
28400                 // Ignore the terminating NULL byte(s):
28401                 if (c === '\u0000') {
28402                     break;
28403                 }
28404                 str += c;
28405             }
28406             return str;
28407         }
28408         return values;
28409     }
28410     
28411 });
28412
28413 Roo.apply(Roo.bootstrap.UploadCropbox, {
28414     tags : {
28415         'Orientation': 0x0112
28416     },
28417     
28418     Orientation: {
28419             1: 0, //'top-left',
28420 //            2: 'top-right',
28421             3: 180, //'bottom-right',
28422 //            4: 'bottom-left',
28423 //            5: 'left-top',
28424             6: 90, //'right-top',
28425 //            7: 'right-bottom',
28426             8: 270 //'left-bottom'
28427     },
28428     
28429     exifTagTypes : {
28430         // byte, 8-bit unsigned int:
28431         1: {
28432             getValue: function (dataView, dataOffset) {
28433                 return dataView.getUint8(dataOffset);
28434             },
28435             size: 1
28436         },
28437         // ascii, 8-bit byte:
28438         2: {
28439             getValue: function (dataView, dataOffset) {
28440                 return String.fromCharCode(dataView.getUint8(dataOffset));
28441             },
28442             size: 1,
28443             ascii: true
28444         },
28445         // short, 16 bit int:
28446         3: {
28447             getValue: function (dataView, dataOffset, littleEndian) {
28448                 return dataView.getUint16(dataOffset, littleEndian);
28449             },
28450             size: 2
28451         },
28452         // long, 32 bit int:
28453         4: {
28454             getValue: function (dataView, dataOffset, littleEndian) {
28455                 return dataView.getUint32(dataOffset, littleEndian);
28456             },
28457             size: 4
28458         },
28459         // rational = two long values, first is numerator, second is denominator:
28460         5: {
28461             getValue: function (dataView, dataOffset, littleEndian) {
28462                 return dataView.getUint32(dataOffset, littleEndian) /
28463                     dataView.getUint32(dataOffset + 4, littleEndian);
28464             },
28465             size: 8
28466         },
28467         // slong, 32 bit signed int:
28468         9: {
28469             getValue: function (dataView, dataOffset, littleEndian) {
28470                 return dataView.getInt32(dataOffset, littleEndian);
28471             },
28472             size: 4
28473         },
28474         // srational, two slongs, first is numerator, second is denominator:
28475         10: {
28476             getValue: function (dataView, dataOffset, littleEndian) {
28477                 return dataView.getInt32(dataOffset, littleEndian) /
28478                     dataView.getInt32(dataOffset + 4, littleEndian);
28479             },
28480             size: 8
28481         }
28482     },
28483     
28484     footer : {
28485         STANDARD : [
28486             {
28487                 tag : 'div',
28488                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28489                 action : 'rotate-left',
28490                 cn : [
28491                     {
28492                         tag : 'button',
28493                         cls : 'btn btn-default',
28494                         html : '<i class="fa fa-undo"></i>'
28495                     }
28496                 ]
28497             },
28498             {
28499                 tag : 'div',
28500                 cls : 'btn-group roo-upload-cropbox-picture',
28501                 action : 'picture',
28502                 cn : [
28503                     {
28504                         tag : 'button',
28505                         cls : 'btn btn-default',
28506                         html : '<i class="fa fa-picture-o"></i>'
28507                     }
28508                 ]
28509             },
28510             {
28511                 tag : 'div',
28512                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28513                 action : 'rotate-right',
28514                 cn : [
28515                     {
28516                         tag : 'button',
28517                         cls : 'btn btn-default',
28518                         html : '<i class="fa fa-repeat"></i>'
28519                     }
28520                 ]
28521             }
28522         ],
28523         DOCUMENT : [
28524             {
28525                 tag : 'div',
28526                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28527                 action : 'rotate-left',
28528                 cn : [
28529                     {
28530                         tag : 'button',
28531                         cls : 'btn btn-default',
28532                         html : '<i class="fa fa-undo"></i>'
28533                     }
28534                 ]
28535             },
28536             {
28537                 tag : 'div',
28538                 cls : 'btn-group roo-upload-cropbox-download',
28539                 action : 'download',
28540                 cn : [
28541                     {
28542                         tag : 'button',
28543                         cls : 'btn btn-default',
28544                         html : '<i class="fa fa-download"></i>'
28545                     }
28546                 ]
28547             },
28548             {
28549                 tag : 'div',
28550                 cls : 'btn-group roo-upload-cropbox-crop',
28551                 action : 'crop',
28552                 cn : [
28553                     {
28554                         tag : 'button',
28555                         cls : 'btn btn-default',
28556                         html : '<i class="fa fa-crop"></i>'
28557                     }
28558                 ]
28559             },
28560             {
28561                 tag : 'div',
28562                 cls : 'btn-group roo-upload-cropbox-trash',
28563                 action : 'trash',
28564                 cn : [
28565                     {
28566                         tag : 'button',
28567                         cls : 'btn btn-default',
28568                         html : '<i class="fa fa-trash"></i>'
28569                     }
28570                 ]
28571             },
28572             {
28573                 tag : 'div',
28574                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28575                 action : 'rotate-right',
28576                 cn : [
28577                     {
28578                         tag : 'button',
28579                         cls : 'btn btn-default',
28580                         html : '<i class="fa fa-repeat"></i>'
28581                     }
28582                 ]
28583             }
28584         ],
28585         ROTATOR : [
28586             {
28587                 tag : 'div',
28588                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28589                 action : 'rotate-left',
28590                 cn : [
28591                     {
28592                         tag : 'button',
28593                         cls : 'btn btn-default',
28594                         html : '<i class="fa fa-undo"></i>'
28595                     }
28596                 ]
28597             },
28598             {
28599                 tag : 'div',
28600                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28601                 action : 'rotate-right',
28602                 cn : [
28603                     {
28604                         tag : 'button',
28605                         cls : 'btn btn-default',
28606                         html : '<i class="fa fa-repeat"></i>'
28607                     }
28608                 ]
28609             }
28610         ]
28611     }
28612 });
28613
28614 /*
28615 * Licence: LGPL
28616 */
28617
28618 /**
28619  * @class Roo.bootstrap.DocumentManager
28620  * @extends Roo.bootstrap.Component
28621  * Bootstrap DocumentManager class
28622  * @cfg {String} paramName default 'imageUpload'
28623  * @cfg {String} toolTipName default 'filename'
28624  * @cfg {String} method default POST
28625  * @cfg {String} url action url
28626  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28627  * @cfg {Boolean} multiple multiple upload default true
28628  * @cfg {Number} thumbSize default 300
28629  * @cfg {String} fieldLabel
28630  * @cfg {Number} labelWidth default 4
28631  * @cfg {String} labelAlign (left|top) default left
28632  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28633 * @cfg {Number} labellg set the width of label (1-12)
28634  * @cfg {Number} labelmd set the width of label (1-12)
28635  * @cfg {Number} labelsm set the width of label (1-12)
28636  * @cfg {Number} labelxs set the width of label (1-12)
28637  * 
28638  * @constructor
28639  * Create a new DocumentManager
28640  * @param {Object} config The config object
28641  */
28642
28643 Roo.bootstrap.DocumentManager = function(config){
28644     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28645     
28646     this.files = [];
28647     this.delegates = [];
28648     
28649     this.addEvents({
28650         /**
28651          * @event initial
28652          * Fire when initial the DocumentManager
28653          * @param {Roo.bootstrap.DocumentManager} this
28654          */
28655         "initial" : true,
28656         /**
28657          * @event inspect
28658          * inspect selected file
28659          * @param {Roo.bootstrap.DocumentManager} this
28660          * @param {File} file
28661          */
28662         "inspect" : true,
28663         /**
28664          * @event exception
28665          * Fire when xhr load exception
28666          * @param {Roo.bootstrap.DocumentManager} this
28667          * @param {XMLHttpRequest} xhr
28668          */
28669         "exception" : true,
28670         /**
28671          * @event afterupload
28672          * Fire when xhr load exception
28673          * @param {Roo.bootstrap.DocumentManager} this
28674          * @param {XMLHttpRequest} xhr
28675          */
28676         "afterupload" : true,
28677         /**
28678          * @event prepare
28679          * prepare the form data
28680          * @param {Roo.bootstrap.DocumentManager} this
28681          * @param {Object} formData
28682          */
28683         "prepare" : true,
28684         /**
28685          * @event remove
28686          * Fire when remove the file
28687          * @param {Roo.bootstrap.DocumentManager} this
28688          * @param {Object} file
28689          */
28690         "remove" : true,
28691         /**
28692          * @event refresh
28693          * Fire after refresh the file
28694          * @param {Roo.bootstrap.DocumentManager} this
28695          */
28696         "refresh" : true,
28697         /**
28698          * @event click
28699          * Fire after click the image
28700          * @param {Roo.bootstrap.DocumentManager} this
28701          * @param {Object} file
28702          */
28703         "click" : true,
28704         /**
28705          * @event edit
28706          * Fire when upload a image and editable set to true
28707          * @param {Roo.bootstrap.DocumentManager} this
28708          * @param {Object} file
28709          */
28710         "edit" : true,
28711         /**
28712          * @event beforeselectfile
28713          * Fire before select file
28714          * @param {Roo.bootstrap.DocumentManager} this
28715          */
28716         "beforeselectfile" : true,
28717         /**
28718          * @event process
28719          * Fire before process file
28720          * @param {Roo.bootstrap.DocumentManager} this
28721          * @param {Object} file
28722          */
28723         "process" : true,
28724         /**
28725          * @event previewrendered
28726          * Fire when preview rendered
28727          * @param {Roo.bootstrap.DocumentManager} this
28728          * @param {Object} file
28729          */
28730         "previewrendered" : true,
28731         /**
28732          */
28733         "previewResize" : true
28734         
28735     });
28736 };
28737
28738 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28739     
28740     boxes : 0,
28741     inputName : '',
28742     thumbSize : 300,
28743     multiple : true,
28744     files : false,
28745     method : 'POST',
28746     url : '',
28747     paramName : 'imageUpload',
28748     toolTipName : 'filename',
28749     fieldLabel : '',
28750     labelWidth : 4,
28751     labelAlign : 'left',
28752     editable : true,
28753     delegates : false,
28754     xhr : false, 
28755     
28756     labellg : 0,
28757     labelmd : 0,
28758     labelsm : 0,
28759     labelxs : 0,
28760     
28761     getAutoCreate : function()
28762     {   
28763         var managerWidget = {
28764             tag : 'div',
28765             cls : 'roo-document-manager',
28766             cn : [
28767                 {
28768                     tag : 'input',
28769                     cls : 'roo-document-manager-selector',
28770                     type : 'file'
28771                 },
28772                 {
28773                     tag : 'div',
28774                     cls : 'roo-document-manager-uploader',
28775                     cn : [
28776                         {
28777                             tag : 'div',
28778                             cls : 'roo-document-manager-upload-btn',
28779                             html : '<i class="fa fa-plus"></i>'
28780                         }
28781                     ]
28782                     
28783                 }
28784             ]
28785         };
28786         
28787         var content = [
28788             {
28789                 tag : 'div',
28790                 cls : 'column col-md-12',
28791                 cn : managerWidget
28792             }
28793         ];
28794         
28795         if(this.fieldLabel.length){
28796             
28797             content = [
28798                 {
28799                     tag : 'div',
28800                     cls : 'column col-md-12',
28801                     html : this.fieldLabel
28802                 },
28803                 {
28804                     tag : 'div',
28805                     cls : 'column col-md-12',
28806                     cn : managerWidget
28807                 }
28808             ];
28809
28810             if(this.labelAlign == 'left'){
28811                 content = [
28812                     {
28813                         tag : 'div',
28814                         cls : 'column',
28815                         html : this.fieldLabel
28816                     },
28817                     {
28818                         tag : 'div',
28819                         cls : 'column',
28820                         cn : managerWidget
28821                     }
28822                 ];
28823                 
28824                 if(this.labelWidth > 12){
28825                     content[0].style = "width: " + this.labelWidth + 'px';
28826                 }
28827
28828                 if(this.labelWidth < 13 && this.labelmd == 0){
28829                     this.labelmd = this.labelWidth;
28830                 }
28831
28832                 if(this.labellg > 0){
28833                     content[0].cls += ' col-lg-' + this.labellg;
28834                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28835                 }
28836
28837                 if(this.labelmd > 0){
28838                     content[0].cls += ' col-md-' + this.labelmd;
28839                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28840                 }
28841
28842                 if(this.labelsm > 0){
28843                     content[0].cls += ' col-sm-' + this.labelsm;
28844                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28845                 }
28846
28847                 if(this.labelxs > 0){
28848                     content[0].cls += ' col-xs-' + this.labelxs;
28849                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28850                 }
28851                 
28852             }
28853         }
28854         
28855         var cfg = {
28856             tag : 'div',
28857             cls : 'row clearfix',
28858             cn : content
28859         };
28860         
28861         return cfg;
28862         
28863     },
28864     
28865     initEvents : function()
28866     {
28867         this.managerEl = this.el.select('.roo-document-manager', true).first();
28868         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28869         
28870         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28871         this.selectorEl.hide();
28872         
28873         if(this.multiple){
28874             this.selectorEl.attr('multiple', 'multiple');
28875         }
28876         
28877         this.selectorEl.on('change', this.onFileSelected, this);
28878         
28879         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28880         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28881         
28882         this.uploader.on('click', this.onUploaderClick, this);
28883         
28884         this.renderProgressDialog();
28885         
28886         var _this = this;
28887         
28888         window.addEventListener("resize", function() { _this.refresh(); } );
28889         
28890         this.fireEvent('initial', this);
28891     },
28892     
28893     renderProgressDialog : function()
28894     {
28895         var _this = this;
28896         
28897         this.progressDialog = new Roo.bootstrap.Modal({
28898             cls : 'roo-document-manager-progress-dialog',
28899             allow_close : false,
28900             title : '',
28901             buttons : [
28902                 {
28903                     name  :'cancel',
28904                     weight : 'danger',
28905                     html : 'Cancel'
28906                 }
28907             ], 
28908             listeners : { 
28909                 btnclick : function() {
28910                     _this.uploadCancel();
28911                     this.hide();
28912                 }
28913             }
28914         });
28915          
28916         this.progressDialog.render(Roo.get(document.body));
28917          
28918         this.progress = new Roo.bootstrap.Progress({
28919             cls : 'roo-document-manager-progress',
28920             active : true,
28921             striped : true
28922         });
28923         
28924         this.progress.render(this.progressDialog.getChildContainer());
28925         
28926         this.progressBar = new Roo.bootstrap.ProgressBar({
28927             cls : 'roo-document-manager-progress-bar',
28928             aria_valuenow : 0,
28929             aria_valuemin : 0,
28930             aria_valuemax : 12,
28931             panel : 'success'
28932         });
28933         
28934         this.progressBar.render(this.progress.getChildContainer());
28935     },
28936     
28937     onUploaderClick : function(e)
28938     {
28939         e.preventDefault();
28940      
28941         if(this.fireEvent('beforeselectfile', this) != false){
28942             this.selectorEl.dom.click();
28943         }
28944         
28945     },
28946     
28947     onFileSelected : function(e)
28948     {
28949         e.preventDefault();
28950         
28951         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28952             return;
28953         }
28954         
28955         Roo.each(this.selectorEl.dom.files, function(file){
28956             if(this.fireEvent('inspect', this, file) != false){
28957                 this.files.push(file);
28958             }
28959         }, this);
28960         
28961         this.queue();
28962         
28963     },
28964     
28965     queue : function()
28966     {
28967         this.selectorEl.dom.value = '';
28968         
28969         if(!this.files || !this.files.length){
28970             return;
28971         }
28972         
28973         if(this.boxes > 0 && this.files.length > this.boxes){
28974             this.files = this.files.slice(0, this.boxes);
28975         }
28976         
28977         this.uploader.show();
28978         
28979         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28980             this.uploader.hide();
28981         }
28982         
28983         var _this = this;
28984         
28985         var files = [];
28986         
28987         var docs = [];
28988         
28989         Roo.each(this.files, function(file){
28990             
28991             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28992                 var f = this.renderPreview(file);
28993                 files.push(f);
28994                 return;
28995             }
28996             
28997             if(file.type.indexOf('image') != -1){
28998                 this.delegates.push(
28999                     (function(){
29000                         _this.process(file);
29001                     }).createDelegate(this)
29002                 );
29003         
29004                 return;
29005             }
29006             
29007             docs.push(
29008                 (function(){
29009                     _this.process(file);
29010                 }).createDelegate(this)
29011             );
29012             
29013         }, this);
29014         
29015         this.files = files;
29016         
29017         this.delegates = this.delegates.concat(docs);
29018         
29019         if(!this.delegates.length){
29020             this.refresh();
29021             return;
29022         }
29023         
29024         this.progressBar.aria_valuemax = this.delegates.length;
29025         
29026         this.arrange();
29027         
29028         return;
29029     },
29030     
29031     arrange : function()
29032     {
29033         if(!this.delegates.length){
29034             this.progressDialog.hide();
29035             this.refresh();
29036             return;
29037         }
29038         
29039         var delegate = this.delegates.shift();
29040         
29041         this.progressDialog.show();
29042         
29043         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29044         
29045         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29046         
29047         delegate();
29048     },
29049     
29050     refresh : function()
29051     {
29052         this.uploader.show();
29053         
29054         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29055             this.uploader.hide();
29056         }
29057         
29058         Roo.isTouch ? this.closable(false) : this.closable(true);
29059         
29060         this.fireEvent('refresh', this);
29061     },
29062     
29063     onRemove : function(e, el, o)
29064     {
29065         e.preventDefault();
29066         
29067         this.fireEvent('remove', this, o);
29068         
29069     },
29070     
29071     remove : function(o)
29072     {
29073         var files = [];
29074         
29075         Roo.each(this.files, function(file){
29076             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29077                 files.push(file);
29078                 return;
29079             }
29080
29081             o.target.remove();
29082
29083         }, this);
29084         
29085         this.files = files;
29086         
29087         this.refresh();
29088     },
29089     
29090     clear : function()
29091     {
29092         Roo.each(this.files, function(file){
29093             if(!file.target){
29094                 return;
29095             }
29096             
29097             file.target.remove();
29098
29099         }, this);
29100         
29101         this.files = [];
29102         
29103         this.refresh();
29104     },
29105     
29106     onClick : function(e, el, o)
29107     {
29108         e.preventDefault();
29109         
29110         this.fireEvent('click', this, o);
29111         
29112     },
29113     
29114     closable : function(closable)
29115     {
29116         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29117             
29118             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29119             
29120             if(closable){
29121                 el.show();
29122                 return;
29123             }
29124             
29125             el.hide();
29126             
29127         }, this);
29128     },
29129     
29130     xhrOnLoad : function(xhr)
29131     {
29132         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29133             el.remove();
29134         }, this);
29135         
29136         if (xhr.readyState !== 4) {
29137             this.arrange();
29138             this.fireEvent('exception', this, xhr);
29139             return;
29140         }
29141
29142         var response = Roo.decode(xhr.responseText);
29143         
29144         if(!response.success){
29145             this.arrange();
29146             this.fireEvent('exception', this, xhr);
29147             return;
29148         }
29149         
29150         var file = this.renderPreview(response.data);
29151         
29152         this.files.push(file);
29153         
29154         this.arrange();
29155         
29156         this.fireEvent('afterupload', this, xhr);
29157         
29158     },
29159     
29160     xhrOnError : function(xhr)
29161     {
29162         Roo.log('xhr on error');
29163         
29164         var response = Roo.decode(xhr.responseText);
29165           
29166         Roo.log(response);
29167         
29168         this.arrange();
29169     },
29170     
29171     process : function(file)
29172     {
29173         if(this.fireEvent('process', this, file) !== false){
29174             if(this.editable && file.type.indexOf('image') != -1){
29175                 this.fireEvent('edit', this, file);
29176                 return;
29177             }
29178
29179             this.uploadStart(file, false);
29180
29181             return;
29182         }
29183         
29184     },
29185     
29186     uploadStart : function(file, crop)
29187     {
29188         this.xhr = new XMLHttpRequest();
29189         
29190         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29191             this.arrange();
29192             return;
29193         }
29194         
29195         file.xhr = this.xhr;
29196             
29197         this.managerEl.createChild({
29198             tag : 'div',
29199             cls : 'roo-document-manager-loading',
29200             cn : [
29201                 {
29202                     tag : 'div',
29203                     tooltip : file.name,
29204                     cls : 'roo-document-manager-thumb',
29205                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29206                 }
29207             ]
29208
29209         });
29210
29211         this.xhr.open(this.method, this.url, true);
29212         
29213         var headers = {
29214             "Accept": "application/json",
29215             "Cache-Control": "no-cache",
29216             "X-Requested-With": "XMLHttpRequest"
29217         };
29218         
29219         for (var headerName in headers) {
29220             var headerValue = headers[headerName];
29221             if (headerValue) {
29222                 this.xhr.setRequestHeader(headerName, headerValue);
29223             }
29224         }
29225         
29226         var _this = this;
29227         
29228         this.xhr.onload = function()
29229         {
29230             _this.xhrOnLoad(_this.xhr);
29231         }
29232         
29233         this.xhr.onerror = function()
29234         {
29235             _this.xhrOnError(_this.xhr);
29236         }
29237         
29238         var formData = new FormData();
29239
29240         formData.append('returnHTML', 'NO');
29241         
29242         if(crop){
29243             formData.append('crop', crop);
29244         }
29245         
29246         formData.append(this.paramName, file, file.name);
29247         
29248         var options = {
29249             file : file, 
29250             manually : false
29251         };
29252         
29253         if(this.fireEvent('prepare', this, formData, options) != false){
29254             
29255             if(options.manually){
29256                 return;
29257             }
29258             
29259             this.xhr.send(formData);
29260             return;
29261         };
29262         
29263         this.uploadCancel();
29264     },
29265     
29266     uploadCancel : function()
29267     {
29268         if (this.xhr) {
29269             this.xhr.abort();
29270         }
29271         
29272         this.delegates = [];
29273         
29274         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29275             el.remove();
29276         }, this);
29277         
29278         this.arrange();
29279     },
29280     
29281     renderPreview : function(file)
29282     {
29283         if(typeof(file.target) != 'undefined' && file.target){
29284             return file;
29285         }
29286         
29287         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29288         
29289         var previewEl = this.managerEl.createChild({
29290             tag : 'div',
29291             cls : 'roo-document-manager-preview',
29292             cn : [
29293                 {
29294                     tag : 'div',
29295                     tooltip : file[this.toolTipName],
29296                     cls : 'roo-document-manager-thumb',
29297                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29298                 },
29299                 {
29300                     tag : 'button',
29301                     cls : 'close',
29302                     html : '<i class="fa fa-times-circle"></i>'
29303                 }
29304             ]
29305         });
29306
29307         var close = previewEl.select('button.close', true).first();
29308
29309         close.on('click', this.onRemove, this, file);
29310
29311         file.target = previewEl;
29312
29313         var image = previewEl.select('img', true).first();
29314         
29315         var _this = this;
29316         
29317         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29318         
29319         image.on('click', this.onClick, this, file);
29320         
29321         this.fireEvent('previewrendered', this, file);
29322         
29323         return file;
29324         
29325     },
29326     
29327     onPreviewLoad : function(file, image)
29328     {
29329         if(typeof(file.target) == 'undefined' || !file.target){
29330             return;
29331         }
29332         
29333         var width = image.dom.naturalWidth || image.dom.width;
29334         var height = image.dom.naturalHeight || image.dom.height;
29335         
29336         if(!this.previewResize) {
29337             return;
29338         }
29339         
29340         if(width > height){
29341             file.target.addClass('wide');
29342             return;
29343         }
29344         
29345         file.target.addClass('tall');
29346         return;
29347         
29348     },
29349     
29350     uploadFromSource : function(file, crop)
29351     {
29352         this.xhr = new XMLHttpRequest();
29353         
29354         this.managerEl.createChild({
29355             tag : 'div',
29356             cls : 'roo-document-manager-loading',
29357             cn : [
29358                 {
29359                     tag : 'div',
29360                     tooltip : file.name,
29361                     cls : 'roo-document-manager-thumb',
29362                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29363                 }
29364             ]
29365
29366         });
29367
29368         this.xhr.open(this.method, this.url, true);
29369         
29370         var headers = {
29371             "Accept": "application/json",
29372             "Cache-Control": "no-cache",
29373             "X-Requested-With": "XMLHttpRequest"
29374         };
29375         
29376         for (var headerName in headers) {
29377             var headerValue = headers[headerName];
29378             if (headerValue) {
29379                 this.xhr.setRequestHeader(headerName, headerValue);
29380             }
29381         }
29382         
29383         var _this = this;
29384         
29385         this.xhr.onload = function()
29386         {
29387             _this.xhrOnLoad(_this.xhr);
29388         }
29389         
29390         this.xhr.onerror = function()
29391         {
29392             _this.xhrOnError(_this.xhr);
29393         }
29394         
29395         var formData = new FormData();
29396
29397         formData.append('returnHTML', 'NO');
29398         
29399         formData.append('crop', crop);
29400         
29401         if(typeof(file.filename) != 'undefined'){
29402             formData.append('filename', file.filename);
29403         }
29404         
29405         if(typeof(file.mimetype) != 'undefined'){
29406             formData.append('mimetype', file.mimetype);
29407         }
29408         
29409         Roo.log(formData);
29410         
29411         if(this.fireEvent('prepare', this, formData) != false){
29412             this.xhr.send(formData);
29413         };
29414     }
29415 });
29416
29417 /*
29418 * Licence: LGPL
29419 */
29420
29421 /**
29422  * @class Roo.bootstrap.DocumentViewer
29423  * @extends Roo.bootstrap.Component
29424  * Bootstrap DocumentViewer class
29425  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29426  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29427  * 
29428  * @constructor
29429  * Create a new DocumentViewer
29430  * @param {Object} config The config object
29431  */
29432
29433 Roo.bootstrap.DocumentViewer = function(config){
29434     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29435     
29436     this.addEvents({
29437         /**
29438          * @event initial
29439          * Fire after initEvent
29440          * @param {Roo.bootstrap.DocumentViewer} this
29441          */
29442         "initial" : true,
29443         /**
29444          * @event click
29445          * Fire after click
29446          * @param {Roo.bootstrap.DocumentViewer} this
29447          */
29448         "click" : true,
29449         /**
29450          * @event download
29451          * Fire after download button
29452          * @param {Roo.bootstrap.DocumentViewer} this
29453          */
29454         "download" : true,
29455         /**
29456          * @event trash
29457          * Fire after trash button
29458          * @param {Roo.bootstrap.DocumentViewer} this
29459          */
29460         "trash" : true
29461         
29462     });
29463 };
29464
29465 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29466     
29467     showDownload : true,
29468     
29469     showTrash : true,
29470     
29471     getAutoCreate : function()
29472     {
29473         var cfg = {
29474             tag : 'div',
29475             cls : 'roo-document-viewer',
29476             cn : [
29477                 {
29478                     tag : 'div',
29479                     cls : 'roo-document-viewer-body',
29480                     cn : [
29481                         {
29482                             tag : 'div',
29483                             cls : 'roo-document-viewer-thumb',
29484                             cn : [
29485                                 {
29486                                     tag : 'img',
29487                                     cls : 'roo-document-viewer-image'
29488                                 }
29489                             ]
29490                         }
29491                     ]
29492                 },
29493                 {
29494                     tag : 'div',
29495                     cls : 'roo-document-viewer-footer',
29496                     cn : {
29497                         tag : 'div',
29498                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29499                         cn : [
29500                             {
29501                                 tag : 'div',
29502                                 cls : 'btn-group roo-document-viewer-download',
29503                                 cn : [
29504                                     {
29505                                         tag : 'button',
29506                                         cls : 'btn btn-default',
29507                                         html : '<i class="fa fa-download"></i>'
29508                                     }
29509                                 ]
29510                             },
29511                             {
29512                                 tag : 'div',
29513                                 cls : 'btn-group roo-document-viewer-trash',
29514                                 cn : [
29515                                     {
29516                                         tag : 'button',
29517                                         cls : 'btn btn-default',
29518                                         html : '<i class="fa fa-trash"></i>'
29519                                     }
29520                                 ]
29521                             }
29522                         ]
29523                     }
29524                 }
29525             ]
29526         };
29527         
29528         return cfg;
29529     },
29530     
29531     initEvents : function()
29532     {
29533         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29534         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29535         
29536         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29537         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29538         
29539         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29540         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29541         
29542         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29543         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29544         
29545         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29546         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29547         
29548         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29549         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29550         
29551         this.bodyEl.on('click', this.onClick, this);
29552         this.downloadBtn.on('click', this.onDownload, this);
29553         this.trashBtn.on('click', this.onTrash, this);
29554         
29555         this.downloadBtn.hide();
29556         this.trashBtn.hide();
29557         
29558         if(this.showDownload){
29559             this.downloadBtn.show();
29560         }
29561         
29562         if(this.showTrash){
29563             this.trashBtn.show();
29564         }
29565         
29566         if(!this.showDownload && !this.showTrash) {
29567             this.footerEl.hide();
29568         }
29569         
29570     },
29571     
29572     initial : function()
29573     {
29574         this.fireEvent('initial', this);
29575         
29576     },
29577     
29578     onClick : function(e)
29579     {
29580         e.preventDefault();
29581         
29582         this.fireEvent('click', this);
29583     },
29584     
29585     onDownload : function(e)
29586     {
29587         e.preventDefault();
29588         
29589         this.fireEvent('download', this);
29590     },
29591     
29592     onTrash : function(e)
29593     {
29594         e.preventDefault();
29595         
29596         this.fireEvent('trash', this);
29597     }
29598     
29599 });
29600 /*
29601  * - LGPL
29602  *
29603  * nav progress bar
29604  * 
29605  */
29606
29607 /**
29608  * @class Roo.bootstrap.NavProgressBar
29609  * @extends Roo.bootstrap.Component
29610  * Bootstrap NavProgressBar class
29611  * 
29612  * @constructor
29613  * Create a new nav progress bar
29614  * @param {Object} config The config object
29615  */
29616
29617 Roo.bootstrap.NavProgressBar = function(config){
29618     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29619
29620     this.bullets = this.bullets || [];
29621    
29622 //    Roo.bootstrap.NavProgressBar.register(this);
29623      this.addEvents({
29624         /**
29625              * @event changed
29626              * Fires when the active item changes
29627              * @param {Roo.bootstrap.NavProgressBar} this
29628              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29629              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29630          */
29631         'changed': true
29632      });
29633     
29634 };
29635
29636 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29637     
29638     bullets : [],
29639     barItems : [],
29640     
29641     getAutoCreate : function()
29642     {
29643         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29644         
29645         cfg = {
29646             tag : 'div',
29647             cls : 'roo-navigation-bar-group',
29648             cn : [
29649                 {
29650                     tag : 'div',
29651                     cls : 'roo-navigation-top-bar'
29652                 },
29653                 {
29654                     tag : 'div',
29655                     cls : 'roo-navigation-bullets-bar',
29656                     cn : [
29657                         {
29658                             tag : 'ul',
29659                             cls : 'roo-navigation-bar'
29660                         }
29661                     ]
29662                 },
29663                 
29664                 {
29665                     tag : 'div',
29666                     cls : 'roo-navigation-bottom-bar'
29667                 }
29668             ]
29669             
29670         };
29671         
29672         return cfg;
29673         
29674     },
29675     
29676     initEvents: function() 
29677     {
29678         
29679     },
29680     
29681     onRender : function(ct, position) 
29682     {
29683         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29684         
29685         if(this.bullets.length){
29686             Roo.each(this.bullets, function(b){
29687                this.addItem(b);
29688             }, this);
29689         }
29690         
29691         this.format();
29692         
29693     },
29694     
29695     addItem : function(cfg)
29696     {
29697         var item = new Roo.bootstrap.NavProgressItem(cfg);
29698         
29699         item.parentId = this.id;
29700         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29701         
29702         if(cfg.html){
29703             var top = new Roo.bootstrap.Element({
29704                 tag : 'div',
29705                 cls : 'roo-navigation-bar-text'
29706             });
29707             
29708             var bottom = new Roo.bootstrap.Element({
29709                 tag : 'div',
29710                 cls : 'roo-navigation-bar-text'
29711             });
29712             
29713             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29714             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29715             
29716             var topText = new Roo.bootstrap.Element({
29717                 tag : 'span',
29718                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29719             });
29720             
29721             var bottomText = new Roo.bootstrap.Element({
29722                 tag : 'span',
29723                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29724             });
29725             
29726             topText.onRender(top.el, null);
29727             bottomText.onRender(bottom.el, null);
29728             
29729             item.topEl = top;
29730             item.bottomEl = bottom;
29731         }
29732         
29733         this.barItems.push(item);
29734         
29735         return item;
29736     },
29737     
29738     getActive : function()
29739     {
29740         var active = false;
29741         
29742         Roo.each(this.barItems, function(v){
29743             
29744             if (!v.isActive()) {
29745                 return;
29746             }
29747             
29748             active = v;
29749             return false;
29750             
29751         });
29752         
29753         return active;
29754     },
29755     
29756     setActiveItem : function(item)
29757     {
29758         var prev = false;
29759         
29760         Roo.each(this.barItems, function(v){
29761             if (v.rid == item.rid) {
29762                 return ;
29763             }
29764             
29765             if (v.isActive()) {
29766                 v.setActive(false);
29767                 prev = v;
29768             }
29769         });
29770
29771         item.setActive(true);
29772         
29773         this.fireEvent('changed', this, item, prev);
29774     },
29775     
29776     getBarItem: function(rid)
29777     {
29778         var ret = false;
29779         
29780         Roo.each(this.barItems, function(e) {
29781             if (e.rid != rid) {
29782                 return;
29783             }
29784             
29785             ret =  e;
29786             return false;
29787         });
29788         
29789         return ret;
29790     },
29791     
29792     indexOfItem : function(item)
29793     {
29794         var index = false;
29795         
29796         Roo.each(this.barItems, function(v, i){
29797             
29798             if (v.rid != item.rid) {
29799                 return;
29800             }
29801             
29802             index = i;
29803             return false
29804         });
29805         
29806         return index;
29807     },
29808     
29809     setActiveNext : function()
29810     {
29811         var i = this.indexOfItem(this.getActive());
29812         
29813         if (i > this.barItems.length) {
29814             return;
29815         }
29816         
29817         this.setActiveItem(this.barItems[i+1]);
29818     },
29819     
29820     setActivePrev : function()
29821     {
29822         var i = this.indexOfItem(this.getActive());
29823         
29824         if (i  < 1) {
29825             return;
29826         }
29827         
29828         this.setActiveItem(this.barItems[i-1]);
29829     },
29830     
29831     format : function()
29832     {
29833         if(!this.barItems.length){
29834             return;
29835         }
29836      
29837         var width = 100 / this.barItems.length;
29838         
29839         Roo.each(this.barItems, function(i){
29840             i.el.setStyle('width', width + '%');
29841             i.topEl.el.setStyle('width', width + '%');
29842             i.bottomEl.el.setStyle('width', width + '%');
29843         }, this);
29844         
29845     }
29846     
29847 });
29848 /*
29849  * - LGPL
29850  *
29851  * Nav Progress Item
29852  * 
29853  */
29854
29855 /**
29856  * @class Roo.bootstrap.NavProgressItem
29857  * @extends Roo.bootstrap.Component
29858  * Bootstrap NavProgressItem class
29859  * @cfg {String} rid the reference id
29860  * @cfg {Boolean} active (true|false) Is item active default false
29861  * @cfg {Boolean} disabled (true|false) Is item active default false
29862  * @cfg {String} html
29863  * @cfg {String} position (top|bottom) text position default bottom
29864  * @cfg {String} icon show icon instead of number
29865  * 
29866  * @constructor
29867  * Create a new NavProgressItem
29868  * @param {Object} config The config object
29869  */
29870 Roo.bootstrap.NavProgressItem = function(config){
29871     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29872     this.addEvents({
29873         // raw events
29874         /**
29875          * @event click
29876          * The raw click event for the entire grid.
29877          * @param {Roo.bootstrap.NavProgressItem} this
29878          * @param {Roo.EventObject} e
29879          */
29880         "click" : true
29881     });
29882    
29883 };
29884
29885 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29886     
29887     rid : '',
29888     active : false,
29889     disabled : false,
29890     html : '',
29891     position : 'bottom',
29892     icon : false,
29893     
29894     getAutoCreate : function()
29895     {
29896         var iconCls = 'roo-navigation-bar-item-icon';
29897         
29898         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29899         
29900         var cfg = {
29901             tag: 'li',
29902             cls: 'roo-navigation-bar-item',
29903             cn : [
29904                 {
29905                     tag : 'i',
29906                     cls : iconCls
29907                 }
29908             ]
29909         };
29910         
29911         if(this.active){
29912             cfg.cls += ' active';
29913         }
29914         if(this.disabled){
29915             cfg.cls += ' disabled';
29916         }
29917         
29918         return cfg;
29919     },
29920     
29921     disable : function()
29922     {
29923         this.setDisabled(true);
29924     },
29925     
29926     enable : function()
29927     {
29928         this.setDisabled(false);
29929     },
29930     
29931     initEvents: function() 
29932     {
29933         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29934         
29935         this.iconEl.on('click', this.onClick, this);
29936     },
29937     
29938     onClick : function(e)
29939     {
29940         e.preventDefault();
29941         
29942         if(this.disabled){
29943             return;
29944         }
29945         
29946         if(this.fireEvent('click', this, e) === false){
29947             return;
29948         };
29949         
29950         this.parent().setActiveItem(this);
29951     },
29952     
29953     isActive: function () 
29954     {
29955         return this.active;
29956     },
29957     
29958     setActive : function(state)
29959     {
29960         if(this.active == state){
29961             return;
29962         }
29963         
29964         this.active = state;
29965         
29966         if (state) {
29967             this.el.addClass('active');
29968             return;
29969         }
29970         
29971         this.el.removeClass('active');
29972         
29973         return;
29974     },
29975     
29976     setDisabled : function(state)
29977     {
29978         if(this.disabled == state){
29979             return;
29980         }
29981         
29982         this.disabled = state;
29983         
29984         if (state) {
29985             this.el.addClass('disabled');
29986             return;
29987         }
29988         
29989         this.el.removeClass('disabled');
29990     },
29991     
29992     tooltipEl : function()
29993     {
29994         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29995     }
29996 });
29997  
29998
29999  /*
30000  * - LGPL
30001  *
30002  * FieldLabel
30003  * 
30004  */
30005
30006 /**
30007  * @class Roo.bootstrap.FieldLabel
30008  * @extends Roo.bootstrap.Component
30009  * Bootstrap FieldLabel class
30010  * @cfg {String} html contents of the element
30011  * @cfg {String} tag tag of the element default label
30012  * @cfg {String} cls class of the element
30013  * @cfg {String} target label target 
30014  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30015  * @cfg {String} invalidClass default "text-warning"
30016  * @cfg {String} validClass default "text-success"
30017  * @cfg {String} iconTooltip default "This field is required"
30018  * @cfg {String} indicatorpos (left|right) default left
30019  * 
30020  * @constructor
30021  * Create a new FieldLabel
30022  * @param {Object} config The config object
30023  */
30024
30025 Roo.bootstrap.FieldLabel = function(config){
30026     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30027     
30028     this.addEvents({
30029             /**
30030              * @event invalid
30031              * Fires after the field has been marked as invalid.
30032              * @param {Roo.form.FieldLabel} this
30033              * @param {String} msg The validation message
30034              */
30035             invalid : true,
30036             /**
30037              * @event valid
30038              * Fires after the field has been validated with no errors.
30039              * @param {Roo.form.FieldLabel} this
30040              */
30041             valid : true
30042         });
30043 };
30044
30045 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30046     
30047     tag: 'label',
30048     cls: '',
30049     html: '',
30050     target: '',
30051     allowBlank : true,
30052     invalidClass : 'has-warning',
30053     validClass : 'has-success',
30054     iconTooltip : 'This field is required',
30055     indicatorpos : 'left',
30056     
30057     getAutoCreate : function(){
30058         
30059         var cls = "";
30060         if (!this.allowBlank) {
30061             cls  = "visible";
30062         }
30063         
30064         var cfg = {
30065             tag : this.tag,
30066             cls : 'roo-bootstrap-field-label ' + this.cls,
30067             for : this.target,
30068             cn : [
30069                 {
30070                     tag : 'i',
30071                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30072                     tooltip : this.iconTooltip
30073                 },
30074                 {
30075                     tag : 'span',
30076                     html : this.html
30077                 }
30078             ] 
30079         };
30080         
30081         if(this.indicatorpos == 'right'){
30082             var cfg = {
30083                 tag : this.tag,
30084                 cls : 'roo-bootstrap-field-label ' + this.cls,
30085                 for : this.target,
30086                 cn : [
30087                     {
30088                         tag : 'span',
30089                         html : this.html
30090                     },
30091                     {
30092                         tag : 'i',
30093                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30094                         tooltip : this.iconTooltip
30095                     }
30096                 ] 
30097             };
30098         }
30099         
30100         return cfg;
30101     },
30102     
30103     initEvents: function() 
30104     {
30105         Roo.bootstrap.Element.superclass.initEvents.call(this);
30106         
30107         this.indicator = this.indicatorEl();
30108         
30109         if(this.indicator){
30110             this.indicator.removeClass('visible');
30111             this.indicator.addClass('invisible');
30112         }
30113         
30114         Roo.bootstrap.FieldLabel.register(this);
30115     },
30116     
30117     indicatorEl : function()
30118     {
30119         var indicator = this.el.select('i.roo-required-indicator',true).first();
30120         
30121         if(!indicator){
30122             return false;
30123         }
30124         
30125         return indicator;
30126         
30127     },
30128     
30129     /**
30130      * Mark this field as valid
30131      */
30132     markValid : function()
30133     {
30134         if(this.indicator){
30135             this.indicator.removeClass('visible');
30136             this.indicator.addClass('invisible');
30137         }
30138         
30139         this.el.removeClass(this.invalidClass);
30140         
30141         this.el.addClass(this.validClass);
30142         
30143         this.fireEvent('valid', this);
30144     },
30145     
30146     /**
30147      * Mark this field as invalid
30148      * @param {String} msg The validation message
30149      */
30150     markInvalid : function(msg)
30151     {
30152         if(this.indicator){
30153             this.indicator.removeClass('invisible');
30154             this.indicator.addClass('visible');
30155         }
30156         
30157         this.el.removeClass(this.validClass);
30158         
30159         this.el.addClass(this.invalidClass);
30160         
30161         this.fireEvent('invalid', this, msg);
30162     }
30163     
30164    
30165 });
30166
30167 Roo.apply(Roo.bootstrap.FieldLabel, {
30168     
30169     groups: {},
30170     
30171      /**
30172     * register a FieldLabel Group
30173     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30174     */
30175     register : function(label)
30176     {
30177         if(this.groups.hasOwnProperty(label.target)){
30178             return;
30179         }
30180      
30181         this.groups[label.target] = label;
30182         
30183     },
30184     /**
30185     * fetch a FieldLabel Group based on the target
30186     * @param {string} target
30187     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30188     */
30189     get: function(target) {
30190         if (typeof(this.groups[target]) == 'undefined') {
30191             return false;
30192         }
30193         
30194         return this.groups[target] ;
30195     }
30196 });
30197
30198  
30199
30200  /*
30201  * - LGPL
30202  *
30203  * page DateSplitField.
30204  * 
30205  */
30206
30207
30208 /**
30209  * @class Roo.bootstrap.DateSplitField
30210  * @extends Roo.bootstrap.Component
30211  * Bootstrap DateSplitField class
30212  * @cfg {string} fieldLabel - the label associated
30213  * @cfg {Number} labelWidth set the width of label (0-12)
30214  * @cfg {String} labelAlign (top|left)
30215  * @cfg {Boolean} dayAllowBlank (true|false) default false
30216  * @cfg {Boolean} monthAllowBlank (true|false) default false
30217  * @cfg {Boolean} yearAllowBlank (true|false) default false
30218  * @cfg {string} dayPlaceholder 
30219  * @cfg {string} monthPlaceholder
30220  * @cfg {string} yearPlaceholder
30221  * @cfg {string} dayFormat default 'd'
30222  * @cfg {string} monthFormat default 'm'
30223  * @cfg {string} yearFormat default 'Y'
30224  * @cfg {Number} labellg set the width of label (1-12)
30225  * @cfg {Number} labelmd set the width of label (1-12)
30226  * @cfg {Number} labelsm set the width of label (1-12)
30227  * @cfg {Number} labelxs set the width of label (1-12)
30228
30229  *     
30230  * @constructor
30231  * Create a new DateSplitField
30232  * @param {Object} config The config object
30233  */
30234
30235 Roo.bootstrap.DateSplitField = function(config){
30236     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30237     
30238     this.addEvents({
30239         // raw events
30240          /**
30241          * @event years
30242          * getting the data of years
30243          * @param {Roo.bootstrap.DateSplitField} this
30244          * @param {Object} years
30245          */
30246         "years" : true,
30247         /**
30248          * @event days
30249          * getting the data of days
30250          * @param {Roo.bootstrap.DateSplitField} this
30251          * @param {Object} days
30252          */
30253         "days" : true,
30254         /**
30255          * @event invalid
30256          * Fires after the field has been marked as invalid.
30257          * @param {Roo.form.Field} this
30258          * @param {String} msg The validation message
30259          */
30260         invalid : true,
30261        /**
30262          * @event valid
30263          * Fires after the field has been validated with no errors.
30264          * @param {Roo.form.Field} this
30265          */
30266         valid : true
30267     });
30268 };
30269
30270 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30271     
30272     fieldLabel : '',
30273     labelAlign : 'top',
30274     labelWidth : 3,
30275     dayAllowBlank : false,
30276     monthAllowBlank : false,
30277     yearAllowBlank : false,
30278     dayPlaceholder : '',
30279     monthPlaceholder : '',
30280     yearPlaceholder : '',
30281     dayFormat : 'd',
30282     monthFormat : 'm',
30283     yearFormat : 'Y',
30284     isFormField : true,
30285     labellg : 0,
30286     labelmd : 0,
30287     labelsm : 0,
30288     labelxs : 0,
30289     
30290     getAutoCreate : function()
30291     {
30292         var cfg = {
30293             tag : 'div',
30294             cls : 'row roo-date-split-field-group',
30295             cn : [
30296                 {
30297                     tag : 'input',
30298                     type : 'hidden',
30299                     cls : 'form-hidden-field roo-date-split-field-group-value',
30300                     name : this.name
30301                 }
30302             ]
30303         };
30304         
30305         var labelCls = 'col-md-12';
30306         var contentCls = 'col-md-4';
30307         
30308         if(this.fieldLabel){
30309             
30310             var label = {
30311                 tag : 'div',
30312                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30313                 cn : [
30314                     {
30315                         tag : 'label',
30316                         html : this.fieldLabel
30317                     }
30318                 ]
30319             };
30320             
30321             if(this.labelAlign == 'left'){
30322             
30323                 if(this.labelWidth > 12){
30324                     label.style = "width: " + this.labelWidth + 'px';
30325                 }
30326
30327                 if(this.labelWidth < 13 && this.labelmd == 0){
30328                     this.labelmd = this.labelWidth;
30329                 }
30330
30331                 if(this.labellg > 0){
30332                     labelCls = ' col-lg-' + this.labellg;
30333                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30334                 }
30335
30336                 if(this.labelmd > 0){
30337                     labelCls = ' col-md-' + this.labelmd;
30338                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30339                 }
30340
30341                 if(this.labelsm > 0){
30342                     labelCls = ' col-sm-' + this.labelsm;
30343                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30344                 }
30345
30346                 if(this.labelxs > 0){
30347                     labelCls = ' col-xs-' + this.labelxs;
30348                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30349                 }
30350             }
30351             
30352             label.cls += ' ' + labelCls;
30353             
30354             cfg.cn.push(label);
30355         }
30356         
30357         Roo.each(['day', 'month', 'year'], function(t){
30358             cfg.cn.push({
30359                 tag : 'div',
30360                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30361             });
30362         }, this);
30363         
30364         return cfg;
30365     },
30366     
30367     inputEl: function ()
30368     {
30369         return this.el.select('.roo-date-split-field-group-value', true).first();
30370     },
30371     
30372     onRender : function(ct, position) 
30373     {
30374         var _this = this;
30375         
30376         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30377         
30378         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30379         
30380         this.dayField = new Roo.bootstrap.ComboBox({
30381             allowBlank : this.dayAllowBlank,
30382             alwaysQuery : true,
30383             displayField : 'value',
30384             editable : false,
30385             fieldLabel : '',
30386             forceSelection : true,
30387             mode : 'local',
30388             placeholder : this.dayPlaceholder,
30389             selectOnFocus : true,
30390             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30391             triggerAction : 'all',
30392             typeAhead : true,
30393             valueField : 'value',
30394             store : new Roo.data.SimpleStore({
30395                 data : (function() {    
30396                     var days = [];
30397                     _this.fireEvent('days', _this, days);
30398                     return days;
30399                 })(),
30400                 fields : [ 'value' ]
30401             }),
30402             listeners : {
30403                 select : function (_self, record, index)
30404                 {
30405                     _this.setValue(_this.getValue());
30406                 }
30407             }
30408         });
30409
30410         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30411         
30412         this.monthField = new Roo.bootstrap.MonthField({
30413             after : '<i class=\"fa fa-calendar\"></i>',
30414             allowBlank : this.monthAllowBlank,
30415             placeholder : this.monthPlaceholder,
30416             readOnly : true,
30417             listeners : {
30418                 render : function (_self)
30419                 {
30420                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30421                         e.preventDefault();
30422                         _self.focus();
30423                     });
30424                 },
30425                 select : function (_self, oldvalue, newvalue)
30426                 {
30427                     _this.setValue(_this.getValue());
30428                 }
30429             }
30430         });
30431         
30432         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30433         
30434         this.yearField = new Roo.bootstrap.ComboBox({
30435             allowBlank : this.yearAllowBlank,
30436             alwaysQuery : true,
30437             displayField : 'value',
30438             editable : false,
30439             fieldLabel : '',
30440             forceSelection : true,
30441             mode : 'local',
30442             placeholder : this.yearPlaceholder,
30443             selectOnFocus : true,
30444             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30445             triggerAction : 'all',
30446             typeAhead : true,
30447             valueField : 'value',
30448             store : new Roo.data.SimpleStore({
30449                 data : (function() {
30450                     var years = [];
30451                     _this.fireEvent('years', _this, years);
30452                     return years;
30453                 })(),
30454                 fields : [ 'value' ]
30455             }),
30456             listeners : {
30457                 select : function (_self, record, index)
30458                 {
30459                     _this.setValue(_this.getValue());
30460                 }
30461             }
30462         });
30463
30464         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30465     },
30466     
30467     setValue : function(v, format)
30468     {
30469         this.inputEl.dom.value = v;
30470         
30471         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30472         
30473         var d = Date.parseDate(v, f);
30474         
30475         if(!d){
30476             this.validate();
30477             return;
30478         }
30479         
30480         this.setDay(d.format(this.dayFormat));
30481         this.setMonth(d.format(this.monthFormat));
30482         this.setYear(d.format(this.yearFormat));
30483         
30484         this.validate();
30485         
30486         return;
30487     },
30488     
30489     setDay : function(v)
30490     {
30491         this.dayField.setValue(v);
30492         this.inputEl.dom.value = this.getValue();
30493         this.validate();
30494         return;
30495     },
30496     
30497     setMonth : function(v)
30498     {
30499         this.monthField.setValue(v, true);
30500         this.inputEl.dom.value = this.getValue();
30501         this.validate();
30502         return;
30503     },
30504     
30505     setYear : function(v)
30506     {
30507         this.yearField.setValue(v);
30508         this.inputEl.dom.value = this.getValue();
30509         this.validate();
30510         return;
30511     },
30512     
30513     getDay : function()
30514     {
30515         return this.dayField.getValue();
30516     },
30517     
30518     getMonth : function()
30519     {
30520         return this.monthField.getValue();
30521     },
30522     
30523     getYear : function()
30524     {
30525         return this.yearField.getValue();
30526     },
30527     
30528     getValue : function()
30529     {
30530         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30531         
30532         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30533         
30534         return date;
30535     },
30536     
30537     reset : function()
30538     {
30539         this.setDay('');
30540         this.setMonth('');
30541         this.setYear('');
30542         this.inputEl.dom.value = '';
30543         this.validate();
30544         return;
30545     },
30546     
30547     validate : function()
30548     {
30549         var d = this.dayField.validate();
30550         var m = this.monthField.validate();
30551         var y = this.yearField.validate();
30552         
30553         var valid = true;
30554         
30555         if(
30556                 (!this.dayAllowBlank && !d) ||
30557                 (!this.monthAllowBlank && !m) ||
30558                 (!this.yearAllowBlank && !y)
30559         ){
30560             valid = false;
30561         }
30562         
30563         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30564             return valid;
30565         }
30566         
30567         if(valid){
30568             this.markValid();
30569             return valid;
30570         }
30571         
30572         this.markInvalid();
30573         
30574         return valid;
30575     },
30576     
30577     markValid : function()
30578     {
30579         
30580         var label = this.el.select('label', true).first();
30581         var icon = this.el.select('i.fa-star', true).first();
30582
30583         if(label && icon){
30584             icon.remove();
30585         }
30586         
30587         this.fireEvent('valid', this);
30588     },
30589     
30590      /**
30591      * Mark this field as invalid
30592      * @param {String} msg The validation message
30593      */
30594     markInvalid : function(msg)
30595     {
30596         
30597         var label = this.el.select('label', true).first();
30598         var icon = this.el.select('i.fa-star', true).first();
30599
30600         if(label && !icon){
30601             this.el.select('.roo-date-split-field-label', true).createChild({
30602                 tag : 'i',
30603                 cls : 'text-danger fa fa-lg fa-star',
30604                 tooltip : 'This field is required',
30605                 style : 'margin-right:5px;'
30606             }, label, true);
30607         }
30608         
30609         this.fireEvent('invalid', this, msg);
30610     },
30611     
30612     clearInvalid : function()
30613     {
30614         var label = this.el.select('label', true).first();
30615         var icon = this.el.select('i.fa-star', true).first();
30616
30617         if(label && icon){
30618             icon.remove();
30619         }
30620         
30621         this.fireEvent('valid', this);
30622     },
30623     
30624     getName: function()
30625     {
30626         return this.name;
30627     }
30628     
30629 });
30630
30631  /**
30632  *
30633  * This is based on 
30634  * http://masonry.desandro.com
30635  *
30636  * The idea is to render all the bricks based on vertical width...
30637  *
30638  * The original code extends 'outlayer' - we might need to use that....
30639  * 
30640  */
30641
30642
30643 /**
30644  * @class Roo.bootstrap.LayoutMasonry
30645  * @extends Roo.bootstrap.Component
30646  * Bootstrap Layout Masonry class
30647  * 
30648  * @constructor
30649  * Create a new Element
30650  * @param {Object} config The config object
30651  */
30652
30653 Roo.bootstrap.LayoutMasonry = function(config){
30654     
30655     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30656     
30657     this.bricks = [];
30658     
30659     Roo.bootstrap.LayoutMasonry.register(this);
30660     
30661     this.addEvents({
30662         // raw events
30663         /**
30664          * @event layout
30665          * Fire after layout the items
30666          * @param {Roo.bootstrap.LayoutMasonry} this
30667          * @param {Roo.EventObject} e
30668          */
30669         "layout" : true
30670     });
30671     
30672 };
30673
30674 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30675     
30676     /**
30677      * @cfg {Boolean} isLayoutInstant = no animation?
30678      */   
30679     isLayoutInstant : false, // needed?
30680    
30681     /**
30682      * @cfg {Number} boxWidth  width of the columns
30683      */   
30684     boxWidth : 450,
30685     
30686       /**
30687      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30688      */   
30689     boxHeight : 0,
30690     
30691     /**
30692      * @cfg {Number} padWidth padding below box..
30693      */   
30694     padWidth : 10, 
30695     
30696     /**
30697      * @cfg {Number} gutter gutter width..
30698      */   
30699     gutter : 10,
30700     
30701      /**
30702      * @cfg {Number} maxCols maximum number of columns
30703      */   
30704     
30705     maxCols: 0,
30706     
30707     /**
30708      * @cfg {Boolean} isAutoInitial defalut true
30709      */   
30710     isAutoInitial : true, 
30711     
30712     containerWidth: 0,
30713     
30714     /**
30715      * @cfg {Boolean} isHorizontal defalut false
30716      */   
30717     isHorizontal : false, 
30718
30719     currentSize : null,
30720     
30721     tag: 'div',
30722     
30723     cls: '',
30724     
30725     bricks: null, //CompositeElement
30726     
30727     cols : 1,
30728     
30729     _isLayoutInited : false,
30730     
30731 //    isAlternative : false, // only use for vertical layout...
30732     
30733     /**
30734      * @cfg {Number} alternativePadWidth padding below box..
30735      */   
30736     alternativePadWidth : 50,
30737     
30738     selectedBrick : [],
30739     
30740     getAutoCreate : function(){
30741         
30742         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30743         
30744         var cfg = {
30745             tag: this.tag,
30746             cls: 'blog-masonary-wrapper ' + this.cls,
30747             cn : {
30748                 cls : 'mas-boxes masonary'
30749             }
30750         };
30751         
30752         return cfg;
30753     },
30754     
30755     getChildContainer: function( )
30756     {
30757         if (this.boxesEl) {
30758             return this.boxesEl;
30759         }
30760         
30761         this.boxesEl = this.el.select('.mas-boxes').first();
30762         
30763         return this.boxesEl;
30764     },
30765     
30766     
30767     initEvents : function()
30768     {
30769         var _this = this;
30770         
30771         if(this.isAutoInitial){
30772             Roo.log('hook children rendered');
30773             this.on('childrenrendered', function() {
30774                 Roo.log('children rendered');
30775                 _this.initial();
30776             } ,this);
30777         }
30778     },
30779     
30780     initial : function()
30781     {
30782         this.selectedBrick = [];
30783         
30784         this.currentSize = this.el.getBox(true);
30785         
30786         Roo.EventManager.onWindowResize(this.resize, this); 
30787
30788         if(!this.isAutoInitial){
30789             this.layout();
30790             return;
30791         }
30792         
30793         this.layout();
30794         
30795         return;
30796         //this.layout.defer(500,this);
30797         
30798     },
30799     
30800     resize : function()
30801     {
30802         var cs = this.el.getBox(true);
30803         
30804         if (
30805                 this.currentSize.width == cs.width && 
30806                 this.currentSize.x == cs.x && 
30807                 this.currentSize.height == cs.height && 
30808                 this.currentSize.y == cs.y 
30809         ) {
30810             Roo.log("no change in with or X or Y");
30811             return;
30812         }
30813         
30814         this.currentSize = cs;
30815         
30816         this.layout();
30817         
30818     },
30819     
30820     layout : function()
30821     {   
30822         this._resetLayout();
30823         
30824         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30825         
30826         this.layoutItems( isInstant );
30827       
30828         this._isLayoutInited = true;
30829         
30830         this.fireEvent('layout', this);
30831         
30832     },
30833     
30834     _resetLayout : function()
30835     {
30836         if(this.isHorizontal){
30837             this.horizontalMeasureColumns();
30838             return;
30839         }
30840         
30841         this.verticalMeasureColumns();
30842         
30843     },
30844     
30845     verticalMeasureColumns : function()
30846     {
30847         this.getContainerWidth();
30848         
30849 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30850 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30851 //            return;
30852 //        }
30853         
30854         var boxWidth = this.boxWidth + this.padWidth;
30855         
30856         if(this.containerWidth < this.boxWidth){
30857             boxWidth = this.containerWidth
30858         }
30859         
30860         var containerWidth = this.containerWidth;
30861         
30862         var cols = Math.floor(containerWidth / boxWidth);
30863         
30864         this.cols = Math.max( cols, 1 );
30865         
30866         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30867         
30868         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30869         
30870         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30871         
30872         this.colWidth = boxWidth + avail - this.padWidth;
30873         
30874         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30875         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30876     },
30877     
30878     horizontalMeasureColumns : function()
30879     {
30880         this.getContainerWidth();
30881         
30882         var boxWidth = this.boxWidth;
30883         
30884         if(this.containerWidth < boxWidth){
30885             boxWidth = this.containerWidth;
30886         }
30887         
30888         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30889         
30890         this.el.setHeight(boxWidth);
30891         
30892     },
30893     
30894     getContainerWidth : function()
30895     {
30896         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30897     },
30898     
30899     layoutItems : function( isInstant )
30900     {
30901         Roo.log(this.bricks);
30902         
30903         var items = Roo.apply([], this.bricks);
30904         
30905         if(this.isHorizontal){
30906             this._horizontalLayoutItems( items , isInstant );
30907             return;
30908         }
30909         
30910 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30911 //            this._verticalAlternativeLayoutItems( items , isInstant );
30912 //            return;
30913 //        }
30914         
30915         this._verticalLayoutItems( items , isInstant );
30916         
30917     },
30918     
30919     _verticalLayoutItems : function ( items , isInstant)
30920     {
30921         if ( !items || !items.length ) {
30922             return;
30923         }
30924         
30925         var standard = [
30926             ['xs', 'xs', 'xs', 'tall'],
30927             ['xs', 'xs', 'tall'],
30928             ['xs', 'xs', 'sm'],
30929             ['xs', 'xs', 'xs'],
30930             ['xs', 'tall'],
30931             ['xs', 'sm'],
30932             ['xs', 'xs'],
30933             ['xs'],
30934             
30935             ['sm', 'xs', 'xs'],
30936             ['sm', 'xs'],
30937             ['sm'],
30938             
30939             ['tall', 'xs', 'xs', 'xs'],
30940             ['tall', 'xs', 'xs'],
30941             ['tall', 'xs'],
30942             ['tall']
30943             
30944         ];
30945         
30946         var queue = [];
30947         
30948         var boxes = [];
30949         
30950         var box = [];
30951         
30952         Roo.each(items, function(item, k){
30953             
30954             switch (item.size) {
30955                 // these layouts take up a full box,
30956                 case 'md' :
30957                 case 'md-left' :
30958                 case 'md-right' :
30959                 case 'wide' :
30960                     
30961                     if(box.length){
30962                         boxes.push(box);
30963                         box = [];
30964                     }
30965                     
30966                     boxes.push([item]);
30967                     
30968                     break;
30969                     
30970                 case 'xs' :
30971                 case 'sm' :
30972                 case 'tall' :
30973                     
30974                     box.push(item);
30975                     
30976                     break;
30977                 default :
30978                     break;
30979                     
30980             }
30981             
30982         }, this);
30983         
30984         if(box.length){
30985             boxes.push(box);
30986             box = [];
30987         }
30988         
30989         var filterPattern = function(box, length)
30990         {
30991             if(!box.length){
30992                 return;
30993             }
30994             
30995             var match = false;
30996             
30997             var pattern = box.slice(0, length);
30998             
30999             var format = [];
31000             
31001             Roo.each(pattern, function(i){
31002                 format.push(i.size);
31003             }, this);
31004             
31005             Roo.each(standard, function(s){
31006                 
31007                 if(String(s) != String(format)){
31008                     return;
31009                 }
31010                 
31011                 match = true;
31012                 return false;
31013                 
31014             }, this);
31015             
31016             if(!match && length == 1){
31017                 return;
31018             }
31019             
31020             if(!match){
31021                 filterPattern(box, length - 1);
31022                 return;
31023             }
31024                 
31025             queue.push(pattern);
31026
31027             box = box.slice(length, box.length);
31028
31029             filterPattern(box, 4);
31030
31031             return;
31032             
31033         }
31034         
31035         Roo.each(boxes, function(box, k){
31036             
31037             if(!box.length){
31038                 return;
31039             }
31040             
31041             if(box.length == 1){
31042                 queue.push(box);
31043                 return;
31044             }
31045             
31046             filterPattern(box, 4);
31047             
31048         }, this);
31049         
31050         this._processVerticalLayoutQueue( queue, isInstant );
31051         
31052     },
31053     
31054 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31055 //    {
31056 //        if ( !items || !items.length ) {
31057 //            return;
31058 //        }
31059 //
31060 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31061 //        
31062 //    },
31063     
31064     _horizontalLayoutItems : function ( items , isInstant)
31065     {
31066         if ( !items || !items.length || items.length < 3) {
31067             return;
31068         }
31069         
31070         items.reverse();
31071         
31072         var eItems = items.slice(0, 3);
31073         
31074         items = items.slice(3, items.length);
31075         
31076         var standard = [
31077             ['xs', 'xs', 'xs', 'wide'],
31078             ['xs', 'xs', 'wide'],
31079             ['xs', 'xs', 'sm'],
31080             ['xs', 'xs', 'xs'],
31081             ['xs', 'wide'],
31082             ['xs', 'sm'],
31083             ['xs', 'xs'],
31084             ['xs'],
31085             
31086             ['sm', 'xs', 'xs'],
31087             ['sm', 'xs'],
31088             ['sm'],
31089             
31090             ['wide', 'xs', 'xs', 'xs'],
31091             ['wide', 'xs', 'xs'],
31092             ['wide', 'xs'],
31093             ['wide'],
31094             
31095             ['wide-thin']
31096         ];
31097         
31098         var queue = [];
31099         
31100         var boxes = [];
31101         
31102         var box = [];
31103         
31104         Roo.each(items, function(item, k){
31105             
31106             switch (item.size) {
31107                 case 'md' :
31108                 case 'md-left' :
31109                 case 'md-right' :
31110                 case 'tall' :
31111                     
31112                     if(box.length){
31113                         boxes.push(box);
31114                         box = [];
31115                     }
31116                     
31117                     boxes.push([item]);
31118                     
31119                     break;
31120                     
31121                 case 'xs' :
31122                 case 'sm' :
31123                 case 'wide' :
31124                 case 'wide-thin' :
31125                     
31126                     box.push(item);
31127                     
31128                     break;
31129                 default :
31130                     break;
31131                     
31132             }
31133             
31134         }, this);
31135         
31136         if(box.length){
31137             boxes.push(box);
31138             box = [];
31139         }
31140         
31141         var filterPattern = function(box, length)
31142         {
31143             if(!box.length){
31144                 return;
31145             }
31146             
31147             var match = false;
31148             
31149             var pattern = box.slice(0, length);
31150             
31151             var format = [];
31152             
31153             Roo.each(pattern, function(i){
31154                 format.push(i.size);
31155             }, this);
31156             
31157             Roo.each(standard, function(s){
31158                 
31159                 if(String(s) != String(format)){
31160                     return;
31161                 }
31162                 
31163                 match = true;
31164                 return false;
31165                 
31166             }, this);
31167             
31168             if(!match && length == 1){
31169                 return;
31170             }
31171             
31172             if(!match){
31173                 filterPattern(box, length - 1);
31174                 return;
31175             }
31176                 
31177             queue.push(pattern);
31178
31179             box = box.slice(length, box.length);
31180
31181             filterPattern(box, 4);
31182
31183             return;
31184             
31185         }
31186         
31187         Roo.each(boxes, function(box, k){
31188             
31189             if(!box.length){
31190                 return;
31191             }
31192             
31193             if(box.length == 1){
31194                 queue.push(box);
31195                 return;
31196             }
31197             
31198             filterPattern(box, 4);
31199             
31200         }, this);
31201         
31202         
31203         var prune = [];
31204         
31205         var pos = this.el.getBox(true);
31206         
31207         var minX = pos.x;
31208         
31209         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31210         
31211         var hit_end = false;
31212         
31213         Roo.each(queue, function(box){
31214             
31215             if(hit_end){
31216                 
31217                 Roo.each(box, function(b){
31218                 
31219                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31220                     b.el.hide();
31221
31222                 }, this);
31223
31224                 return;
31225             }
31226             
31227             var mx = 0;
31228             
31229             Roo.each(box, function(b){
31230                 
31231                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31232                 b.el.show();
31233
31234                 mx = Math.max(mx, b.x);
31235                 
31236             }, this);
31237             
31238             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31239             
31240             if(maxX < minX){
31241                 
31242                 Roo.each(box, function(b){
31243                 
31244                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31245                     b.el.hide();
31246                     
31247                 }, this);
31248                 
31249                 hit_end = true;
31250                 
31251                 return;
31252             }
31253             
31254             prune.push(box);
31255             
31256         }, this);
31257         
31258         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31259     },
31260     
31261     /** Sets position of item in DOM
31262     * @param {Element} item
31263     * @param {Number} x - horizontal position
31264     * @param {Number} y - vertical position
31265     * @param {Boolean} isInstant - disables transitions
31266     */
31267     _processVerticalLayoutQueue : function( queue, isInstant )
31268     {
31269         var pos = this.el.getBox(true);
31270         var x = pos.x;
31271         var y = pos.y;
31272         var maxY = [];
31273         
31274         for (var i = 0; i < this.cols; i++){
31275             maxY[i] = pos.y;
31276         }
31277         
31278         Roo.each(queue, function(box, k){
31279             
31280             var col = k % this.cols;
31281             
31282             Roo.each(box, function(b,kk){
31283                 
31284                 b.el.position('absolute');
31285                 
31286                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31287                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31288                 
31289                 if(b.size == 'md-left' || b.size == 'md-right'){
31290                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31291                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31292                 }
31293                 
31294                 b.el.setWidth(width);
31295                 b.el.setHeight(height);
31296                 // iframe?
31297                 b.el.select('iframe',true).setSize(width,height);
31298                 
31299             }, this);
31300             
31301             for (var i = 0; i < this.cols; i++){
31302                 
31303                 if(maxY[i] < maxY[col]){
31304                     col = i;
31305                     continue;
31306                 }
31307                 
31308                 col = Math.min(col, i);
31309                 
31310             }
31311             
31312             x = pos.x + col * (this.colWidth + this.padWidth);
31313             
31314             y = maxY[col];
31315             
31316             var positions = [];
31317             
31318             switch (box.length){
31319                 case 1 :
31320                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31321                     break;
31322                 case 2 :
31323                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31324                     break;
31325                 case 3 :
31326                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31327                     break;
31328                 case 4 :
31329                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31330                     break;
31331                 default :
31332                     break;
31333             }
31334             
31335             Roo.each(box, function(b,kk){
31336                 
31337                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31338                 
31339                 var sz = b.el.getSize();
31340                 
31341                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31342                 
31343             }, this);
31344             
31345         }, this);
31346         
31347         var mY = 0;
31348         
31349         for (var i = 0; i < this.cols; i++){
31350             mY = Math.max(mY, maxY[i]);
31351         }
31352         
31353         this.el.setHeight(mY - pos.y);
31354         
31355     },
31356     
31357 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31358 //    {
31359 //        var pos = this.el.getBox(true);
31360 //        var x = pos.x;
31361 //        var y = pos.y;
31362 //        var maxX = pos.right;
31363 //        
31364 //        var maxHeight = 0;
31365 //        
31366 //        Roo.each(items, function(item, k){
31367 //            
31368 //            var c = k % 2;
31369 //            
31370 //            item.el.position('absolute');
31371 //                
31372 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31373 //
31374 //            item.el.setWidth(width);
31375 //
31376 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31377 //
31378 //            item.el.setHeight(height);
31379 //            
31380 //            if(c == 0){
31381 //                item.el.setXY([x, y], isInstant ? false : true);
31382 //            } else {
31383 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31384 //            }
31385 //            
31386 //            y = y + height + this.alternativePadWidth;
31387 //            
31388 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31389 //            
31390 //        }, this);
31391 //        
31392 //        this.el.setHeight(maxHeight);
31393 //        
31394 //    },
31395     
31396     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31397     {
31398         var pos = this.el.getBox(true);
31399         
31400         var minX = pos.x;
31401         var minY = pos.y;
31402         
31403         var maxX = pos.right;
31404         
31405         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31406         
31407         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31408         
31409         Roo.each(queue, function(box, k){
31410             
31411             Roo.each(box, function(b, kk){
31412                 
31413                 b.el.position('absolute');
31414                 
31415                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31416                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31417                 
31418                 if(b.size == 'md-left' || b.size == 'md-right'){
31419                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31420                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31421                 }
31422                 
31423                 b.el.setWidth(width);
31424                 b.el.setHeight(height);
31425                 
31426             }, this);
31427             
31428             if(!box.length){
31429                 return;
31430             }
31431             
31432             var positions = [];
31433             
31434             switch (box.length){
31435                 case 1 :
31436                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31437                     break;
31438                 case 2 :
31439                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31440                     break;
31441                 case 3 :
31442                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31443                     break;
31444                 case 4 :
31445                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31446                     break;
31447                 default :
31448                     break;
31449             }
31450             
31451             Roo.each(box, function(b,kk){
31452                 
31453                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31454                 
31455                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31456                 
31457             }, this);
31458             
31459         }, this);
31460         
31461     },
31462     
31463     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31464     {
31465         Roo.each(eItems, function(b,k){
31466             
31467             b.size = (k == 0) ? 'sm' : 'xs';
31468             b.x = (k == 0) ? 2 : 1;
31469             b.y = (k == 0) ? 2 : 1;
31470             
31471             b.el.position('absolute');
31472             
31473             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31474                 
31475             b.el.setWidth(width);
31476             
31477             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31478             
31479             b.el.setHeight(height);
31480             
31481         }, this);
31482
31483         var positions = [];
31484         
31485         positions.push({
31486             x : maxX - this.unitWidth * 2 - this.gutter,
31487             y : minY
31488         });
31489         
31490         positions.push({
31491             x : maxX - this.unitWidth,
31492             y : minY + (this.unitWidth + this.gutter) * 2
31493         });
31494         
31495         positions.push({
31496             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31497             y : minY
31498         });
31499         
31500         Roo.each(eItems, function(b,k){
31501             
31502             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31503
31504         }, this);
31505         
31506     },
31507     
31508     getVerticalOneBoxColPositions : function(x, y, box)
31509     {
31510         var pos = [];
31511         
31512         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31513         
31514         if(box[0].size == 'md-left'){
31515             rand = 0;
31516         }
31517         
31518         if(box[0].size == 'md-right'){
31519             rand = 1;
31520         }
31521         
31522         pos.push({
31523             x : x + (this.unitWidth + this.gutter) * rand,
31524             y : y
31525         });
31526         
31527         return pos;
31528     },
31529     
31530     getVerticalTwoBoxColPositions : function(x, y, box)
31531     {
31532         var pos = [];
31533         
31534         if(box[0].size == 'xs'){
31535             
31536             pos.push({
31537                 x : x,
31538                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31539             });
31540
31541             pos.push({
31542                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31543                 y : y
31544             });
31545             
31546             return pos;
31547             
31548         }
31549         
31550         pos.push({
31551             x : x,
31552             y : y
31553         });
31554
31555         pos.push({
31556             x : x + (this.unitWidth + this.gutter) * 2,
31557             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31558         });
31559         
31560         return pos;
31561         
31562     },
31563     
31564     getVerticalThreeBoxColPositions : function(x, y, box)
31565     {
31566         var pos = [];
31567         
31568         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31569             
31570             pos.push({
31571                 x : x,
31572                 y : y
31573             });
31574
31575             pos.push({
31576                 x : x + (this.unitWidth + this.gutter) * 1,
31577                 y : y
31578             });
31579             
31580             pos.push({
31581                 x : x + (this.unitWidth + this.gutter) * 2,
31582                 y : y
31583             });
31584             
31585             return pos;
31586             
31587         }
31588         
31589         if(box[0].size == 'xs' && box[1].size == 'xs'){
31590             
31591             pos.push({
31592                 x : x,
31593                 y : y
31594             });
31595
31596             pos.push({
31597                 x : x,
31598                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31599             });
31600             
31601             pos.push({
31602                 x : x + (this.unitWidth + this.gutter) * 1,
31603                 y : y
31604             });
31605             
31606             return pos;
31607             
31608         }
31609         
31610         pos.push({
31611             x : x,
31612             y : y
31613         });
31614
31615         pos.push({
31616             x : x + (this.unitWidth + this.gutter) * 2,
31617             y : y
31618         });
31619
31620         pos.push({
31621             x : x + (this.unitWidth + this.gutter) * 2,
31622             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31623         });
31624             
31625         return pos;
31626         
31627     },
31628     
31629     getVerticalFourBoxColPositions : function(x, y, box)
31630     {
31631         var pos = [];
31632         
31633         if(box[0].size == 'xs'){
31634             
31635             pos.push({
31636                 x : x,
31637                 y : y
31638             });
31639
31640             pos.push({
31641                 x : x,
31642                 y : y + (this.unitHeight + this.gutter) * 1
31643             });
31644             
31645             pos.push({
31646                 x : x,
31647                 y : y + (this.unitHeight + this.gutter) * 2
31648             });
31649             
31650             pos.push({
31651                 x : x + (this.unitWidth + this.gutter) * 1,
31652                 y : y
31653             });
31654             
31655             return pos;
31656             
31657         }
31658         
31659         pos.push({
31660             x : x,
31661             y : y
31662         });
31663
31664         pos.push({
31665             x : x + (this.unitWidth + this.gutter) * 2,
31666             y : y
31667         });
31668
31669         pos.push({
31670             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31671             y : y + (this.unitHeight + this.gutter) * 1
31672         });
31673
31674         pos.push({
31675             x : x + (this.unitWidth + this.gutter) * 2,
31676             y : y + (this.unitWidth + this.gutter) * 2
31677         });
31678
31679         return pos;
31680         
31681     },
31682     
31683     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31684     {
31685         var pos = [];
31686         
31687         if(box[0].size == 'md-left'){
31688             pos.push({
31689                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31690                 y : minY
31691             });
31692             
31693             return pos;
31694         }
31695         
31696         if(box[0].size == 'md-right'){
31697             pos.push({
31698                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31699                 y : minY + (this.unitWidth + this.gutter) * 1
31700             });
31701             
31702             return pos;
31703         }
31704         
31705         var rand = Math.floor(Math.random() * (4 - box[0].y));
31706         
31707         pos.push({
31708             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31709             y : minY + (this.unitWidth + this.gutter) * rand
31710         });
31711         
31712         return pos;
31713         
31714     },
31715     
31716     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31717     {
31718         var pos = [];
31719         
31720         if(box[0].size == 'xs'){
31721             
31722             pos.push({
31723                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31724                 y : minY
31725             });
31726
31727             pos.push({
31728                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31729                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31730             });
31731             
31732             return pos;
31733             
31734         }
31735         
31736         pos.push({
31737             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31738             y : minY
31739         });
31740
31741         pos.push({
31742             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31743             y : minY + (this.unitWidth + this.gutter) * 2
31744         });
31745         
31746         return pos;
31747         
31748     },
31749     
31750     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31751     {
31752         var pos = [];
31753         
31754         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31755             
31756             pos.push({
31757                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31758                 y : minY
31759             });
31760
31761             pos.push({
31762                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31763                 y : minY + (this.unitWidth + this.gutter) * 1
31764             });
31765             
31766             pos.push({
31767                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31768                 y : minY + (this.unitWidth + this.gutter) * 2
31769             });
31770             
31771             return pos;
31772             
31773         }
31774         
31775         if(box[0].size == 'xs' && box[1].size == 'xs'){
31776             
31777             pos.push({
31778                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31779                 y : minY
31780             });
31781
31782             pos.push({
31783                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31784                 y : minY
31785             });
31786             
31787             pos.push({
31788                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31789                 y : minY + (this.unitWidth + this.gutter) * 1
31790             });
31791             
31792             return pos;
31793             
31794         }
31795         
31796         pos.push({
31797             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31798             y : minY
31799         });
31800
31801         pos.push({
31802             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31803             y : minY + (this.unitWidth + this.gutter) * 2
31804         });
31805
31806         pos.push({
31807             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31808             y : minY + (this.unitWidth + this.gutter) * 2
31809         });
31810             
31811         return pos;
31812         
31813     },
31814     
31815     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31816     {
31817         var pos = [];
31818         
31819         if(box[0].size == 'xs'){
31820             
31821             pos.push({
31822                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31823                 y : minY
31824             });
31825
31826             pos.push({
31827                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31828                 y : minY
31829             });
31830             
31831             pos.push({
31832                 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),
31833                 y : minY
31834             });
31835             
31836             pos.push({
31837                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31838                 y : minY + (this.unitWidth + this.gutter) * 1
31839             });
31840             
31841             return pos;
31842             
31843         }
31844         
31845         pos.push({
31846             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31847             y : minY
31848         });
31849         
31850         pos.push({
31851             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31852             y : minY + (this.unitWidth + this.gutter) * 2
31853         });
31854         
31855         pos.push({
31856             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31857             y : minY + (this.unitWidth + this.gutter) * 2
31858         });
31859         
31860         pos.push({
31861             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),
31862             y : minY + (this.unitWidth + this.gutter) * 2
31863         });
31864
31865         return pos;
31866         
31867     },
31868     
31869     /**
31870     * remove a Masonry Brick
31871     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31872     */
31873     removeBrick : function(brick_id)
31874     {
31875         if (!brick_id) {
31876             return;
31877         }
31878         
31879         for (var i = 0; i<this.bricks.length; i++) {
31880             if (this.bricks[i].id == brick_id) {
31881                 this.bricks.splice(i,1);
31882                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31883                 this.initial();
31884             }
31885         }
31886     },
31887     
31888     /**
31889     * adds a Masonry Brick
31890     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31891     */
31892     addBrick : function(cfg)
31893     {
31894         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31895         //this.register(cn);
31896         cn.parentId = this.id;
31897         cn.onRender(this.el, null);
31898         return cn;
31899     },
31900     
31901     /**
31902     * register a Masonry Brick
31903     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31904     */
31905     
31906     register : function(brick)
31907     {
31908         this.bricks.push(brick);
31909         brick.masonryId = this.id;
31910     },
31911     
31912     /**
31913     * clear all the Masonry Brick
31914     */
31915     clearAll : function()
31916     {
31917         this.bricks = [];
31918         //this.getChildContainer().dom.innerHTML = "";
31919         this.el.dom.innerHTML = '';
31920     },
31921     
31922     getSelected : function()
31923     {
31924         if (!this.selectedBrick) {
31925             return false;
31926         }
31927         
31928         return this.selectedBrick;
31929     }
31930 });
31931
31932 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31933     
31934     groups: {},
31935      /**
31936     * register a Masonry Layout
31937     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31938     */
31939     
31940     register : function(layout)
31941     {
31942         this.groups[layout.id] = layout;
31943     },
31944     /**
31945     * fetch a  Masonry Layout based on the masonry layout ID
31946     * @param {string} the masonry layout to add
31947     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31948     */
31949     
31950     get: function(layout_id) {
31951         if (typeof(this.groups[layout_id]) == 'undefined') {
31952             return false;
31953         }
31954         return this.groups[layout_id] ;
31955     }
31956     
31957     
31958     
31959 });
31960
31961  
31962
31963  /**
31964  *
31965  * This is based on 
31966  * http://masonry.desandro.com
31967  *
31968  * The idea is to render all the bricks based on vertical width...
31969  *
31970  * The original code extends 'outlayer' - we might need to use that....
31971  * 
31972  */
31973
31974
31975 /**
31976  * @class Roo.bootstrap.LayoutMasonryAuto
31977  * @extends Roo.bootstrap.Component
31978  * Bootstrap Layout Masonry class
31979  * 
31980  * @constructor
31981  * Create a new Element
31982  * @param {Object} config The config object
31983  */
31984
31985 Roo.bootstrap.LayoutMasonryAuto = function(config){
31986     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31987 };
31988
31989 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31990     
31991       /**
31992      * @cfg {Boolean} isFitWidth  - resize the width..
31993      */   
31994     isFitWidth : false,  // options..
31995     /**
31996      * @cfg {Boolean} isOriginLeft = left align?
31997      */   
31998     isOriginLeft : true,
31999     /**
32000      * @cfg {Boolean} isOriginTop = top align?
32001      */   
32002     isOriginTop : false,
32003     /**
32004      * @cfg {Boolean} isLayoutInstant = no animation?
32005      */   
32006     isLayoutInstant : false, // needed?
32007     /**
32008      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32009      */   
32010     isResizingContainer : true,
32011     /**
32012      * @cfg {Number} columnWidth  width of the columns 
32013      */   
32014     
32015     columnWidth : 0,
32016     
32017     /**
32018      * @cfg {Number} maxCols maximum number of columns
32019      */   
32020     
32021     maxCols: 0,
32022     /**
32023      * @cfg {Number} padHeight padding below box..
32024      */   
32025     
32026     padHeight : 10, 
32027     
32028     /**
32029      * @cfg {Boolean} isAutoInitial defalut true
32030      */   
32031     
32032     isAutoInitial : true, 
32033     
32034     // private?
32035     gutter : 0,
32036     
32037     containerWidth: 0,
32038     initialColumnWidth : 0,
32039     currentSize : null,
32040     
32041     colYs : null, // array.
32042     maxY : 0,
32043     padWidth: 10,
32044     
32045     
32046     tag: 'div',
32047     cls: '',
32048     bricks: null, //CompositeElement
32049     cols : 0, // array?
32050     // element : null, // wrapped now this.el
32051     _isLayoutInited : null, 
32052     
32053     
32054     getAutoCreate : function(){
32055         
32056         var cfg = {
32057             tag: this.tag,
32058             cls: 'blog-masonary-wrapper ' + this.cls,
32059             cn : {
32060                 cls : 'mas-boxes masonary'
32061             }
32062         };
32063         
32064         return cfg;
32065     },
32066     
32067     getChildContainer: function( )
32068     {
32069         if (this.boxesEl) {
32070             return this.boxesEl;
32071         }
32072         
32073         this.boxesEl = this.el.select('.mas-boxes').first();
32074         
32075         return this.boxesEl;
32076     },
32077     
32078     
32079     initEvents : function()
32080     {
32081         var _this = this;
32082         
32083         if(this.isAutoInitial){
32084             Roo.log('hook children rendered');
32085             this.on('childrenrendered', function() {
32086                 Roo.log('children rendered');
32087                 _this.initial();
32088             } ,this);
32089         }
32090         
32091     },
32092     
32093     initial : function()
32094     {
32095         this.reloadItems();
32096
32097         this.currentSize = this.el.getBox(true);
32098
32099         /// was window resize... - let's see if this works..
32100         Roo.EventManager.onWindowResize(this.resize, this); 
32101
32102         if(!this.isAutoInitial){
32103             this.layout();
32104             return;
32105         }
32106         
32107         this.layout.defer(500,this);
32108     },
32109     
32110     reloadItems: function()
32111     {
32112         this.bricks = this.el.select('.masonry-brick', true);
32113         
32114         this.bricks.each(function(b) {
32115             //Roo.log(b.getSize());
32116             if (!b.attr('originalwidth')) {
32117                 b.attr('originalwidth',  b.getSize().width);
32118             }
32119             
32120         });
32121         
32122         Roo.log(this.bricks.elements.length);
32123     },
32124     
32125     resize : function()
32126     {
32127         Roo.log('resize');
32128         var cs = this.el.getBox(true);
32129         
32130         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32131             Roo.log("no change in with or X");
32132             return;
32133         }
32134         this.currentSize = cs;
32135         this.layout();
32136     },
32137     
32138     layout : function()
32139     {
32140          Roo.log('layout');
32141         this._resetLayout();
32142         //this._manageStamps();
32143       
32144         // don't animate first layout
32145         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32146         this.layoutItems( isInstant );
32147       
32148         // flag for initalized
32149         this._isLayoutInited = true;
32150     },
32151     
32152     layoutItems : function( isInstant )
32153     {
32154         //var items = this._getItemsForLayout( this.items );
32155         // original code supports filtering layout items.. we just ignore it..
32156         
32157         this._layoutItems( this.bricks , isInstant );
32158       
32159         this._postLayout();
32160     },
32161     _layoutItems : function ( items , isInstant)
32162     {
32163        //this.fireEvent( 'layout', this, items );
32164     
32165
32166         if ( !items || !items.elements.length ) {
32167           // no items, emit event with empty array
32168             return;
32169         }
32170
32171         var queue = [];
32172         items.each(function(item) {
32173             Roo.log("layout item");
32174             Roo.log(item);
32175             // get x/y object from method
32176             var position = this._getItemLayoutPosition( item );
32177             // enqueue
32178             position.item = item;
32179             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32180             queue.push( position );
32181         }, this);
32182       
32183         this._processLayoutQueue( queue );
32184     },
32185     /** Sets position of item in DOM
32186     * @param {Element} item
32187     * @param {Number} x - horizontal position
32188     * @param {Number} y - vertical position
32189     * @param {Boolean} isInstant - disables transitions
32190     */
32191     _processLayoutQueue : function( queue )
32192     {
32193         for ( var i=0, len = queue.length; i < len; i++ ) {
32194             var obj = queue[i];
32195             obj.item.position('absolute');
32196             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32197         }
32198     },
32199       
32200     
32201     /**
32202     * Any logic you want to do after each layout,
32203     * i.e. size the container
32204     */
32205     _postLayout : function()
32206     {
32207         this.resizeContainer();
32208     },
32209     
32210     resizeContainer : function()
32211     {
32212         if ( !this.isResizingContainer ) {
32213             return;
32214         }
32215         var size = this._getContainerSize();
32216         if ( size ) {
32217             this.el.setSize(size.width,size.height);
32218             this.boxesEl.setSize(size.width,size.height);
32219         }
32220     },
32221     
32222     
32223     
32224     _resetLayout : function()
32225     {
32226         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32227         this.colWidth = this.el.getWidth();
32228         //this.gutter = this.el.getWidth(); 
32229         
32230         this.measureColumns();
32231
32232         // reset column Y
32233         var i = this.cols;
32234         this.colYs = [];
32235         while (i--) {
32236             this.colYs.push( 0 );
32237         }
32238     
32239         this.maxY = 0;
32240     },
32241
32242     measureColumns : function()
32243     {
32244         this.getContainerWidth();
32245       // if columnWidth is 0, default to outerWidth of first item
32246         if ( !this.columnWidth ) {
32247             var firstItem = this.bricks.first();
32248             Roo.log(firstItem);
32249             this.columnWidth  = this.containerWidth;
32250             if (firstItem && firstItem.attr('originalwidth') ) {
32251                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32252             }
32253             // columnWidth fall back to item of first element
32254             Roo.log("set column width?");
32255                         this.initialColumnWidth = this.columnWidth  ;
32256
32257             // if first elem has no width, default to size of container
32258             
32259         }
32260         
32261         
32262         if (this.initialColumnWidth) {
32263             this.columnWidth = this.initialColumnWidth;
32264         }
32265         
32266         
32267             
32268         // column width is fixed at the top - however if container width get's smaller we should
32269         // reduce it...
32270         
32271         // this bit calcs how man columns..
32272             
32273         var columnWidth = this.columnWidth += this.gutter;
32274       
32275         // calculate columns
32276         var containerWidth = this.containerWidth + this.gutter;
32277         
32278         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32279         // fix rounding errors, typically with gutters
32280         var excess = columnWidth - containerWidth % columnWidth;
32281         
32282         
32283         // if overshoot is less than a pixel, round up, otherwise floor it
32284         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32285         cols = Math[ mathMethod ]( cols );
32286         this.cols = Math.max( cols, 1 );
32287         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32288         
32289          // padding positioning..
32290         var totalColWidth = this.cols * this.columnWidth;
32291         var padavail = this.containerWidth - totalColWidth;
32292         // so for 2 columns - we need 3 'pads'
32293         
32294         var padNeeded = (1+this.cols) * this.padWidth;
32295         
32296         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32297         
32298         this.columnWidth += padExtra
32299         //this.padWidth = Math.floor(padavail /  ( this.cols));
32300         
32301         // adjust colum width so that padding is fixed??
32302         
32303         // we have 3 columns ... total = width * 3
32304         // we have X left over... that should be used by 
32305         
32306         //if (this.expandC) {
32307             
32308         //}
32309         
32310         
32311         
32312     },
32313     
32314     getContainerWidth : function()
32315     {
32316        /* // container is parent if fit width
32317         var container = this.isFitWidth ? this.element.parentNode : this.element;
32318         // check that this.size and size are there
32319         // IE8 triggers resize on body size change, so they might not be
32320         
32321         var size = getSize( container );  //FIXME
32322         this.containerWidth = size && size.innerWidth; //FIXME
32323         */
32324          
32325         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32326         
32327     },
32328     
32329     _getItemLayoutPosition : function( item )  // what is item?
32330     {
32331         // we resize the item to our columnWidth..
32332       
32333         item.setWidth(this.columnWidth);
32334         item.autoBoxAdjust  = false;
32335         
32336         var sz = item.getSize();
32337  
32338         // how many columns does this brick span
32339         var remainder = this.containerWidth % this.columnWidth;
32340         
32341         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32342         // round if off by 1 pixel, otherwise use ceil
32343         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32344         colSpan = Math.min( colSpan, this.cols );
32345         
32346         // normally this should be '1' as we dont' currently allow multi width columns..
32347         
32348         var colGroup = this._getColGroup( colSpan );
32349         // get the minimum Y value from the columns
32350         var minimumY = Math.min.apply( Math, colGroup );
32351         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32352         
32353         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32354          
32355         // position the brick
32356         var position = {
32357             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32358             y: this.currentSize.y + minimumY + this.padHeight
32359         };
32360         
32361         Roo.log(position);
32362         // apply setHeight to necessary columns
32363         var setHeight = minimumY + sz.height + this.padHeight;
32364         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32365         
32366         var setSpan = this.cols + 1 - colGroup.length;
32367         for ( var i = 0; i < setSpan; i++ ) {
32368           this.colYs[ shortColIndex + i ] = setHeight ;
32369         }
32370       
32371         return position;
32372     },
32373     
32374     /**
32375      * @param {Number} colSpan - number of columns the element spans
32376      * @returns {Array} colGroup
32377      */
32378     _getColGroup : function( colSpan )
32379     {
32380         if ( colSpan < 2 ) {
32381           // if brick spans only one column, use all the column Ys
32382           return this.colYs;
32383         }
32384       
32385         var colGroup = [];
32386         // how many different places could this brick fit horizontally
32387         var groupCount = this.cols + 1 - colSpan;
32388         // for each group potential horizontal position
32389         for ( var i = 0; i < groupCount; i++ ) {
32390           // make an array of colY values for that one group
32391           var groupColYs = this.colYs.slice( i, i + colSpan );
32392           // and get the max value of the array
32393           colGroup[i] = Math.max.apply( Math, groupColYs );
32394         }
32395         return colGroup;
32396     },
32397     /*
32398     _manageStamp : function( stamp )
32399     {
32400         var stampSize =  stamp.getSize();
32401         var offset = stamp.getBox();
32402         // get the columns that this stamp affects
32403         var firstX = this.isOriginLeft ? offset.x : offset.right;
32404         var lastX = firstX + stampSize.width;
32405         var firstCol = Math.floor( firstX / this.columnWidth );
32406         firstCol = Math.max( 0, firstCol );
32407         
32408         var lastCol = Math.floor( lastX / this.columnWidth );
32409         // lastCol should not go over if multiple of columnWidth #425
32410         lastCol -= lastX % this.columnWidth ? 0 : 1;
32411         lastCol = Math.min( this.cols - 1, lastCol );
32412         
32413         // set colYs to bottom of the stamp
32414         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32415             stampSize.height;
32416             
32417         for ( var i = firstCol; i <= lastCol; i++ ) {
32418           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32419         }
32420     },
32421     */
32422     
32423     _getContainerSize : function()
32424     {
32425         this.maxY = Math.max.apply( Math, this.colYs );
32426         var size = {
32427             height: this.maxY
32428         };
32429       
32430         if ( this.isFitWidth ) {
32431             size.width = this._getContainerFitWidth();
32432         }
32433       
32434         return size;
32435     },
32436     
32437     _getContainerFitWidth : function()
32438     {
32439         var unusedCols = 0;
32440         // count unused columns
32441         var i = this.cols;
32442         while ( --i ) {
32443           if ( this.colYs[i] !== 0 ) {
32444             break;
32445           }
32446           unusedCols++;
32447         }
32448         // fit container to columns that have been used
32449         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32450     },
32451     
32452     needsResizeLayout : function()
32453     {
32454         var previousWidth = this.containerWidth;
32455         this.getContainerWidth();
32456         return previousWidth !== this.containerWidth;
32457     }
32458  
32459 });
32460
32461  
32462
32463  /*
32464  * - LGPL
32465  *
32466  * element
32467  * 
32468  */
32469
32470 /**
32471  * @class Roo.bootstrap.MasonryBrick
32472  * @extends Roo.bootstrap.Component
32473  * Bootstrap MasonryBrick class
32474  * 
32475  * @constructor
32476  * Create a new MasonryBrick
32477  * @param {Object} config The config object
32478  */
32479
32480 Roo.bootstrap.MasonryBrick = function(config){
32481     
32482     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32483     
32484     Roo.bootstrap.MasonryBrick.register(this);
32485     
32486     this.addEvents({
32487         // raw events
32488         /**
32489          * @event click
32490          * When a MasonryBrick is clcik
32491          * @param {Roo.bootstrap.MasonryBrick} this
32492          * @param {Roo.EventObject} e
32493          */
32494         "click" : true
32495     });
32496 };
32497
32498 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32499     
32500     /**
32501      * @cfg {String} title
32502      */   
32503     title : '',
32504     /**
32505      * @cfg {String} html
32506      */   
32507     html : '',
32508     /**
32509      * @cfg {String} bgimage
32510      */   
32511     bgimage : '',
32512     /**
32513      * @cfg {String} videourl
32514      */   
32515     videourl : '',
32516     /**
32517      * @cfg {String} cls
32518      */   
32519     cls : '',
32520     /**
32521      * @cfg {String} href
32522      */   
32523     href : '',
32524     /**
32525      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32526      */   
32527     size : 'xs',
32528     
32529     /**
32530      * @cfg {String} placetitle (center|bottom)
32531      */   
32532     placetitle : '',
32533     
32534     /**
32535      * @cfg {Boolean} isFitContainer defalut true
32536      */   
32537     isFitContainer : true, 
32538     
32539     /**
32540      * @cfg {Boolean} preventDefault defalut false
32541      */   
32542     preventDefault : false, 
32543     
32544     /**
32545      * @cfg {Boolean} inverse defalut false
32546      */   
32547     maskInverse : false, 
32548     
32549     getAutoCreate : function()
32550     {
32551         if(!this.isFitContainer){
32552             return this.getSplitAutoCreate();
32553         }
32554         
32555         var cls = 'masonry-brick masonry-brick-full';
32556         
32557         if(this.href.length){
32558             cls += ' masonry-brick-link';
32559         }
32560         
32561         if(this.bgimage.length){
32562             cls += ' masonry-brick-image';
32563         }
32564         
32565         if(this.maskInverse){
32566             cls += ' mask-inverse';
32567         }
32568         
32569         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32570             cls += ' enable-mask';
32571         }
32572         
32573         if(this.size){
32574             cls += ' masonry-' + this.size + '-brick';
32575         }
32576         
32577         if(this.placetitle.length){
32578             
32579             switch (this.placetitle) {
32580                 case 'center' :
32581                     cls += ' masonry-center-title';
32582                     break;
32583                 case 'bottom' :
32584                     cls += ' masonry-bottom-title';
32585                     break;
32586                 default:
32587                     break;
32588             }
32589             
32590         } else {
32591             if(!this.html.length && !this.bgimage.length){
32592                 cls += ' masonry-center-title';
32593             }
32594
32595             if(!this.html.length && this.bgimage.length){
32596                 cls += ' masonry-bottom-title';
32597             }
32598         }
32599         
32600         if(this.cls){
32601             cls += ' ' + this.cls;
32602         }
32603         
32604         var cfg = {
32605             tag: (this.href.length) ? 'a' : 'div',
32606             cls: cls,
32607             cn: [
32608                 {
32609                     tag: 'div',
32610                     cls: 'masonry-brick-mask'
32611                 },
32612                 {
32613                     tag: 'div',
32614                     cls: 'masonry-brick-paragraph',
32615                     cn: []
32616                 }
32617             ]
32618         };
32619         
32620         if(this.href.length){
32621             cfg.href = this.href;
32622         }
32623         
32624         var cn = cfg.cn[1].cn;
32625         
32626         if(this.title.length){
32627             cn.push({
32628                 tag: 'h4',
32629                 cls: 'masonry-brick-title',
32630                 html: this.title
32631             });
32632         }
32633         
32634         if(this.html.length){
32635             cn.push({
32636                 tag: 'p',
32637                 cls: 'masonry-brick-text',
32638                 html: this.html
32639             });
32640         }
32641         
32642         if (!this.title.length && !this.html.length) {
32643             cfg.cn[1].cls += ' hide';
32644         }
32645         
32646         if(this.bgimage.length){
32647             cfg.cn.push({
32648                 tag: 'img',
32649                 cls: 'masonry-brick-image-view',
32650                 src: this.bgimage
32651             });
32652         }
32653         
32654         if(this.videourl.length){
32655             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32656             // youtube support only?
32657             cfg.cn.push({
32658                 tag: 'iframe',
32659                 cls: 'masonry-brick-image-view',
32660                 src: vurl,
32661                 frameborder : 0,
32662                 allowfullscreen : true
32663             });
32664         }
32665         
32666         return cfg;
32667         
32668     },
32669     
32670     getSplitAutoCreate : function()
32671     {
32672         var cls = 'masonry-brick masonry-brick-split';
32673         
32674         if(this.href.length){
32675             cls += ' masonry-brick-link';
32676         }
32677         
32678         if(this.bgimage.length){
32679             cls += ' masonry-brick-image';
32680         }
32681         
32682         if(this.size){
32683             cls += ' masonry-' + this.size + '-brick';
32684         }
32685         
32686         switch (this.placetitle) {
32687             case 'center' :
32688                 cls += ' masonry-center-title';
32689                 break;
32690             case 'bottom' :
32691                 cls += ' masonry-bottom-title';
32692                 break;
32693             default:
32694                 if(!this.bgimage.length){
32695                     cls += ' masonry-center-title';
32696                 }
32697
32698                 if(this.bgimage.length){
32699                     cls += ' masonry-bottom-title';
32700                 }
32701                 break;
32702         }
32703         
32704         if(this.cls){
32705             cls += ' ' + this.cls;
32706         }
32707         
32708         var cfg = {
32709             tag: (this.href.length) ? 'a' : 'div',
32710             cls: cls,
32711             cn: [
32712                 {
32713                     tag: 'div',
32714                     cls: 'masonry-brick-split-head',
32715                     cn: [
32716                         {
32717                             tag: 'div',
32718                             cls: 'masonry-brick-paragraph',
32719                             cn: []
32720                         }
32721                     ]
32722                 },
32723                 {
32724                     tag: 'div',
32725                     cls: 'masonry-brick-split-body',
32726                     cn: []
32727                 }
32728             ]
32729         };
32730         
32731         if(this.href.length){
32732             cfg.href = this.href;
32733         }
32734         
32735         if(this.title.length){
32736             cfg.cn[0].cn[0].cn.push({
32737                 tag: 'h4',
32738                 cls: 'masonry-brick-title',
32739                 html: this.title
32740             });
32741         }
32742         
32743         if(this.html.length){
32744             cfg.cn[1].cn.push({
32745                 tag: 'p',
32746                 cls: 'masonry-brick-text',
32747                 html: this.html
32748             });
32749         }
32750
32751         if(this.bgimage.length){
32752             cfg.cn[0].cn.push({
32753                 tag: 'img',
32754                 cls: 'masonry-brick-image-view',
32755                 src: this.bgimage
32756             });
32757         }
32758         
32759         if(this.videourl.length){
32760             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32761             // youtube support only?
32762             cfg.cn[0].cn.cn.push({
32763                 tag: 'iframe',
32764                 cls: 'masonry-brick-image-view',
32765                 src: vurl,
32766                 frameborder : 0,
32767                 allowfullscreen : true
32768             });
32769         }
32770         
32771         return cfg;
32772     },
32773     
32774     initEvents: function() 
32775     {
32776         switch (this.size) {
32777             case 'xs' :
32778                 this.x = 1;
32779                 this.y = 1;
32780                 break;
32781             case 'sm' :
32782                 this.x = 2;
32783                 this.y = 2;
32784                 break;
32785             case 'md' :
32786             case 'md-left' :
32787             case 'md-right' :
32788                 this.x = 3;
32789                 this.y = 3;
32790                 break;
32791             case 'tall' :
32792                 this.x = 2;
32793                 this.y = 3;
32794                 break;
32795             case 'wide' :
32796                 this.x = 3;
32797                 this.y = 2;
32798                 break;
32799             case 'wide-thin' :
32800                 this.x = 3;
32801                 this.y = 1;
32802                 break;
32803                         
32804             default :
32805                 break;
32806         }
32807         
32808         if(Roo.isTouch){
32809             this.el.on('touchstart', this.onTouchStart, this);
32810             this.el.on('touchmove', this.onTouchMove, this);
32811             this.el.on('touchend', this.onTouchEnd, this);
32812             this.el.on('contextmenu', this.onContextMenu, this);
32813         } else {
32814             this.el.on('mouseenter'  ,this.enter, this);
32815             this.el.on('mouseleave', this.leave, this);
32816             this.el.on('click', this.onClick, this);
32817         }
32818         
32819         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32820             this.parent().bricks.push(this);   
32821         }
32822         
32823     },
32824     
32825     onClick: function(e, el)
32826     {
32827         var time = this.endTimer - this.startTimer;
32828         // Roo.log(e.preventDefault());
32829         if(Roo.isTouch){
32830             if(time > 1000){
32831                 e.preventDefault();
32832                 return;
32833             }
32834         }
32835         
32836         if(!this.preventDefault){
32837             return;
32838         }
32839         
32840         e.preventDefault();
32841         
32842         if (this.activeClass != '') {
32843             this.selectBrick();
32844         }
32845         
32846         this.fireEvent('click', this, e);
32847     },
32848     
32849     enter: function(e, el)
32850     {
32851         e.preventDefault();
32852         
32853         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32854             return;
32855         }
32856         
32857         if(this.bgimage.length && this.html.length){
32858             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32859         }
32860     },
32861     
32862     leave: function(e, el)
32863     {
32864         e.preventDefault();
32865         
32866         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32867             return;
32868         }
32869         
32870         if(this.bgimage.length && this.html.length){
32871             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32872         }
32873     },
32874     
32875     onTouchStart: function(e, el)
32876     {
32877 //        e.preventDefault();
32878         
32879         this.touchmoved = false;
32880         
32881         if(!this.isFitContainer){
32882             return;
32883         }
32884         
32885         if(!this.bgimage.length || !this.html.length){
32886             return;
32887         }
32888         
32889         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32890         
32891         this.timer = new Date().getTime();
32892         
32893     },
32894     
32895     onTouchMove: function(e, el)
32896     {
32897         this.touchmoved = true;
32898     },
32899     
32900     onContextMenu : function(e,el)
32901     {
32902         e.preventDefault();
32903         e.stopPropagation();
32904         return false;
32905     },
32906     
32907     onTouchEnd: function(e, el)
32908     {
32909 //        e.preventDefault();
32910         
32911         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32912         
32913             this.leave(e,el);
32914             
32915             return;
32916         }
32917         
32918         if(!this.bgimage.length || !this.html.length){
32919             
32920             if(this.href.length){
32921                 window.location.href = this.href;
32922             }
32923             
32924             return;
32925         }
32926         
32927         if(!this.isFitContainer){
32928             return;
32929         }
32930         
32931         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32932         
32933         window.location.href = this.href;
32934     },
32935     
32936     //selection on single brick only
32937     selectBrick : function() {
32938         
32939         if (!this.parentId) {
32940             return;
32941         }
32942         
32943         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32944         var index = m.selectedBrick.indexOf(this.id);
32945         
32946         if ( index > -1) {
32947             m.selectedBrick.splice(index,1);
32948             this.el.removeClass(this.activeClass);
32949             return;
32950         }
32951         
32952         for(var i = 0; i < m.selectedBrick.length; i++) {
32953             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32954             b.el.removeClass(b.activeClass);
32955         }
32956         
32957         m.selectedBrick = [];
32958         
32959         m.selectedBrick.push(this.id);
32960         this.el.addClass(this.activeClass);
32961         return;
32962     },
32963     
32964     isSelected : function(){
32965         return this.el.hasClass(this.activeClass);
32966         
32967     }
32968 });
32969
32970 Roo.apply(Roo.bootstrap.MasonryBrick, {
32971     
32972     //groups: {},
32973     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32974      /**
32975     * register a Masonry Brick
32976     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32977     */
32978     
32979     register : function(brick)
32980     {
32981         //this.groups[brick.id] = brick;
32982         this.groups.add(brick.id, brick);
32983     },
32984     /**
32985     * fetch a  masonry brick based on the masonry brick ID
32986     * @param {string} the masonry brick to add
32987     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32988     */
32989     
32990     get: function(brick_id) 
32991     {
32992         // if (typeof(this.groups[brick_id]) == 'undefined') {
32993         //     return false;
32994         // }
32995         // return this.groups[brick_id] ;
32996         
32997         if(this.groups.key(brick_id)) {
32998             return this.groups.key(brick_id);
32999         }
33000         
33001         return false;
33002     }
33003     
33004     
33005     
33006 });
33007
33008  /*
33009  * - LGPL
33010  *
33011  * element
33012  * 
33013  */
33014
33015 /**
33016  * @class Roo.bootstrap.Brick
33017  * @extends Roo.bootstrap.Component
33018  * Bootstrap Brick class
33019  * 
33020  * @constructor
33021  * Create a new Brick
33022  * @param {Object} config The config object
33023  */
33024
33025 Roo.bootstrap.Brick = function(config){
33026     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33027     
33028     this.addEvents({
33029         // raw events
33030         /**
33031          * @event click
33032          * When a Brick is click
33033          * @param {Roo.bootstrap.Brick} this
33034          * @param {Roo.EventObject} e
33035          */
33036         "click" : true
33037     });
33038 };
33039
33040 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33041     
33042     /**
33043      * @cfg {String} title
33044      */   
33045     title : '',
33046     /**
33047      * @cfg {String} html
33048      */   
33049     html : '',
33050     /**
33051      * @cfg {String} bgimage
33052      */   
33053     bgimage : '',
33054     /**
33055      * @cfg {String} cls
33056      */   
33057     cls : '',
33058     /**
33059      * @cfg {String} href
33060      */   
33061     href : '',
33062     /**
33063      * @cfg {String} video
33064      */   
33065     video : '',
33066     /**
33067      * @cfg {Boolean} square
33068      */   
33069     square : true,
33070     
33071     getAutoCreate : function()
33072     {
33073         var cls = 'roo-brick';
33074         
33075         if(this.href.length){
33076             cls += ' roo-brick-link';
33077         }
33078         
33079         if(this.bgimage.length){
33080             cls += ' roo-brick-image';
33081         }
33082         
33083         if(!this.html.length && !this.bgimage.length){
33084             cls += ' roo-brick-center-title';
33085         }
33086         
33087         if(!this.html.length && this.bgimage.length){
33088             cls += ' roo-brick-bottom-title';
33089         }
33090         
33091         if(this.cls){
33092             cls += ' ' + this.cls;
33093         }
33094         
33095         var cfg = {
33096             tag: (this.href.length) ? 'a' : 'div',
33097             cls: cls,
33098             cn: [
33099                 {
33100                     tag: 'div',
33101                     cls: 'roo-brick-paragraph',
33102                     cn: []
33103                 }
33104             ]
33105         };
33106         
33107         if(this.href.length){
33108             cfg.href = this.href;
33109         }
33110         
33111         var cn = cfg.cn[0].cn;
33112         
33113         if(this.title.length){
33114             cn.push({
33115                 tag: 'h4',
33116                 cls: 'roo-brick-title',
33117                 html: this.title
33118             });
33119         }
33120         
33121         if(this.html.length){
33122             cn.push({
33123                 tag: 'p',
33124                 cls: 'roo-brick-text',
33125                 html: this.html
33126             });
33127         } else {
33128             cn.cls += ' hide';
33129         }
33130         
33131         if(this.bgimage.length){
33132             cfg.cn.push({
33133                 tag: 'img',
33134                 cls: 'roo-brick-image-view',
33135                 src: this.bgimage
33136             });
33137         }
33138         
33139         return cfg;
33140     },
33141     
33142     initEvents: function() 
33143     {
33144         if(this.title.length || this.html.length){
33145             this.el.on('mouseenter'  ,this.enter, this);
33146             this.el.on('mouseleave', this.leave, this);
33147         }
33148         
33149         Roo.EventManager.onWindowResize(this.resize, this); 
33150         
33151         if(this.bgimage.length){
33152             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33153             this.imageEl.on('load', this.onImageLoad, this);
33154             return;
33155         }
33156         
33157         this.resize();
33158     },
33159     
33160     onImageLoad : function()
33161     {
33162         this.resize();
33163     },
33164     
33165     resize : function()
33166     {
33167         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33168         
33169         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33170         
33171         if(this.bgimage.length){
33172             var image = this.el.select('.roo-brick-image-view', true).first();
33173             
33174             image.setWidth(paragraph.getWidth());
33175             
33176             if(this.square){
33177                 image.setHeight(paragraph.getWidth());
33178             }
33179             
33180             this.el.setHeight(image.getHeight());
33181             paragraph.setHeight(image.getHeight());
33182             
33183         }
33184         
33185     },
33186     
33187     enter: function(e, el)
33188     {
33189         e.preventDefault();
33190         
33191         if(this.bgimage.length){
33192             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33193             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33194         }
33195     },
33196     
33197     leave: function(e, el)
33198     {
33199         e.preventDefault();
33200         
33201         if(this.bgimage.length){
33202             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33203             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33204         }
33205     }
33206     
33207 });
33208
33209  
33210
33211  /*
33212  * - LGPL
33213  *
33214  * Number field 
33215  */
33216
33217 /**
33218  * @class Roo.bootstrap.NumberField
33219  * @extends Roo.bootstrap.Input
33220  * Bootstrap NumberField class
33221  * 
33222  * 
33223  * 
33224  * 
33225  * @constructor
33226  * Create a new NumberField
33227  * @param {Object} config The config object
33228  */
33229
33230 Roo.bootstrap.NumberField = function(config){
33231     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33232 };
33233
33234 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33235     
33236     /**
33237      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33238      */
33239     allowDecimals : true,
33240     /**
33241      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33242      */
33243     decimalSeparator : ".",
33244     /**
33245      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33246      */
33247     decimalPrecision : 2,
33248     /**
33249      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33250      */
33251     allowNegative : true,
33252     
33253     /**
33254      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33255      */
33256     allowZero: true,
33257     /**
33258      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33259      */
33260     minValue : Number.NEGATIVE_INFINITY,
33261     /**
33262      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33263      */
33264     maxValue : Number.MAX_VALUE,
33265     /**
33266      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33267      */
33268     minText : "The minimum value for this field is {0}",
33269     /**
33270      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33271      */
33272     maxText : "The maximum value for this field is {0}",
33273     /**
33274      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33275      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33276      */
33277     nanText : "{0} is not a valid number",
33278     /**
33279      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33280      */
33281     thousandsDelimiter : false,
33282     /**
33283      * @cfg {String} valueAlign alignment of value
33284      */
33285     valueAlign : "left",
33286
33287     getAutoCreate : function()
33288     {
33289         var hiddenInput = {
33290             tag: 'input',
33291             type: 'hidden',
33292             id: Roo.id(),
33293             cls: 'hidden-number-input'
33294         };
33295         
33296         if (this.name) {
33297             hiddenInput.name = this.name;
33298         }
33299         
33300         this.name = '';
33301         
33302         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33303         
33304         this.name = hiddenInput.name;
33305         
33306         if(cfg.cn.length > 0) {
33307             cfg.cn.push(hiddenInput);
33308         }
33309         
33310         return cfg;
33311     },
33312
33313     // private
33314     initEvents : function()
33315     {   
33316         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33317         
33318         var allowed = "0123456789";
33319         
33320         if(this.allowDecimals){
33321             allowed += this.decimalSeparator;
33322         }
33323         
33324         if(this.allowNegative){
33325             allowed += "-";
33326         }
33327         
33328         if(this.thousandsDelimiter) {
33329             allowed += ",";
33330         }
33331         
33332         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33333         
33334         var keyPress = function(e){
33335             
33336             var k = e.getKey();
33337             
33338             var c = e.getCharCode();
33339             
33340             if(
33341                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33342                     allowed.indexOf(String.fromCharCode(c)) === -1
33343             ){
33344                 e.stopEvent();
33345                 return;
33346             }
33347             
33348             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33349                 return;
33350             }
33351             
33352             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33353                 e.stopEvent();
33354             }
33355         };
33356         
33357         this.el.on("keypress", keyPress, this);
33358     },
33359     
33360     validateValue : function(value)
33361     {
33362         
33363         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33364             return false;
33365         }
33366         
33367         var num = this.parseValue(value);
33368         
33369         if(isNaN(num)){
33370             this.markInvalid(String.format(this.nanText, value));
33371             return false;
33372         }
33373         
33374         if(num < this.minValue){
33375             this.markInvalid(String.format(this.minText, this.minValue));
33376             return false;
33377         }
33378         
33379         if(num > this.maxValue){
33380             this.markInvalid(String.format(this.maxText, this.maxValue));
33381             return false;
33382         }
33383         
33384         return true;
33385     },
33386
33387     getValue : function()
33388     {
33389         var v = this.hiddenEl().getValue();
33390         
33391         return this.fixPrecision(this.parseValue(v));
33392     },
33393
33394     parseValue : function(value)
33395     {
33396         if(this.thousandsDelimiter) {
33397             value += "";
33398             r = new RegExp(",", "g");
33399             value = value.replace(r, "");
33400         }
33401         
33402         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33403         return isNaN(value) ? '' : value;
33404     },
33405
33406     fixPrecision : function(value)
33407     {
33408         if(this.thousandsDelimiter) {
33409             value += "";
33410             r = new RegExp(",", "g");
33411             value = value.replace(r, "");
33412         }
33413         
33414         var nan = isNaN(value);
33415         
33416         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33417             return nan ? '' : value;
33418         }
33419         return parseFloat(value).toFixed(this.decimalPrecision);
33420     },
33421
33422     setValue : function(v)
33423     {
33424         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33425         
33426         this.value = v;
33427         
33428         if(this.rendered){
33429             
33430             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33431             
33432             this.inputEl().dom.value = (v == '') ? '' :
33433                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33434             
33435             if(!this.allowZero && v === '0') {
33436                 this.hiddenEl().dom.value = '';
33437                 this.inputEl().dom.value = '';
33438             }
33439             
33440             this.validate();
33441         }
33442     },
33443
33444     decimalPrecisionFcn : function(v)
33445     {
33446         return Math.floor(v);
33447     },
33448
33449     beforeBlur : function()
33450     {
33451         var v = this.parseValue(this.getRawValue());
33452         
33453         if(v || v === 0 || v === ''){
33454             this.setValue(v);
33455         }
33456     },
33457     
33458     hiddenEl : function()
33459     {
33460         return this.el.select('input.hidden-number-input',true).first();
33461     }
33462     
33463 });
33464
33465  
33466
33467 /*
33468 * Licence: LGPL
33469 */
33470
33471 /**
33472  * @class Roo.bootstrap.DocumentSlider
33473  * @extends Roo.bootstrap.Component
33474  * Bootstrap DocumentSlider class
33475  * 
33476  * @constructor
33477  * Create a new DocumentViewer
33478  * @param {Object} config The config object
33479  */
33480
33481 Roo.bootstrap.DocumentSlider = function(config){
33482     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33483     
33484     this.files = [];
33485     
33486     this.addEvents({
33487         /**
33488          * @event initial
33489          * Fire after initEvent
33490          * @param {Roo.bootstrap.DocumentSlider} this
33491          */
33492         "initial" : true,
33493         /**
33494          * @event update
33495          * Fire after update
33496          * @param {Roo.bootstrap.DocumentSlider} this
33497          */
33498         "update" : true,
33499         /**
33500          * @event click
33501          * Fire after click
33502          * @param {Roo.bootstrap.DocumentSlider} this
33503          */
33504         "click" : true
33505     });
33506 };
33507
33508 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33509     
33510     files : false,
33511     
33512     indicator : 0,
33513     
33514     getAutoCreate : function()
33515     {
33516         var cfg = {
33517             tag : 'div',
33518             cls : 'roo-document-slider',
33519             cn : [
33520                 {
33521                     tag : 'div',
33522                     cls : 'roo-document-slider-header',
33523                     cn : [
33524                         {
33525                             tag : 'div',
33526                             cls : 'roo-document-slider-header-title'
33527                         }
33528                     ]
33529                 },
33530                 {
33531                     tag : 'div',
33532                     cls : 'roo-document-slider-body',
33533                     cn : [
33534                         {
33535                             tag : 'div',
33536                             cls : 'roo-document-slider-prev',
33537                             cn : [
33538                                 {
33539                                     tag : 'i',
33540                                     cls : 'fa fa-chevron-left'
33541                                 }
33542                             ]
33543                         },
33544                         {
33545                             tag : 'div',
33546                             cls : 'roo-document-slider-thumb',
33547                             cn : [
33548                                 {
33549                                     tag : 'img',
33550                                     cls : 'roo-document-slider-image'
33551                                 }
33552                             ]
33553                         },
33554                         {
33555                             tag : 'div',
33556                             cls : 'roo-document-slider-next',
33557                             cn : [
33558                                 {
33559                                     tag : 'i',
33560                                     cls : 'fa fa-chevron-right'
33561                                 }
33562                             ]
33563                         }
33564                     ]
33565                 }
33566             ]
33567         };
33568         
33569         return cfg;
33570     },
33571     
33572     initEvents : function()
33573     {
33574         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33575         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33576         
33577         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33578         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33579         
33580         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33581         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33582         
33583         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33584         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33585         
33586         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33587         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33588         
33589         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33590         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33591         
33592         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33593         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33594         
33595         this.thumbEl.on('click', this.onClick, this);
33596         
33597         this.prevIndicator.on('click', this.prev, this);
33598         
33599         this.nextIndicator.on('click', this.next, this);
33600         
33601     },
33602     
33603     initial : function()
33604     {
33605         if(this.files.length){
33606             this.indicator = 1;
33607             this.update()
33608         }
33609         
33610         this.fireEvent('initial', this);
33611     },
33612     
33613     update : function()
33614     {
33615         this.imageEl.attr('src', this.files[this.indicator - 1]);
33616         
33617         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33618         
33619         this.prevIndicator.show();
33620         
33621         if(this.indicator == 1){
33622             this.prevIndicator.hide();
33623         }
33624         
33625         this.nextIndicator.show();
33626         
33627         if(this.indicator == this.files.length){
33628             this.nextIndicator.hide();
33629         }
33630         
33631         this.thumbEl.scrollTo('top');
33632         
33633         this.fireEvent('update', this);
33634     },
33635     
33636     onClick : function(e)
33637     {
33638         e.preventDefault();
33639         
33640         this.fireEvent('click', this);
33641     },
33642     
33643     prev : function(e)
33644     {
33645         e.preventDefault();
33646         
33647         this.indicator = Math.max(1, this.indicator - 1);
33648         
33649         this.update();
33650     },
33651     
33652     next : function(e)
33653     {
33654         e.preventDefault();
33655         
33656         this.indicator = Math.min(this.files.length, this.indicator + 1);
33657         
33658         this.update();
33659     }
33660 });
33661 /*
33662  * - LGPL
33663  *
33664  * RadioSet
33665  *
33666  *
33667  */
33668
33669 /**
33670  * @class Roo.bootstrap.RadioSet
33671  * @extends Roo.bootstrap.Input
33672  * Bootstrap RadioSet class
33673  * @cfg {String} indicatorpos (left|right) default left
33674  * @cfg {Boolean} inline (true|false) inline the element (default true)
33675  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33676  * @constructor
33677  * Create a new RadioSet
33678  * @param {Object} config The config object
33679  */
33680
33681 Roo.bootstrap.RadioSet = function(config){
33682     
33683     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33684     
33685     this.radioes = [];
33686     
33687     Roo.bootstrap.RadioSet.register(this);
33688     
33689     this.addEvents({
33690         /**
33691         * @event check
33692         * Fires when the element is checked or unchecked.
33693         * @param {Roo.bootstrap.RadioSet} this This radio
33694         * @param {Roo.bootstrap.Radio} item The checked item
33695         */
33696        check : true,
33697        /**
33698         * @event click
33699         * Fires when the element is click.
33700         * @param {Roo.bootstrap.RadioSet} this This radio set
33701         * @param {Roo.bootstrap.Radio} item The checked item
33702         * @param {Roo.EventObject} e The event object
33703         */
33704        click : true
33705     });
33706     
33707 };
33708
33709 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33710
33711     radioes : false,
33712     
33713     inline : true,
33714     
33715     weight : '',
33716     
33717     indicatorpos : 'left',
33718     
33719     getAutoCreate : function()
33720     {
33721         var label = {
33722             tag : 'label',
33723             cls : 'roo-radio-set-label',
33724             cn : [
33725                 {
33726                     tag : 'span',
33727                     html : this.fieldLabel
33728                 }
33729             ]
33730         };
33731         
33732         if(this.indicatorpos == 'left'){
33733             label.cn.unshift({
33734                 tag : 'i',
33735                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33736                 tooltip : 'This field is required'
33737             });
33738         } else {
33739             label.cn.push({
33740                 tag : 'i',
33741                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33742                 tooltip : 'This field is required'
33743             });
33744         }
33745         
33746         var items = {
33747             tag : 'div',
33748             cls : 'roo-radio-set-items'
33749         };
33750         
33751         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33752         
33753         if (align === 'left' && this.fieldLabel.length) {
33754             
33755             items = {
33756                 cls : "roo-radio-set-right", 
33757                 cn: [
33758                     items
33759                 ]
33760             };
33761             
33762             if(this.labelWidth > 12){
33763                 label.style = "width: " + this.labelWidth + 'px';
33764             }
33765             
33766             if(this.labelWidth < 13 && this.labelmd == 0){
33767                 this.labelmd = this.labelWidth;
33768             }
33769             
33770             if(this.labellg > 0){
33771                 label.cls += ' col-lg-' + this.labellg;
33772                 items.cls += ' col-lg-' + (12 - this.labellg);
33773             }
33774             
33775             if(this.labelmd > 0){
33776                 label.cls += ' col-md-' + this.labelmd;
33777                 items.cls += ' col-md-' + (12 - this.labelmd);
33778             }
33779             
33780             if(this.labelsm > 0){
33781                 label.cls += ' col-sm-' + this.labelsm;
33782                 items.cls += ' col-sm-' + (12 - this.labelsm);
33783             }
33784             
33785             if(this.labelxs > 0){
33786                 label.cls += ' col-xs-' + this.labelxs;
33787                 items.cls += ' col-xs-' + (12 - this.labelxs);
33788             }
33789         }
33790         
33791         var cfg = {
33792             tag : 'div',
33793             cls : 'roo-radio-set',
33794             cn : [
33795                 {
33796                     tag : 'input',
33797                     cls : 'roo-radio-set-input',
33798                     type : 'hidden',
33799                     name : this.name,
33800                     value : this.value ? this.value :  ''
33801                 },
33802                 label,
33803                 items
33804             ]
33805         };
33806         
33807         if(this.weight.length){
33808             cfg.cls += ' roo-radio-' + this.weight;
33809         }
33810         
33811         if(this.inline) {
33812             cfg.cls += ' roo-radio-set-inline';
33813         }
33814         
33815         var settings=this;
33816         ['xs','sm','md','lg'].map(function(size){
33817             if (settings[size]) {
33818                 cfg.cls += ' col-' + size + '-' + settings[size];
33819             }
33820         });
33821         
33822         return cfg;
33823         
33824     },
33825
33826     initEvents : function()
33827     {
33828         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33829         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33830         
33831         if(!this.fieldLabel.length){
33832             this.labelEl.hide();
33833         }
33834         
33835         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33836         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33837         
33838         this.indicator = this.indicatorEl();
33839         
33840         if(this.indicator){
33841             this.indicator.addClass('invisible');
33842         }
33843         
33844         this.originalValue = this.getValue();
33845         
33846     },
33847     
33848     inputEl: function ()
33849     {
33850         return this.el.select('.roo-radio-set-input', true).first();
33851     },
33852     
33853     getChildContainer : function()
33854     {
33855         return this.itemsEl;
33856     },
33857     
33858     register : function(item)
33859     {
33860         this.radioes.push(item);
33861         
33862     },
33863     
33864     validate : function()
33865     {   
33866         if(this.getVisibilityEl().hasClass('hidden')){
33867             return true;
33868         }
33869         
33870         var valid = false;
33871         
33872         Roo.each(this.radioes, function(i){
33873             if(!i.checked){
33874                 return;
33875             }
33876             
33877             valid = true;
33878             return false;
33879         });
33880         
33881         if(this.allowBlank) {
33882             return true;
33883         }
33884         
33885         if(this.disabled || valid){
33886             this.markValid();
33887             return true;
33888         }
33889         
33890         this.markInvalid();
33891         return false;
33892         
33893     },
33894     
33895     markValid : function()
33896     {
33897         if(this.labelEl.isVisible(true)){
33898             this.indicatorEl().removeClass('visible');
33899             this.indicatorEl().addClass('invisible');
33900         }
33901         
33902         this.el.removeClass([this.invalidClass, this.validClass]);
33903         this.el.addClass(this.validClass);
33904         
33905         this.fireEvent('valid', this);
33906     },
33907     
33908     markInvalid : function(msg)
33909     {
33910         if(this.allowBlank || this.disabled){
33911             return;
33912         }
33913         
33914         if(this.labelEl.isVisible(true)){
33915             this.indicatorEl().removeClass('invisible');
33916             this.indicatorEl().addClass('visible');
33917         }
33918         
33919         this.el.removeClass([this.invalidClass, this.validClass]);
33920         this.el.addClass(this.invalidClass);
33921         
33922         this.fireEvent('invalid', this, msg);
33923         
33924     },
33925     
33926     setValue : function(v, suppressEvent)
33927     {   
33928         if(this.value === v){
33929             return;
33930         }
33931         
33932         this.value = v;
33933         
33934         if(this.rendered){
33935             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33936         }
33937         
33938         Roo.each(this.radioes, function(i){
33939             i.checked = false;
33940             i.el.removeClass('checked');
33941         });
33942         
33943         Roo.each(this.radioes, function(i){
33944             
33945             if(i.value === v || i.value.toString() === v.toString()){
33946                 i.checked = true;
33947                 i.el.addClass('checked');
33948                 
33949                 if(suppressEvent !== true){
33950                     this.fireEvent('check', this, i);
33951                 }
33952                 
33953                 return false;
33954             }
33955             
33956         }, this);
33957         
33958         this.validate();
33959     },
33960     
33961     clearInvalid : function(){
33962         
33963         if(!this.el || this.preventMark){
33964             return;
33965         }
33966         
33967         this.el.removeClass([this.invalidClass]);
33968         
33969         this.fireEvent('valid', this);
33970     }
33971     
33972 });
33973
33974 Roo.apply(Roo.bootstrap.RadioSet, {
33975     
33976     groups: {},
33977     
33978     register : function(set)
33979     {
33980         this.groups[set.name] = set;
33981     },
33982     
33983     get: function(name) 
33984     {
33985         if (typeof(this.groups[name]) == 'undefined') {
33986             return false;
33987         }
33988         
33989         return this.groups[name] ;
33990     }
33991     
33992 });
33993 /*
33994  * Based on:
33995  * Ext JS Library 1.1.1
33996  * Copyright(c) 2006-2007, Ext JS, LLC.
33997  *
33998  * Originally Released Under LGPL - original licence link has changed is not relivant.
33999  *
34000  * Fork - LGPL
34001  * <script type="text/javascript">
34002  */
34003
34004
34005 /**
34006  * @class Roo.bootstrap.SplitBar
34007  * @extends Roo.util.Observable
34008  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34009  * <br><br>
34010  * Usage:
34011  * <pre><code>
34012 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34013                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34014 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34015 split.minSize = 100;
34016 split.maxSize = 600;
34017 split.animate = true;
34018 split.on('moved', splitterMoved);
34019 </code></pre>
34020  * @constructor
34021  * Create a new SplitBar
34022  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34023  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34024  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34025  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34026                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34027                         position of the SplitBar).
34028  */
34029 Roo.bootstrap.SplitBar = function(cfg){
34030     
34031     /** @private */
34032     
34033     //{
34034     //  dragElement : elm
34035     //  resizingElement: el,
34036         // optional..
34037     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34038     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34039         // existingProxy ???
34040     //}
34041     
34042     this.el = Roo.get(cfg.dragElement, true);
34043     this.el.dom.unselectable = "on";
34044     /** @private */
34045     this.resizingEl = Roo.get(cfg.resizingElement, true);
34046
34047     /**
34048      * @private
34049      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34050      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34051      * @type Number
34052      */
34053     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34054     
34055     /**
34056      * The minimum size of the resizing element. (Defaults to 0)
34057      * @type Number
34058      */
34059     this.minSize = 0;
34060     
34061     /**
34062      * The maximum size of the resizing element. (Defaults to 2000)
34063      * @type Number
34064      */
34065     this.maxSize = 2000;
34066     
34067     /**
34068      * Whether to animate the transition to the new size
34069      * @type Boolean
34070      */
34071     this.animate = false;
34072     
34073     /**
34074      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34075      * @type Boolean
34076      */
34077     this.useShim = false;
34078     
34079     /** @private */
34080     this.shim = null;
34081     
34082     if(!cfg.existingProxy){
34083         /** @private */
34084         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34085     }else{
34086         this.proxy = Roo.get(cfg.existingProxy).dom;
34087     }
34088     /** @private */
34089     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34090     
34091     /** @private */
34092     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34093     
34094     /** @private */
34095     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34096     
34097     /** @private */
34098     this.dragSpecs = {};
34099     
34100     /**
34101      * @private The adapter to use to positon and resize elements
34102      */
34103     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34104     this.adapter.init(this);
34105     
34106     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34107         /** @private */
34108         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34109         this.el.addClass("roo-splitbar-h");
34110     }else{
34111         /** @private */
34112         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34113         this.el.addClass("roo-splitbar-v");
34114     }
34115     
34116     this.addEvents({
34117         /**
34118          * @event resize
34119          * Fires when the splitter is moved (alias for {@link #event-moved})
34120          * @param {Roo.bootstrap.SplitBar} this
34121          * @param {Number} newSize the new width or height
34122          */
34123         "resize" : true,
34124         /**
34125          * @event moved
34126          * Fires when the splitter is moved
34127          * @param {Roo.bootstrap.SplitBar} this
34128          * @param {Number} newSize the new width or height
34129          */
34130         "moved" : true,
34131         /**
34132          * @event beforeresize
34133          * Fires before the splitter is dragged
34134          * @param {Roo.bootstrap.SplitBar} this
34135          */
34136         "beforeresize" : true,
34137
34138         "beforeapply" : true
34139     });
34140
34141     Roo.util.Observable.call(this);
34142 };
34143
34144 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34145     onStartProxyDrag : function(x, y){
34146         this.fireEvent("beforeresize", this);
34147         if(!this.overlay){
34148             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34149             o.unselectable();
34150             o.enableDisplayMode("block");
34151             // all splitbars share the same overlay
34152             Roo.bootstrap.SplitBar.prototype.overlay = o;
34153         }
34154         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34155         this.overlay.show();
34156         Roo.get(this.proxy).setDisplayed("block");
34157         var size = this.adapter.getElementSize(this);
34158         this.activeMinSize = this.getMinimumSize();;
34159         this.activeMaxSize = this.getMaximumSize();;
34160         var c1 = size - this.activeMinSize;
34161         var c2 = Math.max(this.activeMaxSize - size, 0);
34162         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34163             this.dd.resetConstraints();
34164             this.dd.setXConstraint(
34165                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34166                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34167             );
34168             this.dd.setYConstraint(0, 0);
34169         }else{
34170             this.dd.resetConstraints();
34171             this.dd.setXConstraint(0, 0);
34172             this.dd.setYConstraint(
34173                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34174                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34175             );
34176          }
34177         this.dragSpecs.startSize = size;
34178         this.dragSpecs.startPoint = [x, y];
34179         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34180     },
34181     
34182     /** 
34183      * @private Called after the drag operation by the DDProxy
34184      */
34185     onEndProxyDrag : function(e){
34186         Roo.get(this.proxy).setDisplayed(false);
34187         var endPoint = Roo.lib.Event.getXY(e);
34188         if(this.overlay){
34189             this.overlay.hide();
34190         }
34191         var newSize;
34192         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34193             newSize = this.dragSpecs.startSize + 
34194                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34195                     endPoint[0] - this.dragSpecs.startPoint[0] :
34196                     this.dragSpecs.startPoint[0] - endPoint[0]
34197                 );
34198         }else{
34199             newSize = this.dragSpecs.startSize + 
34200                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34201                     endPoint[1] - this.dragSpecs.startPoint[1] :
34202                     this.dragSpecs.startPoint[1] - endPoint[1]
34203                 );
34204         }
34205         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34206         if(newSize != this.dragSpecs.startSize){
34207             if(this.fireEvent('beforeapply', this, newSize) !== false){
34208                 this.adapter.setElementSize(this, newSize);
34209                 this.fireEvent("moved", this, newSize);
34210                 this.fireEvent("resize", this, newSize);
34211             }
34212         }
34213     },
34214     
34215     /**
34216      * Get the adapter this SplitBar uses
34217      * @return The adapter object
34218      */
34219     getAdapter : function(){
34220         return this.adapter;
34221     },
34222     
34223     /**
34224      * Set the adapter this SplitBar uses
34225      * @param {Object} adapter A SplitBar adapter object
34226      */
34227     setAdapter : function(adapter){
34228         this.adapter = adapter;
34229         this.adapter.init(this);
34230     },
34231     
34232     /**
34233      * Gets the minimum size for the resizing element
34234      * @return {Number} The minimum size
34235      */
34236     getMinimumSize : function(){
34237         return this.minSize;
34238     },
34239     
34240     /**
34241      * Sets the minimum size for the resizing element
34242      * @param {Number} minSize The minimum size
34243      */
34244     setMinimumSize : function(minSize){
34245         this.minSize = minSize;
34246     },
34247     
34248     /**
34249      * Gets the maximum size for the resizing element
34250      * @return {Number} The maximum size
34251      */
34252     getMaximumSize : function(){
34253         return this.maxSize;
34254     },
34255     
34256     /**
34257      * Sets the maximum size for the resizing element
34258      * @param {Number} maxSize The maximum size
34259      */
34260     setMaximumSize : function(maxSize){
34261         this.maxSize = maxSize;
34262     },
34263     
34264     /**
34265      * Sets the initialize size for the resizing element
34266      * @param {Number} size The initial size
34267      */
34268     setCurrentSize : function(size){
34269         var oldAnimate = this.animate;
34270         this.animate = false;
34271         this.adapter.setElementSize(this, size);
34272         this.animate = oldAnimate;
34273     },
34274     
34275     /**
34276      * Destroy this splitbar. 
34277      * @param {Boolean} removeEl True to remove the element
34278      */
34279     destroy : function(removeEl){
34280         if(this.shim){
34281             this.shim.remove();
34282         }
34283         this.dd.unreg();
34284         this.proxy.parentNode.removeChild(this.proxy);
34285         if(removeEl){
34286             this.el.remove();
34287         }
34288     }
34289 });
34290
34291 /**
34292  * @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.
34293  */
34294 Roo.bootstrap.SplitBar.createProxy = function(dir){
34295     var proxy = new Roo.Element(document.createElement("div"));
34296     proxy.unselectable();
34297     var cls = 'roo-splitbar-proxy';
34298     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34299     document.body.appendChild(proxy.dom);
34300     return proxy.dom;
34301 };
34302
34303 /** 
34304  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34305  * Default Adapter. It assumes the splitter and resizing element are not positioned
34306  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34307  */
34308 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34309 };
34310
34311 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34312     // do nothing for now
34313     init : function(s){
34314     
34315     },
34316     /**
34317      * Called before drag operations to get the current size of the resizing element. 
34318      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34319      */
34320      getElementSize : function(s){
34321         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34322             return s.resizingEl.getWidth();
34323         }else{
34324             return s.resizingEl.getHeight();
34325         }
34326     },
34327     
34328     /**
34329      * Called after drag operations to set the size of the resizing element.
34330      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34331      * @param {Number} newSize The new size to set
34332      * @param {Function} onComplete A function to be invoked when resizing is complete
34333      */
34334     setElementSize : function(s, newSize, onComplete){
34335         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34336             if(!s.animate){
34337                 s.resizingEl.setWidth(newSize);
34338                 if(onComplete){
34339                     onComplete(s, newSize);
34340                 }
34341             }else{
34342                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34343             }
34344         }else{
34345             
34346             if(!s.animate){
34347                 s.resizingEl.setHeight(newSize);
34348                 if(onComplete){
34349                     onComplete(s, newSize);
34350                 }
34351             }else{
34352                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34353             }
34354         }
34355     }
34356 };
34357
34358 /** 
34359  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34360  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34361  * Adapter that  moves the splitter element to align with the resized sizing element. 
34362  * Used with an absolute positioned SplitBar.
34363  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34364  * document.body, make sure you assign an id to the body element.
34365  */
34366 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34367     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34368     this.container = Roo.get(container);
34369 };
34370
34371 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34372     init : function(s){
34373         this.basic.init(s);
34374     },
34375     
34376     getElementSize : function(s){
34377         return this.basic.getElementSize(s);
34378     },
34379     
34380     setElementSize : function(s, newSize, onComplete){
34381         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34382     },
34383     
34384     moveSplitter : function(s){
34385         var yes = Roo.bootstrap.SplitBar;
34386         switch(s.placement){
34387             case yes.LEFT:
34388                 s.el.setX(s.resizingEl.getRight());
34389                 break;
34390             case yes.RIGHT:
34391                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34392                 break;
34393             case yes.TOP:
34394                 s.el.setY(s.resizingEl.getBottom());
34395                 break;
34396             case yes.BOTTOM:
34397                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34398                 break;
34399         }
34400     }
34401 };
34402
34403 /**
34404  * Orientation constant - Create a vertical SplitBar
34405  * @static
34406  * @type Number
34407  */
34408 Roo.bootstrap.SplitBar.VERTICAL = 1;
34409
34410 /**
34411  * Orientation constant - Create a horizontal SplitBar
34412  * @static
34413  * @type Number
34414  */
34415 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34416
34417 /**
34418  * Placement constant - The resizing element is to the left of the splitter element
34419  * @static
34420  * @type Number
34421  */
34422 Roo.bootstrap.SplitBar.LEFT = 1;
34423
34424 /**
34425  * Placement constant - The resizing element is to the right of the splitter element
34426  * @static
34427  * @type Number
34428  */
34429 Roo.bootstrap.SplitBar.RIGHT = 2;
34430
34431 /**
34432  * Placement constant - The resizing element is positioned above the splitter element
34433  * @static
34434  * @type Number
34435  */
34436 Roo.bootstrap.SplitBar.TOP = 3;
34437
34438 /**
34439  * Placement constant - The resizing element is positioned under splitter element
34440  * @static
34441  * @type Number
34442  */
34443 Roo.bootstrap.SplitBar.BOTTOM = 4;
34444 Roo.namespace("Roo.bootstrap.layout");/*
34445  * Based on:
34446  * Ext JS Library 1.1.1
34447  * Copyright(c) 2006-2007, Ext JS, LLC.
34448  *
34449  * Originally Released Under LGPL - original licence link has changed is not relivant.
34450  *
34451  * Fork - LGPL
34452  * <script type="text/javascript">
34453  */
34454
34455 /**
34456  * @class Roo.bootstrap.layout.Manager
34457  * @extends Roo.bootstrap.Component
34458  * Base class for layout managers.
34459  */
34460 Roo.bootstrap.layout.Manager = function(config)
34461 {
34462     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34463
34464
34465
34466
34467
34468     /** false to disable window resize monitoring @type Boolean */
34469     this.monitorWindowResize = true;
34470     this.regions = {};
34471     this.addEvents({
34472         /**
34473          * @event layout
34474          * Fires when a layout is performed.
34475          * @param {Roo.LayoutManager} this
34476          */
34477         "layout" : true,
34478         /**
34479          * @event regionresized
34480          * Fires when the user resizes a region.
34481          * @param {Roo.LayoutRegion} region The resized region
34482          * @param {Number} newSize The new size (width for east/west, height for north/south)
34483          */
34484         "regionresized" : true,
34485         /**
34486          * @event regioncollapsed
34487          * Fires when a region is collapsed.
34488          * @param {Roo.LayoutRegion} region The collapsed region
34489          */
34490         "regioncollapsed" : true,
34491         /**
34492          * @event regionexpanded
34493          * Fires when a region is expanded.
34494          * @param {Roo.LayoutRegion} region The expanded region
34495          */
34496         "regionexpanded" : true
34497     });
34498     this.updating = false;
34499
34500     if (config.el) {
34501         this.el = Roo.get(config.el);
34502         this.initEvents();
34503     }
34504
34505 };
34506
34507 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34508
34509
34510     regions : null,
34511
34512     monitorWindowResize : true,
34513
34514
34515     updating : false,
34516
34517
34518     onRender : function(ct, position)
34519     {
34520         if(!this.el){
34521             this.el = Roo.get(ct);
34522             this.initEvents();
34523         }
34524         //this.fireEvent('render',this);
34525     },
34526
34527
34528     initEvents: function()
34529     {
34530
34531
34532         // ie scrollbar fix
34533         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34534             document.body.scroll = "no";
34535         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34536             this.el.position('relative');
34537         }
34538         this.id = this.el.id;
34539         this.el.addClass("roo-layout-container");
34540         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34541         if(this.el.dom != document.body ) {
34542             this.el.on('resize', this.layout,this);
34543             this.el.on('show', this.layout,this);
34544         }
34545
34546     },
34547
34548     /**
34549      * Returns true if this layout is currently being updated
34550      * @return {Boolean}
34551      */
34552     isUpdating : function(){
34553         return this.updating;
34554     },
34555
34556     /**
34557      * Suspend the LayoutManager from doing auto-layouts while
34558      * making multiple add or remove calls
34559      */
34560     beginUpdate : function(){
34561         this.updating = true;
34562     },
34563
34564     /**
34565      * Restore auto-layouts and optionally disable the manager from performing a layout
34566      * @param {Boolean} noLayout true to disable a layout update
34567      */
34568     endUpdate : function(noLayout){
34569         this.updating = false;
34570         if(!noLayout){
34571             this.layout();
34572         }
34573     },
34574
34575     layout: function(){
34576         // abstract...
34577     },
34578
34579     onRegionResized : function(region, newSize){
34580         this.fireEvent("regionresized", region, newSize);
34581         this.layout();
34582     },
34583
34584     onRegionCollapsed : function(region){
34585         this.fireEvent("regioncollapsed", region);
34586     },
34587
34588     onRegionExpanded : function(region){
34589         this.fireEvent("regionexpanded", region);
34590     },
34591
34592     /**
34593      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34594      * performs box-model adjustments.
34595      * @return {Object} The size as an object {width: (the width), height: (the height)}
34596      */
34597     getViewSize : function()
34598     {
34599         var size;
34600         if(this.el.dom != document.body){
34601             size = this.el.getSize();
34602         }else{
34603             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34604         }
34605         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34606         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34607         return size;
34608     },
34609
34610     /**
34611      * Returns the Element this layout is bound to.
34612      * @return {Roo.Element}
34613      */
34614     getEl : function(){
34615         return this.el;
34616     },
34617
34618     /**
34619      * Returns the specified region.
34620      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34621      * @return {Roo.LayoutRegion}
34622      */
34623     getRegion : function(target){
34624         return this.regions[target.toLowerCase()];
34625     },
34626
34627     onWindowResize : function(){
34628         if(this.monitorWindowResize){
34629             this.layout();
34630         }
34631     }
34632 });
34633 /*
34634  * Based on:
34635  * Ext JS Library 1.1.1
34636  * Copyright(c) 2006-2007, Ext JS, LLC.
34637  *
34638  * Originally Released Under LGPL - original licence link has changed is not relivant.
34639  *
34640  * Fork - LGPL
34641  * <script type="text/javascript">
34642  */
34643 /**
34644  * @class Roo.bootstrap.layout.Border
34645  * @extends Roo.bootstrap.layout.Manager
34646  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34647  * please see: examples/bootstrap/nested.html<br><br>
34648  
34649 <b>The container the layout is rendered into can be either the body element or any other element.
34650 If it is not the body element, the container needs to either be an absolute positioned element,
34651 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34652 the container size if it is not the body element.</b>
34653
34654 * @constructor
34655 * Create a new Border
34656 * @param {Object} config Configuration options
34657  */
34658 Roo.bootstrap.layout.Border = function(config){
34659     config = config || {};
34660     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34661     
34662     
34663     
34664     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34665         if(config[region]){
34666             config[region].region = region;
34667             this.addRegion(config[region]);
34668         }
34669     },this);
34670     
34671 };
34672
34673 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34674
34675 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34676     /**
34677      * Creates and adds a new region if it doesn't already exist.
34678      * @param {String} target The target region key (north, south, east, west or center).
34679      * @param {Object} config The regions config object
34680      * @return {BorderLayoutRegion} The new region
34681      */
34682     addRegion : function(config)
34683     {
34684         if(!this.regions[config.region]){
34685             var r = this.factory(config);
34686             this.bindRegion(r);
34687         }
34688         return this.regions[config.region];
34689     },
34690
34691     // private (kinda)
34692     bindRegion : function(r){
34693         this.regions[r.config.region] = r;
34694         
34695         r.on("visibilitychange",    this.layout, this);
34696         r.on("paneladded",          this.layout, this);
34697         r.on("panelremoved",        this.layout, this);
34698         r.on("invalidated",         this.layout, this);
34699         r.on("resized",             this.onRegionResized, this);
34700         r.on("collapsed",           this.onRegionCollapsed, this);
34701         r.on("expanded",            this.onRegionExpanded, this);
34702     },
34703
34704     /**
34705      * Performs a layout update.
34706      */
34707     layout : function()
34708     {
34709         if(this.updating) {
34710             return;
34711         }
34712         
34713         // render all the rebions if they have not been done alreayd?
34714         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34715             if(this.regions[region] && !this.regions[region].bodyEl){
34716                 this.regions[region].onRender(this.el)
34717             }
34718         },this);
34719         
34720         var size = this.getViewSize();
34721         var w = size.width;
34722         var h = size.height;
34723         var centerW = w;
34724         var centerH = h;
34725         var centerY = 0;
34726         var centerX = 0;
34727         //var x = 0, y = 0;
34728
34729         var rs = this.regions;
34730         var north = rs["north"];
34731         var south = rs["south"]; 
34732         var west = rs["west"];
34733         var east = rs["east"];
34734         var center = rs["center"];
34735         //if(this.hideOnLayout){ // not supported anymore
34736             //c.el.setStyle("display", "none");
34737         //}
34738         if(north && north.isVisible()){
34739             var b = north.getBox();
34740             var m = north.getMargins();
34741             b.width = w - (m.left+m.right);
34742             b.x = m.left;
34743             b.y = m.top;
34744             centerY = b.height + b.y + m.bottom;
34745             centerH -= centerY;
34746             north.updateBox(this.safeBox(b));
34747         }
34748         if(south && south.isVisible()){
34749             var b = south.getBox();
34750             var m = south.getMargins();
34751             b.width = w - (m.left+m.right);
34752             b.x = m.left;
34753             var totalHeight = (b.height + m.top + m.bottom);
34754             b.y = h - totalHeight + m.top;
34755             centerH -= totalHeight;
34756             south.updateBox(this.safeBox(b));
34757         }
34758         if(west && west.isVisible()){
34759             var b = west.getBox();
34760             var m = west.getMargins();
34761             b.height = centerH - (m.top+m.bottom);
34762             b.x = m.left;
34763             b.y = centerY + m.top;
34764             var totalWidth = (b.width + m.left + m.right);
34765             centerX += totalWidth;
34766             centerW -= totalWidth;
34767             west.updateBox(this.safeBox(b));
34768         }
34769         if(east && east.isVisible()){
34770             var b = east.getBox();
34771             var m = east.getMargins();
34772             b.height = centerH - (m.top+m.bottom);
34773             var totalWidth = (b.width + m.left + m.right);
34774             b.x = w - totalWidth + m.left;
34775             b.y = centerY + m.top;
34776             centerW -= totalWidth;
34777             east.updateBox(this.safeBox(b));
34778         }
34779         if(center){
34780             var m = center.getMargins();
34781             var centerBox = {
34782                 x: centerX + m.left,
34783                 y: centerY + m.top,
34784                 width: centerW - (m.left+m.right),
34785                 height: centerH - (m.top+m.bottom)
34786             };
34787             //if(this.hideOnLayout){
34788                 //center.el.setStyle("display", "block");
34789             //}
34790             center.updateBox(this.safeBox(centerBox));
34791         }
34792         this.el.repaint();
34793         this.fireEvent("layout", this);
34794     },
34795
34796     // private
34797     safeBox : function(box){
34798         box.width = Math.max(0, box.width);
34799         box.height = Math.max(0, box.height);
34800         return box;
34801     },
34802
34803     /**
34804      * Adds a ContentPanel (or subclass) to this layout.
34805      * @param {String} target The target region key (north, south, east, west or center).
34806      * @param {Roo.ContentPanel} panel The panel to add
34807      * @return {Roo.ContentPanel} The added panel
34808      */
34809     add : function(target, panel){
34810          
34811         target = target.toLowerCase();
34812         return this.regions[target].add(panel);
34813     },
34814
34815     /**
34816      * Remove a ContentPanel (or subclass) to this layout.
34817      * @param {String} target The target region key (north, south, east, west or center).
34818      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34819      * @return {Roo.ContentPanel} The removed panel
34820      */
34821     remove : function(target, panel){
34822         target = target.toLowerCase();
34823         return this.regions[target].remove(panel);
34824     },
34825
34826     /**
34827      * Searches all regions for a panel with the specified id
34828      * @param {String} panelId
34829      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34830      */
34831     findPanel : function(panelId){
34832         var rs = this.regions;
34833         for(var target in rs){
34834             if(typeof rs[target] != "function"){
34835                 var p = rs[target].getPanel(panelId);
34836                 if(p){
34837                     return p;
34838                 }
34839             }
34840         }
34841         return null;
34842     },
34843
34844     /**
34845      * Searches all regions for a panel with the specified id and activates (shows) it.
34846      * @param {String/ContentPanel} panelId The panels id or the panel itself
34847      * @return {Roo.ContentPanel} The shown panel or null
34848      */
34849     showPanel : function(panelId) {
34850       var rs = this.regions;
34851       for(var target in rs){
34852          var r = rs[target];
34853          if(typeof r != "function"){
34854             if(r.hasPanel(panelId)){
34855                return r.showPanel(panelId);
34856             }
34857          }
34858       }
34859       return null;
34860    },
34861
34862    /**
34863      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34864      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34865      */
34866    /*
34867     restoreState : function(provider){
34868         if(!provider){
34869             provider = Roo.state.Manager;
34870         }
34871         var sm = new Roo.LayoutStateManager();
34872         sm.init(this, provider);
34873     },
34874 */
34875  
34876  
34877     /**
34878      * Adds a xtype elements to the layout.
34879      * <pre><code>
34880
34881 layout.addxtype({
34882        xtype : 'ContentPanel',
34883        region: 'west',
34884        items: [ .... ]
34885    }
34886 );
34887
34888 layout.addxtype({
34889         xtype : 'NestedLayoutPanel',
34890         region: 'west',
34891         layout: {
34892            center: { },
34893            west: { }   
34894         },
34895         items : [ ... list of content panels or nested layout panels.. ]
34896    }
34897 );
34898 </code></pre>
34899      * @param {Object} cfg Xtype definition of item to add.
34900      */
34901     addxtype : function(cfg)
34902     {
34903         // basically accepts a pannel...
34904         // can accept a layout region..!?!?
34905         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34906         
34907         
34908         // theory?  children can only be panels??
34909         
34910         //if (!cfg.xtype.match(/Panel$/)) {
34911         //    return false;
34912         //}
34913         var ret = false;
34914         
34915         if (typeof(cfg.region) == 'undefined') {
34916             Roo.log("Failed to add Panel, region was not set");
34917             Roo.log(cfg);
34918             return false;
34919         }
34920         var region = cfg.region;
34921         delete cfg.region;
34922         
34923           
34924         var xitems = [];
34925         if (cfg.items) {
34926             xitems = cfg.items;
34927             delete cfg.items;
34928         }
34929         var nb = false;
34930         
34931         switch(cfg.xtype) 
34932         {
34933             case 'Content':  // ContentPanel (el, cfg)
34934             case 'Scroll':  // ContentPanel (el, cfg)
34935             case 'View': 
34936                 cfg.autoCreate = true;
34937                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34938                 //} else {
34939                 //    var el = this.el.createChild();
34940                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34941                 //}
34942                 
34943                 this.add(region, ret);
34944                 break;
34945             
34946             /*
34947             case 'TreePanel': // our new panel!
34948                 cfg.el = this.el.createChild();
34949                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34950                 this.add(region, ret);
34951                 break;
34952             */
34953             
34954             case 'Nest': 
34955                 // create a new Layout (which is  a Border Layout...
34956                 
34957                 var clayout = cfg.layout;
34958                 clayout.el  = this.el.createChild();
34959                 clayout.items   = clayout.items  || [];
34960                 
34961                 delete cfg.layout;
34962                 
34963                 // replace this exitems with the clayout ones..
34964                 xitems = clayout.items;
34965                  
34966                 // force background off if it's in center...
34967                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34968                     cfg.background = false;
34969                 }
34970                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34971                 
34972                 
34973                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34974                 //console.log('adding nested layout panel '  + cfg.toSource());
34975                 this.add(region, ret);
34976                 nb = {}; /// find first...
34977                 break;
34978             
34979             case 'Grid':
34980                 
34981                 // needs grid and region
34982                 
34983                 //var el = this.getRegion(region).el.createChild();
34984                 /*
34985                  *var el = this.el.createChild();
34986                 // create the grid first...
34987                 cfg.grid.container = el;
34988                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34989                 */
34990                 
34991                 if (region == 'center' && this.active ) {
34992                     cfg.background = false;
34993                 }
34994                 
34995                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34996                 
34997                 this.add(region, ret);
34998                 /*
34999                 if (cfg.background) {
35000                     // render grid on panel activation (if panel background)
35001                     ret.on('activate', function(gp) {
35002                         if (!gp.grid.rendered) {
35003                     //        gp.grid.render(el);
35004                         }
35005                     });
35006                 } else {
35007                   //  cfg.grid.render(el);
35008                 }
35009                 */
35010                 break;
35011            
35012            
35013             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35014                 // it was the old xcomponent building that caused this before.
35015                 // espeically if border is the top element in the tree.
35016                 ret = this;
35017                 break; 
35018                 
35019                     
35020                 
35021                 
35022                 
35023             default:
35024                 /*
35025                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35026                     
35027                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35028                     this.add(region, ret);
35029                 } else {
35030                 */
35031                     Roo.log(cfg);
35032                     throw "Can not add '" + cfg.xtype + "' to Border";
35033                     return null;
35034              
35035                                 
35036              
35037         }
35038         this.beginUpdate();
35039         // add children..
35040         var region = '';
35041         var abn = {};
35042         Roo.each(xitems, function(i)  {
35043             region = nb && i.region ? i.region : false;
35044             
35045             var add = ret.addxtype(i);
35046            
35047             if (region) {
35048                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35049                 if (!i.background) {
35050                     abn[region] = nb[region] ;
35051                 }
35052             }
35053             
35054         });
35055         this.endUpdate();
35056
35057         // make the last non-background panel active..
35058         //if (nb) { Roo.log(abn); }
35059         if (nb) {
35060             
35061             for(var r in abn) {
35062                 region = this.getRegion(r);
35063                 if (region) {
35064                     // tried using nb[r], but it does not work..
35065                      
35066                     region.showPanel(abn[r]);
35067                    
35068                 }
35069             }
35070         }
35071         return ret;
35072         
35073     },
35074     
35075     
35076 // private
35077     factory : function(cfg)
35078     {
35079         
35080         var validRegions = Roo.bootstrap.layout.Border.regions;
35081
35082         var target = cfg.region;
35083         cfg.mgr = this;
35084         
35085         var r = Roo.bootstrap.layout;
35086         Roo.log(target);
35087         switch(target){
35088             case "north":
35089                 return new r.North(cfg);
35090             case "south":
35091                 return new r.South(cfg);
35092             case "east":
35093                 return new r.East(cfg);
35094             case "west":
35095                 return new r.West(cfg);
35096             case "center":
35097                 return new r.Center(cfg);
35098         }
35099         throw 'Layout region "'+target+'" not supported.';
35100     }
35101     
35102     
35103 });
35104  /*
35105  * Based on:
35106  * Ext JS Library 1.1.1
35107  * Copyright(c) 2006-2007, Ext JS, LLC.
35108  *
35109  * Originally Released Under LGPL - original licence link has changed is not relivant.
35110  *
35111  * Fork - LGPL
35112  * <script type="text/javascript">
35113  */
35114  
35115 /**
35116  * @class Roo.bootstrap.layout.Basic
35117  * @extends Roo.util.Observable
35118  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35119  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35120  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35121  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35122  * @cfg {string}   region  the region that it inhabits..
35123  * @cfg {bool}   skipConfig skip config?
35124  * 
35125
35126  */
35127 Roo.bootstrap.layout.Basic = function(config){
35128     
35129     this.mgr = config.mgr;
35130     
35131     this.position = config.region;
35132     
35133     var skipConfig = config.skipConfig;
35134     
35135     this.events = {
35136         /**
35137          * @scope Roo.BasicLayoutRegion
35138          */
35139         
35140         /**
35141          * @event beforeremove
35142          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35143          * @param {Roo.LayoutRegion} this
35144          * @param {Roo.ContentPanel} panel The panel
35145          * @param {Object} e The cancel event object
35146          */
35147         "beforeremove" : true,
35148         /**
35149          * @event invalidated
35150          * Fires when the layout for this region is changed.
35151          * @param {Roo.LayoutRegion} this
35152          */
35153         "invalidated" : true,
35154         /**
35155          * @event visibilitychange
35156          * Fires when this region is shown or hidden 
35157          * @param {Roo.LayoutRegion} this
35158          * @param {Boolean} visibility true or false
35159          */
35160         "visibilitychange" : true,
35161         /**
35162          * @event paneladded
35163          * Fires when a panel is added. 
35164          * @param {Roo.LayoutRegion} this
35165          * @param {Roo.ContentPanel} panel The panel
35166          */
35167         "paneladded" : true,
35168         /**
35169          * @event panelremoved
35170          * Fires when a panel is removed. 
35171          * @param {Roo.LayoutRegion} this
35172          * @param {Roo.ContentPanel} panel The panel
35173          */
35174         "panelremoved" : true,
35175         /**
35176          * @event beforecollapse
35177          * Fires when this region before collapse.
35178          * @param {Roo.LayoutRegion} this
35179          */
35180         "beforecollapse" : true,
35181         /**
35182          * @event collapsed
35183          * Fires when this region is collapsed.
35184          * @param {Roo.LayoutRegion} this
35185          */
35186         "collapsed" : true,
35187         /**
35188          * @event expanded
35189          * Fires when this region is expanded.
35190          * @param {Roo.LayoutRegion} this
35191          */
35192         "expanded" : true,
35193         /**
35194          * @event slideshow
35195          * Fires when this region is slid into view.
35196          * @param {Roo.LayoutRegion} this
35197          */
35198         "slideshow" : true,
35199         /**
35200          * @event slidehide
35201          * Fires when this region slides out of view. 
35202          * @param {Roo.LayoutRegion} this
35203          */
35204         "slidehide" : true,
35205         /**
35206          * @event panelactivated
35207          * Fires when a panel is activated. 
35208          * @param {Roo.LayoutRegion} this
35209          * @param {Roo.ContentPanel} panel The activated panel
35210          */
35211         "panelactivated" : true,
35212         /**
35213          * @event resized
35214          * Fires when the user resizes this region. 
35215          * @param {Roo.LayoutRegion} this
35216          * @param {Number} newSize The new size (width for east/west, height for north/south)
35217          */
35218         "resized" : true
35219     };
35220     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35221     this.panels = new Roo.util.MixedCollection();
35222     this.panels.getKey = this.getPanelId.createDelegate(this);
35223     this.box = null;
35224     this.activePanel = null;
35225     // ensure listeners are added...
35226     
35227     if (config.listeners || config.events) {
35228         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35229             listeners : config.listeners || {},
35230             events : config.events || {}
35231         });
35232     }
35233     
35234     if(skipConfig !== true){
35235         this.applyConfig(config);
35236     }
35237 };
35238
35239 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35240 {
35241     getPanelId : function(p){
35242         return p.getId();
35243     },
35244     
35245     applyConfig : function(config){
35246         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35247         this.config = config;
35248         
35249     },
35250     
35251     /**
35252      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35253      * the width, for horizontal (north, south) the height.
35254      * @param {Number} newSize The new width or height
35255      */
35256     resizeTo : function(newSize){
35257         var el = this.el ? this.el :
35258                  (this.activePanel ? this.activePanel.getEl() : null);
35259         if(el){
35260             switch(this.position){
35261                 case "east":
35262                 case "west":
35263                     el.setWidth(newSize);
35264                     this.fireEvent("resized", this, newSize);
35265                 break;
35266                 case "north":
35267                 case "south":
35268                     el.setHeight(newSize);
35269                     this.fireEvent("resized", this, newSize);
35270                 break;                
35271             }
35272         }
35273     },
35274     
35275     getBox : function(){
35276         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35277     },
35278     
35279     getMargins : function(){
35280         return this.margins;
35281     },
35282     
35283     updateBox : function(box){
35284         this.box = box;
35285         var el = this.activePanel.getEl();
35286         el.dom.style.left = box.x + "px";
35287         el.dom.style.top = box.y + "px";
35288         this.activePanel.setSize(box.width, box.height);
35289     },
35290     
35291     /**
35292      * Returns the container element for this region.
35293      * @return {Roo.Element}
35294      */
35295     getEl : function(){
35296         return this.activePanel;
35297     },
35298     
35299     /**
35300      * Returns true if this region is currently visible.
35301      * @return {Boolean}
35302      */
35303     isVisible : function(){
35304         return this.activePanel ? true : false;
35305     },
35306     
35307     setActivePanel : function(panel){
35308         panel = this.getPanel(panel);
35309         if(this.activePanel && this.activePanel != panel){
35310             this.activePanel.setActiveState(false);
35311             this.activePanel.getEl().setLeftTop(-10000,-10000);
35312         }
35313         this.activePanel = panel;
35314         panel.setActiveState(true);
35315         if(this.box){
35316             panel.setSize(this.box.width, this.box.height);
35317         }
35318         this.fireEvent("panelactivated", this, panel);
35319         this.fireEvent("invalidated");
35320     },
35321     
35322     /**
35323      * Show the specified panel.
35324      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35325      * @return {Roo.ContentPanel} The shown panel or null
35326      */
35327     showPanel : function(panel){
35328         panel = this.getPanel(panel);
35329         if(panel){
35330             this.setActivePanel(panel);
35331         }
35332         return panel;
35333     },
35334     
35335     /**
35336      * Get the active panel for this region.
35337      * @return {Roo.ContentPanel} The active panel or null
35338      */
35339     getActivePanel : function(){
35340         return this.activePanel;
35341     },
35342     
35343     /**
35344      * Add the passed ContentPanel(s)
35345      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35346      * @return {Roo.ContentPanel} The panel added (if only one was added)
35347      */
35348     add : function(panel){
35349         if(arguments.length > 1){
35350             for(var i = 0, len = arguments.length; i < len; i++) {
35351                 this.add(arguments[i]);
35352             }
35353             return null;
35354         }
35355         if(this.hasPanel(panel)){
35356             this.showPanel(panel);
35357             return panel;
35358         }
35359         var el = panel.getEl();
35360         if(el.dom.parentNode != this.mgr.el.dom){
35361             this.mgr.el.dom.appendChild(el.dom);
35362         }
35363         if(panel.setRegion){
35364             panel.setRegion(this);
35365         }
35366         this.panels.add(panel);
35367         el.setStyle("position", "absolute");
35368         if(!panel.background){
35369             this.setActivePanel(panel);
35370             if(this.config.initialSize && this.panels.getCount()==1){
35371                 this.resizeTo(this.config.initialSize);
35372             }
35373         }
35374         this.fireEvent("paneladded", this, panel);
35375         return panel;
35376     },
35377     
35378     /**
35379      * Returns true if the panel is in this region.
35380      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35381      * @return {Boolean}
35382      */
35383     hasPanel : function(panel){
35384         if(typeof panel == "object"){ // must be panel obj
35385             panel = panel.getId();
35386         }
35387         return this.getPanel(panel) ? true : false;
35388     },
35389     
35390     /**
35391      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35392      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35393      * @param {Boolean} preservePanel Overrides the config preservePanel option
35394      * @return {Roo.ContentPanel} The panel that was removed
35395      */
35396     remove : function(panel, preservePanel){
35397         panel = this.getPanel(panel);
35398         if(!panel){
35399             return null;
35400         }
35401         var e = {};
35402         this.fireEvent("beforeremove", this, panel, e);
35403         if(e.cancel === true){
35404             return null;
35405         }
35406         var panelId = panel.getId();
35407         this.panels.removeKey(panelId);
35408         return panel;
35409     },
35410     
35411     /**
35412      * Returns the panel specified or null if it's not in this region.
35413      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35414      * @return {Roo.ContentPanel}
35415      */
35416     getPanel : function(id){
35417         if(typeof id == "object"){ // must be panel obj
35418             return id;
35419         }
35420         return this.panels.get(id);
35421     },
35422     
35423     /**
35424      * Returns this regions position (north/south/east/west/center).
35425      * @return {String} 
35426      */
35427     getPosition: function(){
35428         return this.position;    
35429     }
35430 });/*
35431  * Based on:
35432  * Ext JS Library 1.1.1
35433  * Copyright(c) 2006-2007, Ext JS, LLC.
35434  *
35435  * Originally Released Under LGPL - original licence link has changed is not relivant.
35436  *
35437  * Fork - LGPL
35438  * <script type="text/javascript">
35439  */
35440  
35441 /**
35442  * @class Roo.bootstrap.layout.Region
35443  * @extends Roo.bootstrap.layout.Basic
35444  * This class represents a region in a layout manager.
35445  
35446  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35447  * @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})
35448  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35449  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35450  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35451  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35452  * @cfg {String}    title           The title for the region (overrides panel titles)
35453  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35454  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35455  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35456  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35457  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35458  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35459  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35460  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35461  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35462  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35463
35464  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35465  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35466  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35467  * @cfg {Number}    width           For East/West panels
35468  * @cfg {Number}    height          For North/South panels
35469  * @cfg {Boolean}   split           To show the splitter
35470  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35471  * 
35472  * @cfg {string}   cls             Extra CSS classes to add to region
35473  * 
35474  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35475  * @cfg {string}   region  the region that it inhabits..
35476  *
35477
35478  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35479  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35480
35481  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35482  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35483  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35484  */
35485 Roo.bootstrap.layout.Region = function(config)
35486 {
35487     this.applyConfig(config);
35488
35489     var mgr = config.mgr;
35490     var pos = config.region;
35491     config.skipConfig = true;
35492     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35493     
35494     if (mgr.el) {
35495         this.onRender(mgr.el);   
35496     }
35497      
35498     this.visible = true;
35499     this.collapsed = false;
35500     this.unrendered_panels = [];
35501 };
35502
35503 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35504
35505     position: '', // set by wrapper (eg. north/south etc..)
35506     unrendered_panels : null,  // unrendered panels.
35507     createBody : function(){
35508         /** This region's body element 
35509         * @type Roo.Element */
35510         this.bodyEl = this.el.createChild({
35511                 tag: "div",
35512                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35513         });
35514     },
35515
35516     onRender: function(ctr, pos)
35517     {
35518         var dh = Roo.DomHelper;
35519         /** This region's container element 
35520         * @type Roo.Element */
35521         this.el = dh.append(ctr.dom, {
35522                 tag: "div",
35523                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35524             }, true);
35525         /** This region's title element 
35526         * @type Roo.Element */
35527     
35528         this.titleEl = dh.append(this.el.dom,
35529             {
35530                     tag: "div",
35531                     unselectable: "on",
35532                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35533                     children:[
35534                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35535                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35536                     ]}, true);
35537         
35538         this.titleEl.enableDisplayMode();
35539         /** This region's title text element 
35540         * @type HTMLElement */
35541         this.titleTextEl = this.titleEl.dom.firstChild;
35542         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35543         /*
35544         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35545         this.closeBtn.enableDisplayMode();
35546         this.closeBtn.on("click", this.closeClicked, this);
35547         this.closeBtn.hide();
35548     */
35549         this.createBody(this.config);
35550         if(this.config.hideWhenEmpty){
35551             this.hide();
35552             this.on("paneladded", this.validateVisibility, this);
35553             this.on("panelremoved", this.validateVisibility, this);
35554         }
35555         if(this.autoScroll){
35556             this.bodyEl.setStyle("overflow", "auto");
35557         }else{
35558             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35559         }
35560         //if(c.titlebar !== false){
35561             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35562                 this.titleEl.hide();
35563             }else{
35564                 this.titleEl.show();
35565                 if(this.config.title){
35566                     this.titleTextEl.innerHTML = this.config.title;
35567                 }
35568             }
35569         //}
35570         if(this.config.collapsed){
35571             this.collapse(true);
35572         }
35573         if(this.config.hidden){
35574             this.hide();
35575         }
35576         
35577         if (this.unrendered_panels && this.unrendered_panels.length) {
35578             for (var i =0;i< this.unrendered_panels.length; i++) {
35579                 this.add(this.unrendered_panels[i]);
35580             }
35581             this.unrendered_panels = null;
35582             
35583         }
35584         
35585     },
35586     
35587     applyConfig : function(c)
35588     {
35589         /*
35590          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35591             var dh = Roo.DomHelper;
35592             if(c.titlebar !== false){
35593                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35594                 this.collapseBtn.on("click", this.collapse, this);
35595                 this.collapseBtn.enableDisplayMode();
35596                 /*
35597                 if(c.showPin === true || this.showPin){
35598                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35599                     this.stickBtn.enableDisplayMode();
35600                     this.stickBtn.on("click", this.expand, this);
35601                     this.stickBtn.hide();
35602                 }
35603                 
35604             }
35605             */
35606             /** This region's collapsed element
35607             * @type Roo.Element */
35608             /*
35609              *
35610             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35611                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35612             ]}, true);
35613             
35614             if(c.floatable !== false){
35615                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35616                this.collapsedEl.on("click", this.collapseClick, this);
35617             }
35618
35619             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35620                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35621                    id: "message", unselectable: "on", style:{"float":"left"}});
35622                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35623              }
35624             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35625             this.expandBtn.on("click", this.expand, this);
35626             
35627         }
35628         
35629         if(this.collapseBtn){
35630             this.collapseBtn.setVisible(c.collapsible == true);
35631         }
35632         
35633         this.cmargins = c.cmargins || this.cmargins ||
35634                          (this.position == "west" || this.position == "east" ?
35635                              {top: 0, left: 2, right:2, bottom: 0} :
35636                              {top: 2, left: 0, right:0, bottom: 2});
35637         */
35638         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35639         
35640         
35641         this.bottomTabs = c.tabPosition != "top";
35642         
35643         this.autoScroll = c.autoScroll || false;
35644         
35645         
35646        
35647         
35648         this.duration = c.duration || .30;
35649         this.slideDuration = c.slideDuration || .45;
35650         this.config = c;
35651        
35652     },
35653     /**
35654      * Returns true if this region is currently visible.
35655      * @return {Boolean}
35656      */
35657     isVisible : function(){
35658         return this.visible;
35659     },
35660
35661     /**
35662      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35663      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35664      */
35665     //setCollapsedTitle : function(title){
35666     //    title = title || "&#160;";
35667      //   if(this.collapsedTitleTextEl){
35668       //      this.collapsedTitleTextEl.innerHTML = title;
35669        // }
35670     //},
35671
35672     getBox : function(){
35673         var b;
35674       //  if(!this.collapsed){
35675             b = this.el.getBox(false, true);
35676        // }else{
35677           //  b = this.collapsedEl.getBox(false, true);
35678         //}
35679         return b;
35680     },
35681
35682     getMargins : function(){
35683         return this.margins;
35684         //return this.collapsed ? this.cmargins : this.margins;
35685     },
35686 /*
35687     highlight : function(){
35688         this.el.addClass("x-layout-panel-dragover");
35689     },
35690
35691     unhighlight : function(){
35692         this.el.removeClass("x-layout-panel-dragover");
35693     },
35694 */
35695     updateBox : function(box)
35696     {
35697         if (!this.bodyEl) {
35698             return; // not rendered yet..
35699         }
35700         
35701         this.box = box;
35702         if(!this.collapsed){
35703             this.el.dom.style.left = box.x + "px";
35704             this.el.dom.style.top = box.y + "px";
35705             this.updateBody(box.width, box.height);
35706         }else{
35707             this.collapsedEl.dom.style.left = box.x + "px";
35708             this.collapsedEl.dom.style.top = box.y + "px";
35709             this.collapsedEl.setSize(box.width, box.height);
35710         }
35711         if(this.tabs){
35712             this.tabs.autoSizeTabs();
35713         }
35714     },
35715
35716     updateBody : function(w, h)
35717     {
35718         if(w !== null){
35719             this.el.setWidth(w);
35720             w -= this.el.getBorderWidth("rl");
35721             if(this.config.adjustments){
35722                 w += this.config.adjustments[0];
35723             }
35724         }
35725         if(h !== null && h > 0){
35726             this.el.setHeight(h);
35727             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35728             h -= this.el.getBorderWidth("tb");
35729             if(this.config.adjustments){
35730                 h += this.config.adjustments[1];
35731             }
35732             this.bodyEl.setHeight(h);
35733             if(this.tabs){
35734                 h = this.tabs.syncHeight(h);
35735             }
35736         }
35737         if(this.panelSize){
35738             w = w !== null ? w : this.panelSize.width;
35739             h = h !== null ? h : this.panelSize.height;
35740         }
35741         if(this.activePanel){
35742             var el = this.activePanel.getEl();
35743             w = w !== null ? w : el.getWidth();
35744             h = h !== null ? h : el.getHeight();
35745             this.panelSize = {width: w, height: h};
35746             this.activePanel.setSize(w, h);
35747         }
35748         if(Roo.isIE && this.tabs){
35749             this.tabs.el.repaint();
35750         }
35751     },
35752
35753     /**
35754      * Returns the container element for this region.
35755      * @return {Roo.Element}
35756      */
35757     getEl : function(){
35758         return this.el;
35759     },
35760
35761     /**
35762      * Hides this region.
35763      */
35764     hide : function(){
35765         //if(!this.collapsed){
35766             this.el.dom.style.left = "-2000px";
35767             this.el.hide();
35768         //}else{
35769          //   this.collapsedEl.dom.style.left = "-2000px";
35770          //   this.collapsedEl.hide();
35771        // }
35772         this.visible = false;
35773         this.fireEvent("visibilitychange", this, false);
35774     },
35775
35776     /**
35777      * Shows this region if it was previously hidden.
35778      */
35779     show : function(){
35780         //if(!this.collapsed){
35781             this.el.show();
35782         //}else{
35783         //    this.collapsedEl.show();
35784        // }
35785         this.visible = true;
35786         this.fireEvent("visibilitychange", this, true);
35787     },
35788 /*
35789     closeClicked : function(){
35790         if(this.activePanel){
35791             this.remove(this.activePanel);
35792         }
35793     },
35794
35795     collapseClick : function(e){
35796         if(this.isSlid){
35797            e.stopPropagation();
35798            this.slideIn();
35799         }else{
35800            e.stopPropagation();
35801            this.slideOut();
35802         }
35803     },
35804 */
35805     /**
35806      * Collapses this region.
35807      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35808      */
35809     /*
35810     collapse : function(skipAnim, skipCheck = false){
35811         if(this.collapsed) {
35812             return;
35813         }
35814         
35815         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35816             
35817             this.collapsed = true;
35818             if(this.split){
35819                 this.split.el.hide();
35820             }
35821             if(this.config.animate && skipAnim !== true){
35822                 this.fireEvent("invalidated", this);
35823                 this.animateCollapse();
35824             }else{
35825                 this.el.setLocation(-20000,-20000);
35826                 this.el.hide();
35827                 this.collapsedEl.show();
35828                 this.fireEvent("collapsed", this);
35829                 this.fireEvent("invalidated", this);
35830             }
35831         }
35832         
35833     },
35834 */
35835     animateCollapse : function(){
35836         // overridden
35837     },
35838
35839     /**
35840      * Expands this region if it was previously collapsed.
35841      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35842      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35843      */
35844     /*
35845     expand : function(e, skipAnim){
35846         if(e) {
35847             e.stopPropagation();
35848         }
35849         if(!this.collapsed || this.el.hasActiveFx()) {
35850             return;
35851         }
35852         if(this.isSlid){
35853             this.afterSlideIn();
35854             skipAnim = true;
35855         }
35856         this.collapsed = false;
35857         if(this.config.animate && skipAnim !== true){
35858             this.animateExpand();
35859         }else{
35860             this.el.show();
35861             if(this.split){
35862                 this.split.el.show();
35863             }
35864             this.collapsedEl.setLocation(-2000,-2000);
35865             this.collapsedEl.hide();
35866             this.fireEvent("invalidated", this);
35867             this.fireEvent("expanded", this);
35868         }
35869     },
35870 */
35871     animateExpand : function(){
35872         // overridden
35873     },
35874
35875     initTabs : function()
35876     {
35877         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35878         
35879         var ts = new Roo.bootstrap.panel.Tabs({
35880                 el: this.bodyEl.dom,
35881                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35882                 disableTooltips: this.config.disableTabTips,
35883                 toolbar : this.config.toolbar
35884             });
35885         
35886         if(this.config.hideTabs){
35887             ts.stripWrap.setDisplayed(false);
35888         }
35889         this.tabs = ts;
35890         ts.resizeTabs = this.config.resizeTabs === true;
35891         ts.minTabWidth = this.config.minTabWidth || 40;
35892         ts.maxTabWidth = this.config.maxTabWidth || 250;
35893         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35894         ts.monitorResize = false;
35895         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35896         ts.bodyEl.addClass('roo-layout-tabs-body');
35897         this.panels.each(this.initPanelAsTab, this);
35898     },
35899
35900     initPanelAsTab : function(panel){
35901         var ti = this.tabs.addTab(
35902             panel.getEl().id,
35903             panel.getTitle(),
35904             null,
35905             this.config.closeOnTab && panel.isClosable(),
35906             panel.tpl
35907         );
35908         if(panel.tabTip !== undefined){
35909             ti.setTooltip(panel.tabTip);
35910         }
35911         ti.on("activate", function(){
35912               this.setActivePanel(panel);
35913         }, this);
35914         
35915         if(this.config.closeOnTab){
35916             ti.on("beforeclose", function(t, e){
35917                 e.cancel = true;
35918                 this.remove(panel);
35919             }, this);
35920         }
35921         
35922         panel.tabItem = ti;
35923         
35924         return ti;
35925     },
35926
35927     updatePanelTitle : function(panel, title)
35928     {
35929         if(this.activePanel == panel){
35930             this.updateTitle(title);
35931         }
35932         if(this.tabs){
35933             var ti = this.tabs.getTab(panel.getEl().id);
35934             ti.setText(title);
35935             if(panel.tabTip !== undefined){
35936                 ti.setTooltip(panel.tabTip);
35937             }
35938         }
35939     },
35940
35941     updateTitle : function(title){
35942         if(this.titleTextEl && !this.config.title){
35943             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35944         }
35945     },
35946
35947     setActivePanel : function(panel)
35948     {
35949         panel = this.getPanel(panel);
35950         if(this.activePanel && this.activePanel != panel){
35951             if(this.activePanel.setActiveState(false) === false){
35952                 return;
35953             }
35954         }
35955         this.activePanel = panel;
35956         panel.setActiveState(true);
35957         if(this.panelSize){
35958             panel.setSize(this.panelSize.width, this.panelSize.height);
35959         }
35960         if(this.closeBtn){
35961             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35962         }
35963         this.updateTitle(panel.getTitle());
35964         if(this.tabs){
35965             this.fireEvent("invalidated", this);
35966         }
35967         this.fireEvent("panelactivated", this, panel);
35968     },
35969
35970     /**
35971      * Shows the specified panel.
35972      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35973      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35974      */
35975     showPanel : function(panel)
35976     {
35977         panel = this.getPanel(panel);
35978         if(panel){
35979             if(this.tabs){
35980                 var tab = this.tabs.getTab(panel.getEl().id);
35981                 if(tab.isHidden()){
35982                     this.tabs.unhideTab(tab.id);
35983                 }
35984                 tab.activate();
35985             }else{
35986                 this.setActivePanel(panel);
35987             }
35988         }
35989         return panel;
35990     },
35991
35992     /**
35993      * Get the active panel for this region.
35994      * @return {Roo.ContentPanel} The active panel or null
35995      */
35996     getActivePanel : function(){
35997         return this.activePanel;
35998     },
35999
36000     validateVisibility : function(){
36001         if(this.panels.getCount() < 1){
36002             this.updateTitle("&#160;");
36003             this.closeBtn.hide();
36004             this.hide();
36005         }else{
36006             if(!this.isVisible()){
36007                 this.show();
36008             }
36009         }
36010     },
36011
36012     /**
36013      * Adds the passed ContentPanel(s) to this region.
36014      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36015      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36016      */
36017     add : function(panel)
36018     {
36019         if(arguments.length > 1){
36020             for(var i = 0, len = arguments.length; i < len; i++) {
36021                 this.add(arguments[i]);
36022             }
36023             return null;
36024         }
36025         
36026         // if we have not been rendered yet, then we can not really do much of this..
36027         if (!this.bodyEl) {
36028             this.unrendered_panels.push(panel);
36029             return panel;
36030         }
36031         
36032         
36033         
36034         
36035         if(this.hasPanel(panel)){
36036             this.showPanel(panel);
36037             return panel;
36038         }
36039         panel.setRegion(this);
36040         this.panels.add(panel);
36041        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36042             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36043             // and hide them... ???
36044             this.bodyEl.dom.appendChild(panel.getEl().dom);
36045             if(panel.background !== true){
36046                 this.setActivePanel(panel);
36047             }
36048             this.fireEvent("paneladded", this, panel);
36049             return panel;
36050         }
36051         */
36052         if(!this.tabs){
36053             this.initTabs();
36054         }else{
36055             this.initPanelAsTab(panel);
36056         }
36057         
36058         
36059         if(panel.background !== true){
36060             this.tabs.activate(panel.getEl().id);
36061         }
36062         this.fireEvent("paneladded", this, panel);
36063         return panel;
36064     },
36065
36066     /**
36067      * Hides the tab for the specified panel.
36068      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36069      */
36070     hidePanel : function(panel){
36071         if(this.tabs && (panel = this.getPanel(panel))){
36072             this.tabs.hideTab(panel.getEl().id);
36073         }
36074     },
36075
36076     /**
36077      * Unhides the tab for a previously hidden panel.
36078      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36079      */
36080     unhidePanel : function(panel){
36081         if(this.tabs && (panel = this.getPanel(panel))){
36082             this.tabs.unhideTab(panel.getEl().id);
36083         }
36084     },
36085
36086     clearPanels : function(){
36087         while(this.panels.getCount() > 0){
36088              this.remove(this.panels.first());
36089         }
36090     },
36091
36092     /**
36093      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36094      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36095      * @param {Boolean} preservePanel Overrides the config preservePanel option
36096      * @return {Roo.ContentPanel} The panel that was removed
36097      */
36098     remove : function(panel, preservePanel)
36099     {
36100         panel = this.getPanel(panel);
36101         if(!panel){
36102             return null;
36103         }
36104         var e = {};
36105         this.fireEvent("beforeremove", this, panel, e);
36106         if(e.cancel === true){
36107             return null;
36108         }
36109         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36110         var panelId = panel.getId();
36111         this.panels.removeKey(panelId);
36112         if(preservePanel){
36113             document.body.appendChild(panel.getEl().dom);
36114         }
36115         if(this.tabs){
36116             this.tabs.removeTab(panel.getEl().id);
36117         }else if (!preservePanel){
36118             this.bodyEl.dom.removeChild(panel.getEl().dom);
36119         }
36120         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36121             var p = this.panels.first();
36122             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36123             tempEl.appendChild(p.getEl().dom);
36124             this.bodyEl.update("");
36125             this.bodyEl.dom.appendChild(p.getEl().dom);
36126             tempEl = null;
36127             this.updateTitle(p.getTitle());
36128             this.tabs = null;
36129             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36130             this.setActivePanel(p);
36131         }
36132         panel.setRegion(null);
36133         if(this.activePanel == panel){
36134             this.activePanel = null;
36135         }
36136         if(this.config.autoDestroy !== false && preservePanel !== true){
36137             try{panel.destroy();}catch(e){}
36138         }
36139         this.fireEvent("panelremoved", this, panel);
36140         return panel;
36141     },
36142
36143     /**
36144      * Returns the TabPanel component used by this region
36145      * @return {Roo.TabPanel}
36146      */
36147     getTabs : function(){
36148         return this.tabs;
36149     },
36150
36151     createTool : function(parentEl, className){
36152         var btn = Roo.DomHelper.append(parentEl, {
36153             tag: "div",
36154             cls: "x-layout-tools-button",
36155             children: [ {
36156                 tag: "div",
36157                 cls: "roo-layout-tools-button-inner " + className,
36158                 html: "&#160;"
36159             }]
36160         }, true);
36161         btn.addClassOnOver("roo-layout-tools-button-over");
36162         return btn;
36163     }
36164 });/*
36165  * Based on:
36166  * Ext JS Library 1.1.1
36167  * Copyright(c) 2006-2007, Ext JS, LLC.
36168  *
36169  * Originally Released Under LGPL - original licence link has changed is not relivant.
36170  *
36171  * Fork - LGPL
36172  * <script type="text/javascript">
36173  */
36174  
36175
36176
36177 /**
36178  * @class Roo.SplitLayoutRegion
36179  * @extends Roo.LayoutRegion
36180  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36181  */
36182 Roo.bootstrap.layout.Split = function(config){
36183     this.cursor = config.cursor;
36184     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36185 };
36186
36187 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36188 {
36189     splitTip : "Drag to resize.",
36190     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36191     useSplitTips : false,
36192
36193     applyConfig : function(config){
36194         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36195     },
36196     
36197     onRender : function(ctr,pos) {
36198         
36199         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36200         if(!this.config.split){
36201             return;
36202         }
36203         if(!this.split){
36204             
36205             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36206                             tag: "div",
36207                             id: this.el.id + "-split",
36208                             cls: "roo-layout-split roo-layout-split-"+this.position,
36209                             html: "&#160;"
36210             });
36211             /** The SplitBar for this region 
36212             * @type Roo.SplitBar */
36213             // does not exist yet...
36214             Roo.log([this.position, this.orientation]);
36215             
36216             this.split = new Roo.bootstrap.SplitBar({
36217                 dragElement : splitEl,
36218                 resizingElement: this.el,
36219                 orientation : this.orientation
36220             });
36221             
36222             this.split.on("moved", this.onSplitMove, this);
36223             this.split.useShim = this.config.useShim === true;
36224             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36225             if(this.useSplitTips){
36226                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36227             }
36228             //if(config.collapsible){
36229             //    this.split.el.on("dblclick", this.collapse,  this);
36230             //}
36231         }
36232         if(typeof this.config.minSize != "undefined"){
36233             this.split.minSize = this.config.minSize;
36234         }
36235         if(typeof this.config.maxSize != "undefined"){
36236             this.split.maxSize = this.config.maxSize;
36237         }
36238         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36239             this.hideSplitter();
36240         }
36241         
36242     },
36243
36244     getHMaxSize : function(){
36245          var cmax = this.config.maxSize || 10000;
36246          var center = this.mgr.getRegion("center");
36247          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36248     },
36249
36250     getVMaxSize : function(){
36251          var cmax = this.config.maxSize || 10000;
36252          var center = this.mgr.getRegion("center");
36253          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36254     },
36255
36256     onSplitMove : function(split, newSize){
36257         this.fireEvent("resized", this, newSize);
36258     },
36259     
36260     /** 
36261      * Returns the {@link Roo.SplitBar} for this region.
36262      * @return {Roo.SplitBar}
36263      */
36264     getSplitBar : function(){
36265         return this.split;
36266     },
36267     
36268     hide : function(){
36269         this.hideSplitter();
36270         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36271     },
36272
36273     hideSplitter : function(){
36274         if(this.split){
36275             this.split.el.setLocation(-2000,-2000);
36276             this.split.el.hide();
36277         }
36278     },
36279
36280     show : function(){
36281         if(this.split){
36282             this.split.el.show();
36283         }
36284         Roo.bootstrap.layout.Split.superclass.show.call(this);
36285     },
36286     
36287     beforeSlide: function(){
36288         if(Roo.isGecko){// firefox overflow auto bug workaround
36289             this.bodyEl.clip();
36290             if(this.tabs) {
36291                 this.tabs.bodyEl.clip();
36292             }
36293             if(this.activePanel){
36294                 this.activePanel.getEl().clip();
36295                 
36296                 if(this.activePanel.beforeSlide){
36297                     this.activePanel.beforeSlide();
36298                 }
36299             }
36300         }
36301     },
36302     
36303     afterSlide : function(){
36304         if(Roo.isGecko){// firefox overflow auto bug workaround
36305             this.bodyEl.unclip();
36306             if(this.tabs) {
36307                 this.tabs.bodyEl.unclip();
36308             }
36309             if(this.activePanel){
36310                 this.activePanel.getEl().unclip();
36311                 if(this.activePanel.afterSlide){
36312                     this.activePanel.afterSlide();
36313                 }
36314             }
36315         }
36316     },
36317
36318     initAutoHide : function(){
36319         if(this.autoHide !== false){
36320             if(!this.autoHideHd){
36321                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36322                 this.autoHideHd = {
36323                     "mouseout": function(e){
36324                         if(!e.within(this.el, true)){
36325                             st.delay(500);
36326                         }
36327                     },
36328                     "mouseover" : function(e){
36329                         st.cancel();
36330                     },
36331                     scope : this
36332                 };
36333             }
36334             this.el.on(this.autoHideHd);
36335         }
36336     },
36337
36338     clearAutoHide : function(){
36339         if(this.autoHide !== false){
36340             this.el.un("mouseout", this.autoHideHd.mouseout);
36341             this.el.un("mouseover", this.autoHideHd.mouseover);
36342         }
36343     },
36344
36345     clearMonitor : function(){
36346         Roo.get(document).un("click", this.slideInIf, this);
36347     },
36348
36349     // these names are backwards but not changed for compat
36350     slideOut : function(){
36351         if(this.isSlid || this.el.hasActiveFx()){
36352             return;
36353         }
36354         this.isSlid = true;
36355         if(this.collapseBtn){
36356             this.collapseBtn.hide();
36357         }
36358         this.closeBtnState = this.closeBtn.getStyle('display');
36359         this.closeBtn.hide();
36360         if(this.stickBtn){
36361             this.stickBtn.show();
36362         }
36363         this.el.show();
36364         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36365         this.beforeSlide();
36366         this.el.setStyle("z-index", 10001);
36367         this.el.slideIn(this.getSlideAnchor(), {
36368             callback: function(){
36369                 this.afterSlide();
36370                 this.initAutoHide();
36371                 Roo.get(document).on("click", this.slideInIf, this);
36372                 this.fireEvent("slideshow", this);
36373             },
36374             scope: this,
36375             block: true
36376         });
36377     },
36378
36379     afterSlideIn : function(){
36380         this.clearAutoHide();
36381         this.isSlid = false;
36382         this.clearMonitor();
36383         this.el.setStyle("z-index", "");
36384         if(this.collapseBtn){
36385             this.collapseBtn.show();
36386         }
36387         this.closeBtn.setStyle('display', this.closeBtnState);
36388         if(this.stickBtn){
36389             this.stickBtn.hide();
36390         }
36391         this.fireEvent("slidehide", this);
36392     },
36393
36394     slideIn : function(cb){
36395         if(!this.isSlid || this.el.hasActiveFx()){
36396             Roo.callback(cb);
36397             return;
36398         }
36399         this.isSlid = false;
36400         this.beforeSlide();
36401         this.el.slideOut(this.getSlideAnchor(), {
36402             callback: function(){
36403                 this.el.setLeftTop(-10000, -10000);
36404                 this.afterSlide();
36405                 this.afterSlideIn();
36406                 Roo.callback(cb);
36407             },
36408             scope: this,
36409             block: true
36410         });
36411     },
36412     
36413     slideInIf : function(e){
36414         if(!e.within(this.el)){
36415             this.slideIn();
36416         }
36417     },
36418
36419     animateCollapse : function(){
36420         this.beforeSlide();
36421         this.el.setStyle("z-index", 20000);
36422         var anchor = this.getSlideAnchor();
36423         this.el.slideOut(anchor, {
36424             callback : function(){
36425                 this.el.setStyle("z-index", "");
36426                 this.collapsedEl.slideIn(anchor, {duration:.3});
36427                 this.afterSlide();
36428                 this.el.setLocation(-10000,-10000);
36429                 this.el.hide();
36430                 this.fireEvent("collapsed", this);
36431             },
36432             scope: this,
36433             block: true
36434         });
36435     },
36436
36437     animateExpand : function(){
36438         this.beforeSlide();
36439         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36440         this.el.setStyle("z-index", 20000);
36441         this.collapsedEl.hide({
36442             duration:.1
36443         });
36444         this.el.slideIn(this.getSlideAnchor(), {
36445             callback : function(){
36446                 this.el.setStyle("z-index", "");
36447                 this.afterSlide();
36448                 if(this.split){
36449                     this.split.el.show();
36450                 }
36451                 this.fireEvent("invalidated", this);
36452                 this.fireEvent("expanded", this);
36453             },
36454             scope: this,
36455             block: true
36456         });
36457     },
36458
36459     anchors : {
36460         "west" : "left",
36461         "east" : "right",
36462         "north" : "top",
36463         "south" : "bottom"
36464     },
36465
36466     sanchors : {
36467         "west" : "l",
36468         "east" : "r",
36469         "north" : "t",
36470         "south" : "b"
36471     },
36472
36473     canchors : {
36474         "west" : "tl-tr",
36475         "east" : "tr-tl",
36476         "north" : "tl-bl",
36477         "south" : "bl-tl"
36478     },
36479
36480     getAnchor : function(){
36481         return this.anchors[this.position];
36482     },
36483
36484     getCollapseAnchor : function(){
36485         return this.canchors[this.position];
36486     },
36487
36488     getSlideAnchor : function(){
36489         return this.sanchors[this.position];
36490     },
36491
36492     getAlignAdj : function(){
36493         var cm = this.cmargins;
36494         switch(this.position){
36495             case "west":
36496                 return [0, 0];
36497             break;
36498             case "east":
36499                 return [0, 0];
36500             break;
36501             case "north":
36502                 return [0, 0];
36503             break;
36504             case "south":
36505                 return [0, 0];
36506             break;
36507         }
36508     },
36509
36510     getExpandAdj : function(){
36511         var c = this.collapsedEl, cm = this.cmargins;
36512         switch(this.position){
36513             case "west":
36514                 return [-(cm.right+c.getWidth()+cm.left), 0];
36515             break;
36516             case "east":
36517                 return [cm.right+c.getWidth()+cm.left, 0];
36518             break;
36519             case "north":
36520                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36521             break;
36522             case "south":
36523                 return [0, cm.top+cm.bottom+c.getHeight()];
36524             break;
36525         }
36526     }
36527 });/*
36528  * Based on:
36529  * Ext JS Library 1.1.1
36530  * Copyright(c) 2006-2007, Ext JS, LLC.
36531  *
36532  * Originally Released Under LGPL - original licence link has changed is not relivant.
36533  *
36534  * Fork - LGPL
36535  * <script type="text/javascript">
36536  */
36537 /*
36538  * These classes are private internal classes
36539  */
36540 Roo.bootstrap.layout.Center = function(config){
36541     config.region = "center";
36542     Roo.bootstrap.layout.Region.call(this, config);
36543     this.visible = true;
36544     this.minWidth = config.minWidth || 20;
36545     this.minHeight = config.minHeight || 20;
36546 };
36547
36548 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36549     hide : function(){
36550         // center panel can't be hidden
36551     },
36552     
36553     show : function(){
36554         // center panel can't be hidden
36555     },
36556     
36557     getMinWidth: function(){
36558         return this.minWidth;
36559     },
36560     
36561     getMinHeight: function(){
36562         return this.minHeight;
36563     }
36564 });
36565
36566
36567
36568
36569  
36570
36571
36572
36573
36574
36575 Roo.bootstrap.layout.North = function(config)
36576 {
36577     config.region = 'north';
36578     config.cursor = 'n-resize';
36579     
36580     Roo.bootstrap.layout.Split.call(this, config);
36581     
36582     
36583     if(this.split){
36584         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36585         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36586         this.split.el.addClass("roo-layout-split-v");
36587     }
36588     var size = config.initialSize || config.height;
36589     if(typeof size != "undefined"){
36590         this.el.setHeight(size);
36591     }
36592 };
36593 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36594 {
36595     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36596     
36597     
36598     
36599     getBox : function(){
36600         if(this.collapsed){
36601             return this.collapsedEl.getBox();
36602         }
36603         var box = this.el.getBox();
36604         if(this.split){
36605             box.height += this.split.el.getHeight();
36606         }
36607         return box;
36608     },
36609     
36610     updateBox : function(box){
36611         if(this.split && !this.collapsed){
36612             box.height -= this.split.el.getHeight();
36613             this.split.el.setLeft(box.x);
36614             this.split.el.setTop(box.y+box.height);
36615             this.split.el.setWidth(box.width);
36616         }
36617         if(this.collapsed){
36618             this.updateBody(box.width, null);
36619         }
36620         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36621     }
36622 });
36623
36624
36625
36626
36627
36628 Roo.bootstrap.layout.South = function(config){
36629     config.region = 'south';
36630     config.cursor = 's-resize';
36631     Roo.bootstrap.layout.Split.call(this, config);
36632     if(this.split){
36633         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36634         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36635         this.split.el.addClass("roo-layout-split-v");
36636     }
36637     var size = config.initialSize || config.height;
36638     if(typeof size != "undefined"){
36639         this.el.setHeight(size);
36640     }
36641 };
36642
36643 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36644     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36645     getBox : function(){
36646         if(this.collapsed){
36647             return this.collapsedEl.getBox();
36648         }
36649         var box = this.el.getBox();
36650         if(this.split){
36651             var sh = this.split.el.getHeight();
36652             box.height += sh;
36653             box.y -= sh;
36654         }
36655         return box;
36656     },
36657     
36658     updateBox : function(box){
36659         if(this.split && !this.collapsed){
36660             var sh = this.split.el.getHeight();
36661             box.height -= sh;
36662             box.y += sh;
36663             this.split.el.setLeft(box.x);
36664             this.split.el.setTop(box.y-sh);
36665             this.split.el.setWidth(box.width);
36666         }
36667         if(this.collapsed){
36668             this.updateBody(box.width, null);
36669         }
36670         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36671     }
36672 });
36673
36674 Roo.bootstrap.layout.East = function(config){
36675     config.region = "east";
36676     config.cursor = "e-resize";
36677     Roo.bootstrap.layout.Split.call(this, config);
36678     if(this.split){
36679         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36680         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36681         this.split.el.addClass("roo-layout-split-h");
36682     }
36683     var size = config.initialSize || config.width;
36684     if(typeof size != "undefined"){
36685         this.el.setWidth(size);
36686     }
36687 };
36688 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36689     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36690     getBox : function(){
36691         if(this.collapsed){
36692             return this.collapsedEl.getBox();
36693         }
36694         var box = this.el.getBox();
36695         if(this.split){
36696             var sw = this.split.el.getWidth();
36697             box.width += sw;
36698             box.x -= sw;
36699         }
36700         return box;
36701     },
36702
36703     updateBox : function(box){
36704         if(this.split && !this.collapsed){
36705             var sw = this.split.el.getWidth();
36706             box.width -= sw;
36707             this.split.el.setLeft(box.x);
36708             this.split.el.setTop(box.y);
36709             this.split.el.setHeight(box.height);
36710             box.x += sw;
36711         }
36712         if(this.collapsed){
36713             this.updateBody(null, box.height);
36714         }
36715         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36716     }
36717 });
36718
36719 Roo.bootstrap.layout.West = function(config){
36720     config.region = "west";
36721     config.cursor = "w-resize";
36722     
36723     Roo.bootstrap.layout.Split.call(this, config);
36724     if(this.split){
36725         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36726         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36727         this.split.el.addClass("roo-layout-split-h");
36728     }
36729     
36730 };
36731 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36732     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36733     
36734     onRender: function(ctr, pos)
36735     {
36736         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36737         var size = this.config.initialSize || this.config.width;
36738         if(typeof size != "undefined"){
36739             this.el.setWidth(size);
36740         }
36741     },
36742     
36743     getBox : function(){
36744         if(this.collapsed){
36745             return this.collapsedEl.getBox();
36746         }
36747         var box = this.el.getBox();
36748         if(this.split){
36749             box.width += this.split.el.getWidth();
36750         }
36751         return box;
36752     },
36753     
36754     updateBox : function(box){
36755         if(this.split && !this.collapsed){
36756             var sw = this.split.el.getWidth();
36757             box.width -= sw;
36758             this.split.el.setLeft(box.x+box.width);
36759             this.split.el.setTop(box.y);
36760             this.split.el.setHeight(box.height);
36761         }
36762         if(this.collapsed){
36763             this.updateBody(null, box.height);
36764         }
36765         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36766     }
36767 });
36768 Roo.namespace("Roo.bootstrap.panel");/*
36769  * Based on:
36770  * Ext JS Library 1.1.1
36771  * Copyright(c) 2006-2007, Ext JS, LLC.
36772  *
36773  * Originally Released Under LGPL - original licence link has changed is not relivant.
36774  *
36775  * Fork - LGPL
36776  * <script type="text/javascript">
36777  */
36778 /**
36779  * @class Roo.ContentPanel
36780  * @extends Roo.util.Observable
36781  * A basic ContentPanel element.
36782  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36783  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36784  * @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
36785  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36786  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36787  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36788  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36789  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36790  * @cfg {String} title          The title for this panel
36791  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36792  * @cfg {String} url            Calls {@link #setUrl} with this value
36793  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36794  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36795  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36796  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36797  * @cfg {Boolean} badges render the badges
36798
36799  * @constructor
36800  * Create a new ContentPanel.
36801  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36802  * @param {String/Object} config A string to set only the title or a config object
36803  * @param {String} content (optional) Set the HTML content for this panel
36804  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36805  */
36806 Roo.bootstrap.panel.Content = function( config){
36807     
36808     this.tpl = config.tpl || false;
36809     
36810     var el = config.el;
36811     var content = config.content;
36812
36813     if(config.autoCreate){ // xtype is available if this is called from factory
36814         el = Roo.id();
36815     }
36816     this.el = Roo.get(el);
36817     if(!this.el && config && config.autoCreate){
36818         if(typeof config.autoCreate == "object"){
36819             if(!config.autoCreate.id){
36820                 config.autoCreate.id = config.id||el;
36821             }
36822             this.el = Roo.DomHelper.append(document.body,
36823                         config.autoCreate, true);
36824         }else{
36825             var elcfg =  {   tag: "div",
36826                             cls: "roo-layout-inactive-content",
36827                             id: config.id||el
36828                             };
36829             if (config.html) {
36830                 elcfg.html = config.html;
36831                 
36832             }
36833                         
36834             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36835         }
36836     } 
36837     this.closable = false;
36838     this.loaded = false;
36839     this.active = false;
36840    
36841       
36842     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36843         
36844         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36845         
36846         this.wrapEl = this.el; //this.el.wrap();
36847         var ti = [];
36848         if (config.toolbar.items) {
36849             ti = config.toolbar.items ;
36850             delete config.toolbar.items ;
36851         }
36852         
36853         var nitems = [];
36854         this.toolbar.render(this.wrapEl, 'before');
36855         for(var i =0;i < ti.length;i++) {
36856           //  Roo.log(['add child', items[i]]);
36857             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36858         }
36859         this.toolbar.items = nitems;
36860         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36861         delete config.toolbar;
36862         
36863     }
36864     /*
36865     // xtype created footer. - not sure if will work as we normally have to render first..
36866     if (this.footer && !this.footer.el && this.footer.xtype) {
36867         if (!this.wrapEl) {
36868             this.wrapEl = this.el.wrap();
36869         }
36870     
36871         this.footer.container = this.wrapEl.createChild();
36872          
36873         this.footer = Roo.factory(this.footer, Roo);
36874         
36875     }
36876     */
36877     
36878      if(typeof config == "string"){
36879         this.title = config;
36880     }else{
36881         Roo.apply(this, config);
36882     }
36883     
36884     if(this.resizeEl){
36885         this.resizeEl = Roo.get(this.resizeEl, true);
36886     }else{
36887         this.resizeEl = this.el;
36888     }
36889     // handle view.xtype
36890     
36891  
36892     
36893     
36894     this.addEvents({
36895         /**
36896          * @event activate
36897          * Fires when this panel is activated. 
36898          * @param {Roo.ContentPanel} this
36899          */
36900         "activate" : true,
36901         /**
36902          * @event deactivate
36903          * Fires when this panel is activated. 
36904          * @param {Roo.ContentPanel} this
36905          */
36906         "deactivate" : true,
36907
36908         /**
36909          * @event resize
36910          * Fires when this panel is resized if fitToFrame is true.
36911          * @param {Roo.ContentPanel} this
36912          * @param {Number} width The width after any component adjustments
36913          * @param {Number} height The height after any component adjustments
36914          */
36915         "resize" : true,
36916         
36917          /**
36918          * @event render
36919          * Fires when this tab is created
36920          * @param {Roo.ContentPanel} this
36921          */
36922         "render" : true
36923         
36924         
36925         
36926     });
36927     
36928
36929     
36930     
36931     if(this.autoScroll){
36932         this.resizeEl.setStyle("overflow", "auto");
36933     } else {
36934         // fix randome scrolling
36935         //this.el.on('scroll', function() {
36936         //    Roo.log('fix random scolling');
36937         //    this.scrollTo('top',0); 
36938         //});
36939     }
36940     content = content || this.content;
36941     if(content){
36942         this.setContent(content);
36943     }
36944     if(config && config.url){
36945         this.setUrl(this.url, this.params, this.loadOnce);
36946     }
36947     
36948     
36949     
36950     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36951     
36952     if (this.view && typeof(this.view.xtype) != 'undefined') {
36953         this.view.el = this.el.appendChild(document.createElement("div"));
36954         this.view = Roo.factory(this.view); 
36955         this.view.render  &&  this.view.render(false, '');  
36956     }
36957     
36958     
36959     this.fireEvent('render', this);
36960 };
36961
36962 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36963     
36964     tabTip : '',
36965     
36966     setRegion : function(region){
36967         this.region = region;
36968         this.setActiveClass(region && !this.background);
36969     },
36970     
36971     
36972     setActiveClass: function(state)
36973     {
36974         if(state){
36975            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36976            this.el.setStyle('position','relative');
36977         }else{
36978            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36979            this.el.setStyle('position', 'absolute');
36980         } 
36981     },
36982     
36983     /**
36984      * Returns the toolbar for this Panel if one was configured. 
36985      * @return {Roo.Toolbar} 
36986      */
36987     getToolbar : function(){
36988         return this.toolbar;
36989     },
36990     
36991     setActiveState : function(active)
36992     {
36993         this.active = active;
36994         this.setActiveClass(active);
36995         if(!active){
36996             if(this.fireEvent("deactivate", this) === false){
36997                 return false;
36998             }
36999             return true;
37000         }
37001         this.fireEvent("activate", this);
37002         return true;
37003     },
37004     /**
37005      * Updates this panel's element
37006      * @param {String} content The new content
37007      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37008     */
37009     setContent : function(content, loadScripts){
37010         this.el.update(content, loadScripts);
37011     },
37012
37013     ignoreResize : function(w, h){
37014         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37015             return true;
37016         }else{
37017             this.lastSize = {width: w, height: h};
37018             return false;
37019         }
37020     },
37021     /**
37022      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37023      * @return {Roo.UpdateManager} The UpdateManager
37024      */
37025     getUpdateManager : function(){
37026         return this.el.getUpdateManager();
37027     },
37028      /**
37029      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37030      * @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:
37031 <pre><code>
37032 panel.load({
37033     url: "your-url.php",
37034     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37035     callback: yourFunction,
37036     scope: yourObject, //(optional scope)
37037     discardUrl: false,
37038     nocache: false,
37039     text: "Loading...",
37040     timeout: 30,
37041     scripts: false
37042 });
37043 </code></pre>
37044      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37045      * 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.
37046      * @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}
37047      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37048      * @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.
37049      * @return {Roo.ContentPanel} this
37050      */
37051     load : function(){
37052         var um = this.el.getUpdateManager();
37053         um.update.apply(um, arguments);
37054         return this;
37055     },
37056
37057
37058     /**
37059      * 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.
37060      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37061      * @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)
37062      * @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)
37063      * @return {Roo.UpdateManager} The UpdateManager
37064      */
37065     setUrl : function(url, params, loadOnce){
37066         if(this.refreshDelegate){
37067             this.removeListener("activate", this.refreshDelegate);
37068         }
37069         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37070         this.on("activate", this.refreshDelegate);
37071         return this.el.getUpdateManager();
37072     },
37073     
37074     _handleRefresh : function(url, params, loadOnce){
37075         if(!loadOnce || !this.loaded){
37076             var updater = this.el.getUpdateManager();
37077             updater.update(url, params, this._setLoaded.createDelegate(this));
37078         }
37079     },
37080     
37081     _setLoaded : function(){
37082         this.loaded = true;
37083     }, 
37084     
37085     /**
37086      * Returns this panel's id
37087      * @return {String} 
37088      */
37089     getId : function(){
37090         return this.el.id;
37091     },
37092     
37093     /** 
37094      * Returns this panel's element - used by regiosn to add.
37095      * @return {Roo.Element} 
37096      */
37097     getEl : function(){
37098         return this.wrapEl || this.el;
37099     },
37100     
37101    
37102     
37103     adjustForComponents : function(width, height)
37104     {
37105         //Roo.log('adjustForComponents ');
37106         if(this.resizeEl != this.el){
37107             width -= this.el.getFrameWidth('lr');
37108             height -= this.el.getFrameWidth('tb');
37109         }
37110         if(this.toolbar){
37111             var te = this.toolbar.getEl();
37112             te.setWidth(width);
37113             height -= te.getHeight();
37114         }
37115         if(this.footer){
37116             var te = this.footer.getEl();
37117             te.setWidth(width);
37118             height -= te.getHeight();
37119         }
37120         
37121         
37122         if(this.adjustments){
37123             width += this.adjustments[0];
37124             height += this.adjustments[1];
37125         }
37126         return {"width": width, "height": height};
37127     },
37128     
37129     setSize : function(width, height){
37130         if(this.fitToFrame && !this.ignoreResize(width, height)){
37131             if(this.fitContainer && this.resizeEl != this.el){
37132                 this.el.setSize(width, height);
37133             }
37134             var size = this.adjustForComponents(width, height);
37135             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37136             this.fireEvent('resize', this, size.width, size.height);
37137         }
37138     },
37139     
37140     /**
37141      * Returns this panel's title
37142      * @return {String} 
37143      */
37144     getTitle : function(){
37145         
37146         if (typeof(this.title) != 'object') {
37147             return this.title;
37148         }
37149         
37150         var t = '';
37151         for (var k in this.title) {
37152             if (!this.title.hasOwnProperty(k)) {
37153                 continue;
37154             }
37155             
37156             if (k.indexOf('-') >= 0) {
37157                 var s = k.split('-');
37158                 for (var i = 0; i<s.length; i++) {
37159                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37160                 }
37161             } else {
37162                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37163             }
37164         }
37165         return t;
37166     },
37167     
37168     /**
37169      * Set this panel's title
37170      * @param {String} title
37171      */
37172     setTitle : function(title){
37173         this.title = title;
37174         if(this.region){
37175             this.region.updatePanelTitle(this, title);
37176         }
37177     },
37178     
37179     /**
37180      * Returns true is this panel was configured to be closable
37181      * @return {Boolean} 
37182      */
37183     isClosable : function(){
37184         return this.closable;
37185     },
37186     
37187     beforeSlide : function(){
37188         this.el.clip();
37189         this.resizeEl.clip();
37190     },
37191     
37192     afterSlide : function(){
37193         this.el.unclip();
37194         this.resizeEl.unclip();
37195     },
37196     
37197     /**
37198      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37199      *   Will fail silently if the {@link #setUrl} method has not been called.
37200      *   This does not activate the panel, just updates its content.
37201      */
37202     refresh : function(){
37203         if(this.refreshDelegate){
37204            this.loaded = false;
37205            this.refreshDelegate();
37206         }
37207     },
37208     
37209     /**
37210      * Destroys this panel
37211      */
37212     destroy : function(){
37213         this.el.removeAllListeners();
37214         var tempEl = document.createElement("span");
37215         tempEl.appendChild(this.el.dom);
37216         tempEl.innerHTML = "";
37217         this.el.remove();
37218         this.el = null;
37219     },
37220     
37221     /**
37222      * form - if the content panel contains a form - this is a reference to it.
37223      * @type {Roo.form.Form}
37224      */
37225     form : false,
37226     /**
37227      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37228      *    This contains a reference to it.
37229      * @type {Roo.View}
37230      */
37231     view : false,
37232     
37233       /**
37234      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37235      * <pre><code>
37236
37237 layout.addxtype({
37238        xtype : 'Form',
37239        items: [ .... ]
37240    }
37241 );
37242
37243 </code></pre>
37244      * @param {Object} cfg Xtype definition of item to add.
37245      */
37246     
37247     
37248     getChildContainer: function () {
37249         return this.getEl();
37250     }
37251     
37252     
37253     /*
37254         var  ret = new Roo.factory(cfg);
37255         return ret;
37256         
37257         
37258         // add form..
37259         if (cfg.xtype.match(/^Form$/)) {
37260             
37261             var el;
37262             //if (this.footer) {
37263             //    el = this.footer.container.insertSibling(false, 'before');
37264             //} else {
37265                 el = this.el.createChild();
37266             //}
37267
37268             this.form = new  Roo.form.Form(cfg);
37269             
37270             
37271             if ( this.form.allItems.length) {
37272                 this.form.render(el.dom);
37273             }
37274             return this.form;
37275         }
37276         // should only have one of theses..
37277         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37278             // views.. should not be just added - used named prop 'view''
37279             
37280             cfg.el = this.el.appendChild(document.createElement("div"));
37281             // factory?
37282             
37283             var ret = new Roo.factory(cfg);
37284              
37285              ret.render && ret.render(false, ''); // render blank..
37286             this.view = ret;
37287             return ret;
37288         }
37289         return false;
37290     }
37291     \*/
37292 });
37293  
37294 /**
37295  * @class Roo.bootstrap.panel.Grid
37296  * @extends Roo.bootstrap.panel.Content
37297  * @constructor
37298  * Create a new GridPanel.
37299  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37300  * @param {Object} config A the config object
37301   
37302  */
37303
37304
37305
37306 Roo.bootstrap.panel.Grid = function(config)
37307 {
37308     
37309       
37310     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37311         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37312
37313     config.el = this.wrapper;
37314     //this.el = this.wrapper;
37315     
37316       if (config.container) {
37317         // ctor'ed from a Border/panel.grid
37318         
37319         
37320         this.wrapper.setStyle("overflow", "hidden");
37321         this.wrapper.addClass('roo-grid-container');
37322
37323     }
37324     
37325     
37326     if(config.toolbar){
37327         var tool_el = this.wrapper.createChild();    
37328         this.toolbar = Roo.factory(config.toolbar);
37329         var ti = [];
37330         if (config.toolbar.items) {
37331             ti = config.toolbar.items ;
37332             delete config.toolbar.items ;
37333         }
37334         
37335         var nitems = [];
37336         this.toolbar.render(tool_el);
37337         for(var i =0;i < ti.length;i++) {
37338           //  Roo.log(['add child', items[i]]);
37339             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37340         }
37341         this.toolbar.items = nitems;
37342         
37343         delete config.toolbar;
37344     }
37345     
37346     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37347     config.grid.scrollBody = true;;
37348     config.grid.monitorWindowResize = false; // turn off autosizing
37349     config.grid.autoHeight = false;
37350     config.grid.autoWidth = false;
37351     
37352     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37353     
37354     if (config.background) {
37355         // render grid on panel activation (if panel background)
37356         this.on('activate', function(gp) {
37357             if (!gp.grid.rendered) {
37358                 gp.grid.render(this.wrapper);
37359                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37360             }
37361         });
37362             
37363     } else {
37364         this.grid.render(this.wrapper);
37365         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37366
37367     }
37368     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37369     // ??? needed ??? config.el = this.wrapper;
37370     
37371     
37372     
37373   
37374     // xtype created footer. - not sure if will work as we normally have to render first..
37375     if (this.footer && !this.footer.el && this.footer.xtype) {
37376         
37377         var ctr = this.grid.getView().getFooterPanel(true);
37378         this.footer.dataSource = this.grid.dataSource;
37379         this.footer = Roo.factory(this.footer, Roo);
37380         this.footer.render(ctr);
37381         
37382     }
37383     
37384     
37385     
37386     
37387      
37388 };
37389
37390 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37391     getId : function(){
37392         return this.grid.id;
37393     },
37394     
37395     /**
37396      * Returns the grid for this panel
37397      * @return {Roo.bootstrap.Table} 
37398      */
37399     getGrid : function(){
37400         return this.grid;    
37401     },
37402     
37403     setSize : function(width, height){
37404         if(!this.ignoreResize(width, height)){
37405             var grid = this.grid;
37406             var size = this.adjustForComponents(width, height);
37407             var gridel = grid.getGridEl();
37408             gridel.setSize(size.width, size.height);
37409             /*
37410             var thd = grid.getGridEl().select('thead',true).first();
37411             var tbd = grid.getGridEl().select('tbody', true).first();
37412             if (tbd) {
37413                 tbd.setSize(width, height - thd.getHeight());
37414             }
37415             */
37416             grid.autoSize();
37417         }
37418     },
37419      
37420     
37421     
37422     beforeSlide : function(){
37423         this.grid.getView().scroller.clip();
37424     },
37425     
37426     afterSlide : function(){
37427         this.grid.getView().scroller.unclip();
37428     },
37429     
37430     destroy : function(){
37431         this.grid.destroy();
37432         delete this.grid;
37433         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37434     }
37435 });
37436
37437 /**
37438  * @class Roo.bootstrap.panel.Nest
37439  * @extends Roo.bootstrap.panel.Content
37440  * @constructor
37441  * Create a new Panel, that can contain a layout.Border.
37442  * 
37443  * 
37444  * @param {Roo.BorderLayout} layout The layout for this panel
37445  * @param {String/Object} config A string to set only the title or a config object
37446  */
37447 Roo.bootstrap.panel.Nest = function(config)
37448 {
37449     // construct with only one argument..
37450     /* FIXME - implement nicer consturctors
37451     if (layout.layout) {
37452         config = layout;
37453         layout = config.layout;
37454         delete config.layout;
37455     }
37456     if (layout.xtype && !layout.getEl) {
37457         // then layout needs constructing..
37458         layout = Roo.factory(layout, Roo);
37459     }
37460     */
37461     
37462     config.el =  config.layout.getEl();
37463     
37464     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37465     
37466     config.layout.monitorWindowResize = false; // turn off autosizing
37467     this.layout = config.layout;
37468     this.layout.getEl().addClass("roo-layout-nested-layout");
37469     
37470     
37471     
37472     
37473 };
37474
37475 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37476
37477     setSize : function(width, height){
37478         if(!this.ignoreResize(width, height)){
37479             var size = this.adjustForComponents(width, height);
37480             var el = this.layout.getEl();
37481             if (size.height < 1) {
37482                 el.setWidth(size.width);   
37483             } else {
37484                 el.setSize(size.width, size.height);
37485             }
37486             var touch = el.dom.offsetWidth;
37487             this.layout.layout();
37488             // ie requires a double layout on the first pass
37489             if(Roo.isIE && !this.initialized){
37490                 this.initialized = true;
37491                 this.layout.layout();
37492             }
37493         }
37494     },
37495     
37496     // activate all subpanels if not currently active..
37497     
37498     setActiveState : function(active){
37499         this.active = active;
37500         this.setActiveClass(active);
37501         
37502         if(!active){
37503             this.fireEvent("deactivate", this);
37504             return;
37505         }
37506         
37507         this.fireEvent("activate", this);
37508         // not sure if this should happen before or after..
37509         if (!this.layout) {
37510             return; // should not happen..
37511         }
37512         var reg = false;
37513         for (var r in this.layout.regions) {
37514             reg = this.layout.getRegion(r);
37515             if (reg.getActivePanel()) {
37516                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37517                 reg.setActivePanel(reg.getActivePanel());
37518                 continue;
37519             }
37520             if (!reg.panels.length) {
37521                 continue;
37522             }
37523             reg.showPanel(reg.getPanel(0));
37524         }
37525         
37526         
37527         
37528         
37529     },
37530     
37531     /**
37532      * Returns the nested BorderLayout for this panel
37533      * @return {Roo.BorderLayout} 
37534      */
37535     getLayout : function(){
37536         return this.layout;
37537     },
37538     
37539      /**
37540      * Adds a xtype elements to the layout of the nested panel
37541      * <pre><code>
37542
37543 panel.addxtype({
37544        xtype : 'ContentPanel',
37545        region: 'west',
37546        items: [ .... ]
37547    }
37548 );
37549
37550 panel.addxtype({
37551         xtype : 'NestedLayoutPanel',
37552         region: 'west',
37553         layout: {
37554            center: { },
37555            west: { }   
37556         },
37557         items : [ ... list of content panels or nested layout panels.. ]
37558    }
37559 );
37560 </code></pre>
37561      * @param {Object} cfg Xtype definition of item to add.
37562      */
37563     addxtype : function(cfg) {
37564         return this.layout.addxtype(cfg);
37565     
37566     }
37567 });        /*
37568  * Based on:
37569  * Ext JS Library 1.1.1
37570  * Copyright(c) 2006-2007, Ext JS, LLC.
37571  *
37572  * Originally Released Under LGPL - original licence link has changed is not relivant.
37573  *
37574  * Fork - LGPL
37575  * <script type="text/javascript">
37576  */
37577 /**
37578  * @class Roo.TabPanel
37579  * @extends Roo.util.Observable
37580  * A lightweight tab container.
37581  * <br><br>
37582  * Usage:
37583  * <pre><code>
37584 // basic tabs 1, built from existing content
37585 var tabs = new Roo.TabPanel("tabs1");
37586 tabs.addTab("script", "View Script");
37587 tabs.addTab("markup", "View Markup");
37588 tabs.activate("script");
37589
37590 // more advanced tabs, built from javascript
37591 var jtabs = new Roo.TabPanel("jtabs");
37592 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37593
37594 // set up the UpdateManager
37595 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37596 var updater = tab2.getUpdateManager();
37597 updater.setDefaultUrl("ajax1.htm");
37598 tab2.on('activate', updater.refresh, updater, true);
37599
37600 // Use setUrl for Ajax loading
37601 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37602 tab3.setUrl("ajax2.htm", null, true);
37603
37604 // Disabled tab
37605 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37606 tab4.disable();
37607
37608 jtabs.activate("jtabs-1");
37609  * </code></pre>
37610  * @constructor
37611  * Create a new TabPanel.
37612  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37613  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37614  */
37615 Roo.bootstrap.panel.Tabs = function(config){
37616     /**
37617     * The container element for this TabPanel.
37618     * @type Roo.Element
37619     */
37620     this.el = Roo.get(config.el);
37621     delete config.el;
37622     if(config){
37623         if(typeof config == "boolean"){
37624             this.tabPosition = config ? "bottom" : "top";
37625         }else{
37626             Roo.apply(this, config);
37627         }
37628     }
37629     
37630     if(this.tabPosition == "bottom"){
37631         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37632         this.el.addClass("roo-tabs-bottom");
37633     }
37634     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37635     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37636     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37637     if(Roo.isIE){
37638         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37639     }
37640     if(this.tabPosition != "bottom"){
37641         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37642          * @type Roo.Element
37643          */
37644         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37645         this.el.addClass("roo-tabs-top");
37646     }
37647     this.items = [];
37648
37649     this.bodyEl.setStyle("position", "relative");
37650
37651     this.active = null;
37652     this.activateDelegate = this.activate.createDelegate(this);
37653
37654     this.addEvents({
37655         /**
37656          * @event tabchange
37657          * Fires when the active tab changes
37658          * @param {Roo.TabPanel} this
37659          * @param {Roo.TabPanelItem} activePanel The new active tab
37660          */
37661         "tabchange": true,
37662         /**
37663          * @event beforetabchange
37664          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37665          * @param {Roo.TabPanel} this
37666          * @param {Object} e Set cancel to true on this object to cancel the tab change
37667          * @param {Roo.TabPanelItem} tab The tab being changed to
37668          */
37669         "beforetabchange" : true
37670     });
37671
37672     Roo.EventManager.onWindowResize(this.onResize, this);
37673     this.cpad = this.el.getPadding("lr");
37674     this.hiddenCount = 0;
37675
37676
37677     // toolbar on the tabbar support...
37678     if (this.toolbar) {
37679         alert("no toolbar support yet");
37680         this.toolbar  = false;
37681         /*
37682         var tcfg = this.toolbar;
37683         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37684         this.toolbar = new Roo.Toolbar(tcfg);
37685         if (Roo.isSafari) {
37686             var tbl = tcfg.container.child('table', true);
37687             tbl.setAttribute('width', '100%');
37688         }
37689         */
37690         
37691     }
37692    
37693
37694
37695     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37696 };
37697
37698 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37699     /*
37700      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37701      */
37702     tabPosition : "top",
37703     /*
37704      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37705      */
37706     currentTabWidth : 0,
37707     /*
37708      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37709      */
37710     minTabWidth : 40,
37711     /*
37712      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37713      */
37714     maxTabWidth : 250,
37715     /*
37716      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37717      */
37718     preferredTabWidth : 175,
37719     /*
37720      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37721      */
37722     resizeTabs : false,
37723     /*
37724      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37725      */
37726     monitorResize : true,
37727     /*
37728      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37729      */
37730     toolbar : false,
37731
37732     /**
37733      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37734      * @param {String} id The id of the div to use <b>or create</b>
37735      * @param {String} text The text for the tab
37736      * @param {String} content (optional) Content to put in the TabPanelItem body
37737      * @param {Boolean} closable (optional) True to create a close icon on the tab
37738      * @return {Roo.TabPanelItem} The created TabPanelItem
37739      */
37740     addTab : function(id, text, content, closable, tpl)
37741     {
37742         var item = new Roo.bootstrap.panel.TabItem({
37743             panel: this,
37744             id : id,
37745             text : text,
37746             closable : closable,
37747             tpl : tpl
37748         });
37749         this.addTabItem(item);
37750         if(content){
37751             item.setContent(content);
37752         }
37753         return item;
37754     },
37755
37756     /**
37757      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37758      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37759      * @return {Roo.TabPanelItem}
37760      */
37761     getTab : function(id){
37762         return this.items[id];
37763     },
37764
37765     /**
37766      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37767      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37768      */
37769     hideTab : function(id){
37770         var t = this.items[id];
37771         if(!t.isHidden()){
37772            t.setHidden(true);
37773            this.hiddenCount++;
37774            this.autoSizeTabs();
37775         }
37776     },
37777
37778     /**
37779      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37780      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37781      */
37782     unhideTab : function(id){
37783         var t = this.items[id];
37784         if(t.isHidden()){
37785            t.setHidden(false);
37786            this.hiddenCount--;
37787            this.autoSizeTabs();
37788         }
37789     },
37790
37791     /**
37792      * Adds an existing {@link Roo.TabPanelItem}.
37793      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37794      */
37795     addTabItem : function(item){
37796         this.items[item.id] = item;
37797         this.items.push(item);
37798       //  if(this.resizeTabs){
37799     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37800   //         this.autoSizeTabs();
37801 //        }else{
37802 //            item.autoSize();
37803        // }
37804     },
37805
37806     /**
37807      * Removes a {@link Roo.TabPanelItem}.
37808      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37809      */
37810     removeTab : function(id){
37811         var items = this.items;
37812         var tab = items[id];
37813         if(!tab) { return; }
37814         var index = items.indexOf(tab);
37815         if(this.active == tab && items.length > 1){
37816             var newTab = this.getNextAvailable(index);
37817             if(newTab) {
37818                 newTab.activate();
37819             }
37820         }
37821         this.stripEl.dom.removeChild(tab.pnode.dom);
37822         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37823             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37824         }
37825         items.splice(index, 1);
37826         delete this.items[tab.id];
37827         tab.fireEvent("close", tab);
37828         tab.purgeListeners();
37829         this.autoSizeTabs();
37830     },
37831
37832     getNextAvailable : function(start){
37833         var items = this.items;
37834         var index = start;
37835         // look for a next tab that will slide over to
37836         // replace the one being removed
37837         while(index < items.length){
37838             var item = items[++index];
37839             if(item && !item.isHidden()){
37840                 return item;
37841             }
37842         }
37843         // if one isn't found select the previous tab (on the left)
37844         index = start;
37845         while(index >= 0){
37846             var item = items[--index];
37847             if(item && !item.isHidden()){
37848                 return item;
37849             }
37850         }
37851         return null;
37852     },
37853
37854     /**
37855      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37856      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37857      */
37858     disableTab : function(id){
37859         var tab = this.items[id];
37860         if(tab && this.active != tab){
37861             tab.disable();
37862         }
37863     },
37864
37865     /**
37866      * Enables a {@link Roo.TabPanelItem} that is disabled.
37867      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37868      */
37869     enableTab : function(id){
37870         var tab = this.items[id];
37871         tab.enable();
37872     },
37873
37874     /**
37875      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37876      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37877      * @return {Roo.TabPanelItem} The TabPanelItem.
37878      */
37879     activate : function(id){
37880         var tab = this.items[id];
37881         if(!tab){
37882             return null;
37883         }
37884         if(tab == this.active || tab.disabled){
37885             return tab;
37886         }
37887         var e = {};
37888         this.fireEvent("beforetabchange", this, e, tab);
37889         if(e.cancel !== true && !tab.disabled){
37890             if(this.active){
37891                 this.active.hide();
37892             }
37893             this.active = this.items[id];
37894             this.active.show();
37895             this.fireEvent("tabchange", this, this.active);
37896         }
37897         return tab;
37898     },
37899
37900     /**
37901      * Gets the active {@link Roo.TabPanelItem}.
37902      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37903      */
37904     getActiveTab : function(){
37905         return this.active;
37906     },
37907
37908     /**
37909      * Updates the tab body element to fit the height of the container element
37910      * for overflow scrolling
37911      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37912      */
37913     syncHeight : function(targetHeight){
37914         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37915         var bm = this.bodyEl.getMargins();
37916         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37917         this.bodyEl.setHeight(newHeight);
37918         return newHeight;
37919     },
37920
37921     onResize : function(){
37922         if(this.monitorResize){
37923             this.autoSizeTabs();
37924         }
37925     },
37926
37927     /**
37928      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37929      */
37930     beginUpdate : function(){
37931         this.updating = true;
37932     },
37933
37934     /**
37935      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37936      */
37937     endUpdate : function(){
37938         this.updating = false;
37939         this.autoSizeTabs();
37940     },
37941
37942     /**
37943      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37944      */
37945     autoSizeTabs : function(){
37946         var count = this.items.length;
37947         var vcount = count - this.hiddenCount;
37948         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37949             return;
37950         }
37951         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37952         var availWidth = Math.floor(w / vcount);
37953         var b = this.stripBody;
37954         if(b.getWidth() > w){
37955             var tabs = this.items;
37956             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37957             if(availWidth < this.minTabWidth){
37958                 /*if(!this.sleft){    // incomplete scrolling code
37959                     this.createScrollButtons();
37960                 }
37961                 this.showScroll();
37962                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37963             }
37964         }else{
37965             if(this.currentTabWidth < this.preferredTabWidth){
37966                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37967             }
37968         }
37969     },
37970
37971     /**
37972      * Returns the number of tabs in this TabPanel.
37973      * @return {Number}
37974      */
37975      getCount : function(){
37976          return this.items.length;
37977      },
37978
37979     /**
37980      * Resizes all the tabs to the passed width
37981      * @param {Number} The new width
37982      */
37983     setTabWidth : function(width){
37984         this.currentTabWidth = width;
37985         for(var i = 0, len = this.items.length; i < len; i++) {
37986                 if(!this.items[i].isHidden()) {
37987                 this.items[i].setWidth(width);
37988             }
37989         }
37990     },
37991
37992     /**
37993      * Destroys this TabPanel
37994      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37995      */
37996     destroy : function(removeEl){
37997         Roo.EventManager.removeResizeListener(this.onResize, this);
37998         for(var i = 0, len = this.items.length; i < len; i++){
37999             this.items[i].purgeListeners();
38000         }
38001         if(removeEl === true){
38002             this.el.update("");
38003             this.el.remove();
38004         }
38005     },
38006     
38007     createStrip : function(container)
38008     {
38009         var strip = document.createElement("nav");
38010         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38011         container.appendChild(strip);
38012         return strip;
38013     },
38014     
38015     createStripList : function(strip)
38016     {
38017         // div wrapper for retard IE
38018         // returns the "tr" element.
38019         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38020         //'<div class="x-tabs-strip-wrap">'+
38021           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38022           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38023         return strip.firstChild; //.firstChild.firstChild.firstChild;
38024     },
38025     createBody : function(container)
38026     {
38027         var body = document.createElement("div");
38028         Roo.id(body, "tab-body");
38029         //Roo.fly(body).addClass("x-tabs-body");
38030         Roo.fly(body).addClass("tab-content");
38031         container.appendChild(body);
38032         return body;
38033     },
38034     createItemBody :function(bodyEl, id){
38035         var body = Roo.getDom(id);
38036         if(!body){
38037             body = document.createElement("div");
38038             body.id = id;
38039         }
38040         //Roo.fly(body).addClass("x-tabs-item-body");
38041         Roo.fly(body).addClass("tab-pane");
38042          bodyEl.insertBefore(body, bodyEl.firstChild);
38043         return body;
38044     },
38045     /** @private */
38046     createStripElements :  function(stripEl, text, closable, tpl)
38047     {
38048         var td = document.createElement("li"); // was td..
38049         
38050         
38051         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38052         
38053         
38054         stripEl.appendChild(td);
38055         /*if(closable){
38056             td.className = "x-tabs-closable";
38057             if(!this.closeTpl){
38058                 this.closeTpl = new Roo.Template(
38059                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38060                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38061                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38062                 );
38063             }
38064             var el = this.closeTpl.overwrite(td, {"text": text});
38065             var close = el.getElementsByTagName("div")[0];
38066             var inner = el.getElementsByTagName("em")[0];
38067             return {"el": el, "close": close, "inner": inner};
38068         } else {
38069         */
38070         // not sure what this is..
38071 //            if(!this.tabTpl){
38072                 //this.tabTpl = new Roo.Template(
38073                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38074                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38075                 //);
38076 //                this.tabTpl = new Roo.Template(
38077 //                   '<a href="#">' +
38078 //                   '<span unselectable="on"' +
38079 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38080 //                            ' >{text}</span></a>'
38081 //                );
38082 //                
38083 //            }
38084
38085
38086             var template = tpl || this.tabTpl || false;
38087             
38088             if(!template){
38089                 
38090                 template = new Roo.Template(
38091                    '<a href="#">' +
38092                    '<span unselectable="on"' +
38093                             (this.disableTooltips ? '' : ' title="{text}"') +
38094                             ' >{text}</span></a>'
38095                 );
38096             }
38097             
38098             switch (typeof(template)) {
38099                 case 'object' :
38100                     break;
38101                 case 'string' :
38102                     template = new Roo.Template(template);
38103                     break;
38104                 default :
38105                     break;
38106             }
38107             
38108             var el = template.overwrite(td, {"text": text});
38109             
38110             var inner = el.getElementsByTagName("span")[0];
38111             
38112             return {"el": el, "inner": inner};
38113             
38114     }
38115         
38116     
38117 });
38118
38119 /**
38120  * @class Roo.TabPanelItem
38121  * @extends Roo.util.Observable
38122  * Represents an individual item (tab plus body) in a TabPanel.
38123  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38124  * @param {String} id The id of this TabPanelItem
38125  * @param {String} text The text for the tab of this TabPanelItem
38126  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38127  */
38128 Roo.bootstrap.panel.TabItem = function(config){
38129     /**
38130      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38131      * @type Roo.TabPanel
38132      */
38133     this.tabPanel = config.panel;
38134     /**
38135      * The id for this TabPanelItem
38136      * @type String
38137      */
38138     this.id = config.id;
38139     /** @private */
38140     this.disabled = false;
38141     /** @private */
38142     this.text = config.text;
38143     /** @private */
38144     this.loaded = false;
38145     this.closable = config.closable;
38146
38147     /**
38148      * The body element for this TabPanelItem.
38149      * @type Roo.Element
38150      */
38151     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38152     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38153     this.bodyEl.setStyle("display", "block");
38154     this.bodyEl.setStyle("zoom", "1");
38155     //this.hideAction();
38156
38157     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38158     /** @private */
38159     this.el = Roo.get(els.el);
38160     this.inner = Roo.get(els.inner, true);
38161     this.textEl = Roo.get(this.el.dom.firstChild, true);
38162     this.pnode = Roo.get(els.el.parentNode, true);
38163 //    this.el.on("mousedown", this.onTabMouseDown, this);
38164     this.el.on("click", this.onTabClick, this);
38165     /** @private */
38166     if(config.closable){
38167         var c = Roo.get(els.close, true);
38168         c.dom.title = this.closeText;
38169         c.addClassOnOver("close-over");
38170         c.on("click", this.closeClick, this);
38171      }
38172
38173     this.addEvents({
38174          /**
38175          * @event activate
38176          * Fires when this tab becomes the active tab.
38177          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38178          * @param {Roo.TabPanelItem} this
38179          */
38180         "activate": true,
38181         /**
38182          * @event beforeclose
38183          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38184          * @param {Roo.TabPanelItem} this
38185          * @param {Object} e Set cancel to true on this object to cancel the close.
38186          */
38187         "beforeclose": true,
38188         /**
38189          * @event close
38190          * Fires when this tab is closed.
38191          * @param {Roo.TabPanelItem} this
38192          */
38193          "close": true,
38194         /**
38195          * @event deactivate
38196          * Fires when this tab is no longer the active tab.
38197          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38198          * @param {Roo.TabPanelItem} this
38199          */
38200          "deactivate" : true
38201     });
38202     this.hidden = false;
38203
38204     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38205 };
38206
38207 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38208            {
38209     purgeListeners : function(){
38210        Roo.util.Observable.prototype.purgeListeners.call(this);
38211        this.el.removeAllListeners();
38212     },
38213     /**
38214      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38215      */
38216     show : function(){
38217         this.pnode.addClass("active");
38218         this.showAction();
38219         if(Roo.isOpera){
38220             this.tabPanel.stripWrap.repaint();
38221         }
38222         this.fireEvent("activate", this.tabPanel, this);
38223     },
38224
38225     /**
38226      * Returns true if this tab is the active tab.
38227      * @return {Boolean}
38228      */
38229     isActive : function(){
38230         return this.tabPanel.getActiveTab() == this;
38231     },
38232
38233     /**
38234      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38235      */
38236     hide : function(){
38237         this.pnode.removeClass("active");
38238         this.hideAction();
38239         this.fireEvent("deactivate", this.tabPanel, this);
38240     },
38241
38242     hideAction : function(){
38243         this.bodyEl.hide();
38244         this.bodyEl.setStyle("position", "absolute");
38245         this.bodyEl.setLeft("-20000px");
38246         this.bodyEl.setTop("-20000px");
38247     },
38248
38249     showAction : function(){
38250         this.bodyEl.setStyle("position", "relative");
38251         this.bodyEl.setTop("");
38252         this.bodyEl.setLeft("");
38253         this.bodyEl.show();
38254     },
38255
38256     /**
38257      * Set the tooltip for the tab.
38258      * @param {String} tooltip The tab's tooltip
38259      */
38260     setTooltip : function(text){
38261         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38262             this.textEl.dom.qtip = text;
38263             this.textEl.dom.removeAttribute('title');
38264         }else{
38265             this.textEl.dom.title = text;
38266         }
38267     },
38268
38269     onTabClick : function(e){
38270         e.preventDefault();
38271         this.tabPanel.activate(this.id);
38272     },
38273
38274     onTabMouseDown : function(e){
38275         e.preventDefault();
38276         this.tabPanel.activate(this.id);
38277     },
38278 /*
38279     getWidth : function(){
38280         return this.inner.getWidth();
38281     },
38282
38283     setWidth : function(width){
38284         var iwidth = width - this.pnode.getPadding("lr");
38285         this.inner.setWidth(iwidth);
38286         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38287         this.pnode.setWidth(width);
38288     },
38289 */
38290     /**
38291      * Show or hide the tab
38292      * @param {Boolean} hidden True to hide or false to show.
38293      */
38294     setHidden : function(hidden){
38295         this.hidden = hidden;
38296         this.pnode.setStyle("display", hidden ? "none" : "");
38297     },
38298
38299     /**
38300      * Returns true if this tab is "hidden"
38301      * @return {Boolean}
38302      */
38303     isHidden : function(){
38304         return this.hidden;
38305     },
38306
38307     /**
38308      * Returns the text for this tab
38309      * @return {String}
38310      */
38311     getText : function(){
38312         return this.text;
38313     },
38314     /*
38315     autoSize : function(){
38316         //this.el.beginMeasure();
38317         this.textEl.setWidth(1);
38318         /*
38319          *  #2804 [new] Tabs in Roojs
38320          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38321          */
38322         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38323         //this.el.endMeasure();
38324     //},
38325
38326     /**
38327      * Sets the text for the tab (Note: this also sets the tooltip text)
38328      * @param {String} text The tab's text and tooltip
38329      */
38330     setText : function(text){
38331         this.text = text;
38332         this.textEl.update(text);
38333         this.setTooltip(text);
38334         //if(!this.tabPanel.resizeTabs){
38335         //    this.autoSize();
38336         //}
38337     },
38338     /**
38339      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38340      */
38341     activate : function(){
38342         this.tabPanel.activate(this.id);
38343     },
38344
38345     /**
38346      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38347      */
38348     disable : function(){
38349         if(this.tabPanel.active != this){
38350             this.disabled = true;
38351             this.pnode.addClass("disabled");
38352         }
38353     },
38354
38355     /**
38356      * Enables this TabPanelItem if it was previously disabled.
38357      */
38358     enable : function(){
38359         this.disabled = false;
38360         this.pnode.removeClass("disabled");
38361     },
38362
38363     /**
38364      * Sets the content for this TabPanelItem.
38365      * @param {String} content The content
38366      * @param {Boolean} loadScripts true to look for and load scripts
38367      */
38368     setContent : function(content, loadScripts){
38369         this.bodyEl.update(content, loadScripts);
38370     },
38371
38372     /**
38373      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38374      * @return {Roo.UpdateManager} The UpdateManager
38375      */
38376     getUpdateManager : function(){
38377         return this.bodyEl.getUpdateManager();
38378     },
38379
38380     /**
38381      * Set a URL to be used to load the content for this TabPanelItem.
38382      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38383      * @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)
38384      * @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)
38385      * @return {Roo.UpdateManager} The UpdateManager
38386      */
38387     setUrl : function(url, params, loadOnce){
38388         if(this.refreshDelegate){
38389             this.un('activate', this.refreshDelegate);
38390         }
38391         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38392         this.on("activate", this.refreshDelegate);
38393         return this.bodyEl.getUpdateManager();
38394     },
38395
38396     /** @private */
38397     _handleRefresh : function(url, params, loadOnce){
38398         if(!loadOnce || !this.loaded){
38399             var updater = this.bodyEl.getUpdateManager();
38400             updater.update(url, params, this._setLoaded.createDelegate(this));
38401         }
38402     },
38403
38404     /**
38405      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38406      *   Will fail silently if the setUrl method has not been called.
38407      *   This does not activate the panel, just updates its content.
38408      */
38409     refresh : function(){
38410         if(this.refreshDelegate){
38411            this.loaded = false;
38412            this.refreshDelegate();
38413         }
38414     },
38415
38416     /** @private */
38417     _setLoaded : function(){
38418         this.loaded = true;
38419     },
38420
38421     /** @private */
38422     closeClick : function(e){
38423         var o = {};
38424         e.stopEvent();
38425         this.fireEvent("beforeclose", this, o);
38426         if(o.cancel !== true){
38427             this.tabPanel.removeTab(this.id);
38428         }
38429     },
38430     /**
38431      * The text displayed in the tooltip for the close icon.
38432      * @type String
38433      */
38434     closeText : "Close this tab"
38435 });
38436 /**
38437 *    This script refer to:
38438 *    Title: International Telephone Input
38439 *    Author: Jack O'Connor
38440 *    Code version:  v12.1.12
38441 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38442 **/
38443
38444 Roo.bootstrap.PhoneInputData = function() {
38445     var d = [
38446       [
38447         "Afghanistan (‫افغانستان‬‎)",
38448         "af",
38449         "93"
38450       ],
38451       [
38452         "Albania (Shqipëri)",
38453         "al",
38454         "355"
38455       ],
38456       [
38457         "Algeria (‫الجزائر‬‎)",
38458         "dz",
38459         "213"
38460       ],
38461       [
38462         "American Samoa",
38463         "as",
38464         "1684"
38465       ],
38466       [
38467         "Andorra",
38468         "ad",
38469         "376"
38470       ],
38471       [
38472         "Angola",
38473         "ao",
38474         "244"
38475       ],
38476       [
38477         "Anguilla",
38478         "ai",
38479         "1264"
38480       ],
38481       [
38482         "Antigua and Barbuda",
38483         "ag",
38484         "1268"
38485       ],
38486       [
38487         "Argentina",
38488         "ar",
38489         "54"
38490       ],
38491       [
38492         "Armenia (Հայաստան)",
38493         "am",
38494         "374"
38495       ],
38496       [
38497         "Aruba",
38498         "aw",
38499         "297"
38500       ],
38501       [
38502         "Australia",
38503         "au",
38504         "61",
38505         0
38506       ],
38507       [
38508         "Austria (Österreich)",
38509         "at",
38510         "43"
38511       ],
38512       [
38513         "Azerbaijan (Azərbaycan)",
38514         "az",
38515         "994"
38516       ],
38517       [
38518         "Bahamas",
38519         "bs",
38520         "1242"
38521       ],
38522       [
38523         "Bahrain (‫البحرين‬‎)",
38524         "bh",
38525         "973"
38526       ],
38527       [
38528         "Bangladesh (বাংলাদেশ)",
38529         "bd",
38530         "880"
38531       ],
38532       [
38533         "Barbados",
38534         "bb",
38535         "1246"
38536       ],
38537       [
38538         "Belarus (Беларусь)",
38539         "by",
38540         "375"
38541       ],
38542       [
38543         "Belgium (België)",
38544         "be",
38545         "32"
38546       ],
38547       [
38548         "Belize",
38549         "bz",
38550         "501"
38551       ],
38552       [
38553         "Benin (Bénin)",
38554         "bj",
38555         "229"
38556       ],
38557       [
38558         "Bermuda",
38559         "bm",
38560         "1441"
38561       ],
38562       [
38563         "Bhutan (འབྲུག)",
38564         "bt",
38565         "975"
38566       ],
38567       [
38568         "Bolivia",
38569         "bo",
38570         "591"
38571       ],
38572       [
38573         "Bosnia and Herzegovina (Босна и Херцеговина)",
38574         "ba",
38575         "387"
38576       ],
38577       [
38578         "Botswana",
38579         "bw",
38580         "267"
38581       ],
38582       [
38583         "Brazil (Brasil)",
38584         "br",
38585         "55"
38586       ],
38587       [
38588         "British Indian Ocean Territory",
38589         "io",
38590         "246"
38591       ],
38592       [
38593         "British Virgin Islands",
38594         "vg",
38595         "1284"
38596       ],
38597       [
38598         "Brunei",
38599         "bn",
38600         "673"
38601       ],
38602       [
38603         "Bulgaria (България)",
38604         "bg",
38605         "359"
38606       ],
38607       [
38608         "Burkina Faso",
38609         "bf",
38610         "226"
38611       ],
38612       [
38613         "Burundi (Uburundi)",
38614         "bi",
38615         "257"
38616       ],
38617       [
38618         "Cambodia (កម្ពុជា)",
38619         "kh",
38620         "855"
38621       ],
38622       [
38623         "Cameroon (Cameroun)",
38624         "cm",
38625         "237"
38626       ],
38627       [
38628         "Canada",
38629         "ca",
38630         "1",
38631         1,
38632         ["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"]
38633       ],
38634       [
38635         "Cape Verde (Kabu Verdi)",
38636         "cv",
38637         "238"
38638       ],
38639       [
38640         "Caribbean Netherlands",
38641         "bq",
38642         "599",
38643         1
38644       ],
38645       [
38646         "Cayman Islands",
38647         "ky",
38648         "1345"
38649       ],
38650       [
38651         "Central African Republic (République centrafricaine)",
38652         "cf",
38653         "236"
38654       ],
38655       [
38656         "Chad (Tchad)",
38657         "td",
38658         "235"
38659       ],
38660       [
38661         "Chile",
38662         "cl",
38663         "56"
38664       ],
38665       [
38666         "China (中国)",
38667         "cn",
38668         "86"
38669       ],
38670       [
38671         "Christmas Island",
38672         "cx",
38673         "61",
38674         2
38675       ],
38676       [
38677         "Cocos (Keeling) Islands",
38678         "cc",
38679         "61",
38680         1
38681       ],
38682       [
38683         "Colombia",
38684         "co",
38685         "57"
38686       ],
38687       [
38688         "Comoros (‫جزر القمر‬‎)",
38689         "km",
38690         "269"
38691       ],
38692       [
38693         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38694         "cd",
38695         "243"
38696       ],
38697       [
38698         "Congo (Republic) (Congo-Brazzaville)",
38699         "cg",
38700         "242"
38701       ],
38702       [
38703         "Cook Islands",
38704         "ck",
38705         "682"
38706       ],
38707       [
38708         "Costa Rica",
38709         "cr",
38710         "506"
38711       ],
38712       [
38713         "Côte d’Ivoire",
38714         "ci",
38715         "225"
38716       ],
38717       [
38718         "Croatia (Hrvatska)",
38719         "hr",
38720         "385"
38721       ],
38722       [
38723         "Cuba",
38724         "cu",
38725         "53"
38726       ],
38727       [
38728         "Curaçao",
38729         "cw",
38730         "599",
38731         0
38732       ],
38733       [
38734         "Cyprus (Κύπρος)",
38735         "cy",
38736         "357"
38737       ],
38738       [
38739         "Czech Republic (Česká republika)",
38740         "cz",
38741         "420"
38742       ],
38743       [
38744         "Denmark (Danmark)",
38745         "dk",
38746         "45"
38747       ],
38748       [
38749         "Djibouti",
38750         "dj",
38751         "253"
38752       ],
38753       [
38754         "Dominica",
38755         "dm",
38756         "1767"
38757       ],
38758       [
38759         "Dominican Republic (República Dominicana)",
38760         "do",
38761         "1",
38762         2,
38763         ["809", "829", "849"]
38764       ],
38765       [
38766         "Ecuador",
38767         "ec",
38768         "593"
38769       ],
38770       [
38771         "Egypt (‫مصر‬‎)",
38772         "eg",
38773         "20"
38774       ],
38775       [
38776         "El Salvador",
38777         "sv",
38778         "503"
38779       ],
38780       [
38781         "Equatorial Guinea (Guinea Ecuatorial)",
38782         "gq",
38783         "240"
38784       ],
38785       [
38786         "Eritrea",
38787         "er",
38788         "291"
38789       ],
38790       [
38791         "Estonia (Eesti)",
38792         "ee",
38793         "372"
38794       ],
38795       [
38796         "Ethiopia",
38797         "et",
38798         "251"
38799       ],
38800       [
38801         "Falkland Islands (Islas Malvinas)",
38802         "fk",
38803         "500"
38804       ],
38805       [
38806         "Faroe Islands (Føroyar)",
38807         "fo",
38808         "298"
38809       ],
38810       [
38811         "Fiji",
38812         "fj",
38813         "679"
38814       ],
38815       [
38816         "Finland (Suomi)",
38817         "fi",
38818         "358",
38819         0
38820       ],
38821       [
38822         "France",
38823         "fr",
38824         "33"
38825       ],
38826       [
38827         "French Guiana (Guyane française)",
38828         "gf",
38829         "594"
38830       ],
38831       [
38832         "French Polynesia (Polynésie française)",
38833         "pf",
38834         "689"
38835       ],
38836       [
38837         "Gabon",
38838         "ga",
38839         "241"
38840       ],
38841       [
38842         "Gambia",
38843         "gm",
38844         "220"
38845       ],
38846       [
38847         "Georgia (საქართველო)",
38848         "ge",
38849         "995"
38850       ],
38851       [
38852         "Germany (Deutschland)",
38853         "de",
38854         "49"
38855       ],
38856       [
38857         "Ghana (Gaana)",
38858         "gh",
38859         "233"
38860       ],
38861       [
38862         "Gibraltar",
38863         "gi",
38864         "350"
38865       ],
38866       [
38867         "Greece (Ελλάδα)",
38868         "gr",
38869         "30"
38870       ],
38871       [
38872         "Greenland (Kalaallit Nunaat)",
38873         "gl",
38874         "299"
38875       ],
38876       [
38877         "Grenada",
38878         "gd",
38879         "1473"
38880       ],
38881       [
38882         "Guadeloupe",
38883         "gp",
38884         "590",
38885         0
38886       ],
38887       [
38888         "Guam",
38889         "gu",
38890         "1671"
38891       ],
38892       [
38893         "Guatemala",
38894         "gt",
38895         "502"
38896       ],
38897       [
38898         "Guernsey",
38899         "gg",
38900         "44",
38901         1
38902       ],
38903       [
38904         "Guinea (Guinée)",
38905         "gn",
38906         "224"
38907       ],
38908       [
38909         "Guinea-Bissau (Guiné Bissau)",
38910         "gw",
38911         "245"
38912       ],
38913       [
38914         "Guyana",
38915         "gy",
38916         "592"
38917       ],
38918       [
38919         "Haiti",
38920         "ht",
38921         "509"
38922       ],
38923       [
38924         "Honduras",
38925         "hn",
38926         "504"
38927       ],
38928       [
38929         "Hong Kong (香港)",
38930         "hk",
38931         "852"
38932       ],
38933       [
38934         "Hungary (Magyarország)",
38935         "hu",
38936         "36"
38937       ],
38938       [
38939         "Iceland (Ísland)",
38940         "is",
38941         "354"
38942       ],
38943       [
38944         "India (भारत)",
38945         "in",
38946         "91"
38947       ],
38948       [
38949         "Indonesia",
38950         "id",
38951         "62"
38952       ],
38953       [
38954         "Iran (‫ایران‬‎)",
38955         "ir",
38956         "98"
38957       ],
38958       [
38959         "Iraq (‫العراق‬‎)",
38960         "iq",
38961         "964"
38962       ],
38963       [
38964         "Ireland",
38965         "ie",
38966         "353"
38967       ],
38968       [
38969         "Isle of Man",
38970         "im",
38971         "44",
38972         2
38973       ],
38974       [
38975         "Israel (‫ישראל‬‎)",
38976         "il",
38977         "972"
38978       ],
38979       [
38980         "Italy (Italia)",
38981         "it",
38982         "39",
38983         0
38984       ],
38985       [
38986         "Jamaica",
38987         "jm",
38988         "1876"
38989       ],
38990       [
38991         "Japan (日本)",
38992         "jp",
38993         "81"
38994       ],
38995       [
38996         "Jersey",
38997         "je",
38998         "44",
38999         3
39000       ],
39001       [
39002         "Jordan (‫الأردن‬‎)",
39003         "jo",
39004         "962"
39005       ],
39006       [
39007         "Kazakhstan (Казахстан)",
39008         "kz",
39009         "7",
39010         1
39011       ],
39012       [
39013         "Kenya",
39014         "ke",
39015         "254"
39016       ],
39017       [
39018         "Kiribati",
39019         "ki",
39020         "686"
39021       ],
39022       [
39023         "Kosovo",
39024         "xk",
39025         "383"
39026       ],
39027       [
39028         "Kuwait (‫الكويت‬‎)",
39029         "kw",
39030         "965"
39031       ],
39032       [
39033         "Kyrgyzstan (Кыргызстан)",
39034         "kg",
39035         "996"
39036       ],
39037       [
39038         "Laos (ລາວ)",
39039         "la",
39040         "856"
39041       ],
39042       [
39043         "Latvia (Latvija)",
39044         "lv",
39045         "371"
39046       ],
39047       [
39048         "Lebanon (‫لبنان‬‎)",
39049         "lb",
39050         "961"
39051       ],
39052       [
39053         "Lesotho",
39054         "ls",
39055         "266"
39056       ],
39057       [
39058         "Liberia",
39059         "lr",
39060         "231"
39061       ],
39062       [
39063         "Libya (‫ليبيا‬‎)",
39064         "ly",
39065         "218"
39066       ],
39067       [
39068         "Liechtenstein",
39069         "li",
39070         "423"
39071       ],
39072       [
39073         "Lithuania (Lietuva)",
39074         "lt",
39075         "370"
39076       ],
39077       [
39078         "Luxembourg",
39079         "lu",
39080         "352"
39081       ],
39082       [
39083         "Macau (澳門)",
39084         "mo",
39085         "853"
39086       ],
39087       [
39088         "Macedonia (FYROM) (Македонија)",
39089         "mk",
39090         "389"
39091       ],
39092       [
39093         "Madagascar (Madagasikara)",
39094         "mg",
39095         "261"
39096       ],
39097       [
39098         "Malawi",
39099         "mw",
39100         "265"
39101       ],
39102       [
39103         "Malaysia",
39104         "my",
39105         "60"
39106       ],
39107       [
39108         "Maldives",
39109         "mv",
39110         "960"
39111       ],
39112       [
39113         "Mali",
39114         "ml",
39115         "223"
39116       ],
39117       [
39118         "Malta",
39119         "mt",
39120         "356"
39121       ],
39122       [
39123         "Marshall Islands",
39124         "mh",
39125         "692"
39126       ],
39127       [
39128         "Martinique",
39129         "mq",
39130         "596"
39131       ],
39132       [
39133         "Mauritania (‫موريتانيا‬‎)",
39134         "mr",
39135         "222"
39136       ],
39137       [
39138         "Mauritius (Moris)",
39139         "mu",
39140         "230"
39141       ],
39142       [
39143         "Mayotte",
39144         "yt",
39145         "262",
39146         1
39147       ],
39148       [
39149         "Mexico (México)",
39150         "mx",
39151         "52"
39152       ],
39153       [
39154         "Micronesia",
39155         "fm",
39156         "691"
39157       ],
39158       [
39159         "Moldova (Republica Moldova)",
39160         "md",
39161         "373"
39162       ],
39163       [
39164         "Monaco",
39165         "mc",
39166         "377"
39167       ],
39168       [
39169         "Mongolia (Монгол)",
39170         "mn",
39171         "976"
39172       ],
39173       [
39174         "Montenegro (Crna Gora)",
39175         "me",
39176         "382"
39177       ],
39178       [
39179         "Montserrat",
39180         "ms",
39181         "1664"
39182       ],
39183       [
39184         "Morocco (‫المغرب‬‎)",
39185         "ma",
39186         "212",
39187         0
39188       ],
39189       [
39190         "Mozambique (Moçambique)",
39191         "mz",
39192         "258"
39193       ],
39194       [
39195         "Myanmar (Burma) (မြန်မာ)",
39196         "mm",
39197         "95"
39198       ],
39199       [
39200         "Namibia (Namibië)",
39201         "na",
39202         "264"
39203       ],
39204       [
39205         "Nauru",
39206         "nr",
39207         "674"
39208       ],
39209       [
39210         "Nepal (नेपाल)",
39211         "np",
39212         "977"
39213       ],
39214       [
39215         "Netherlands (Nederland)",
39216         "nl",
39217         "31"
39218       ],
39219       [
39220         "New Caledonia (Nouvelle-Calédonie)",
39221         "nc",
39222         "687"
39223       ],
39224       [
39225         "New Zealand",
39226         "nz",
39227         "64"
39228       ],
39229       [
39230         "Nicaragua",
39231         "ni",
39232         "505"
39233       ],
39234       [
39235         "Niger (Nijar)",
39236         "ne",
39237         "227"
39238       ],
39239       [
39240         "Nigeria",
39241         "ng",
39242         "234"
39243       ],
39244       [
39245         "Niue",
39246         "nu",
39247         "683"
39248       ],
39249       [
39250         "Norfolk Island",
39251         "nf",
39252         "672"
39253       ],
39254       [
39255         "North Korea (조선 민주주의 인민 공화국)",
39256         "kp",
39257         "850"
39258       ],
39259       [
39260         "Northern Mariana Islands",
39261         "mp",
39262         "1670"
39263       ],
39264       [
39265         "Norway (Norge)",
39266         "no",
39267         "47",
39268         0
39269       ],
39270       [
39271         "Oman (‫عُمان‬‎)",
39272         "om",
39273         "968"
39274       ],
39275       [
39276         "Pakistan (‫پاکستان‬‎)",
39277         "pk",
39278         "92"
39279       ],
39280       [
39281         "Palau",
39282         "pw",
39283         "680"
39284       ],
39285       [
39286         "Palestine (‫فلسطين‬‎)",
39287         "ps",
39288         "970"
39289       ],
39290       [
39291         "Panama (Panamá)",
39292         "pa",
39293         "507"
39294       ],
39295       [
39296         "Papua New Guinea",
39297         "pg",
39298         "675"
39299       ],
39300       [
39301         "Paraguay",
39302         "py",
39303         "595"
39304       ],
39305       [
39306         "Peru (Perú)",
39307         "pe",
39308         "51"
39309       ],
39310       [
39311         "Philippines",
39312         "ph",
39313         "63"
39314       ],
39315       [
39316         "Poland (Polska)",
39317         "pl",
39318         "48"
39319       ],
39320       [
39321         "Portugal",
39322         "pt",
39323         "351"
39324       ],
39325       [
39326         "Puerto Rico",
39327         "pr",
39328         "1",
39329         3,
39330         ["787", "939"]
39331       ],
39332       [
39333         "Qatar (‫قطر‬‎)",
39334         "qa",
39335         "974"
39336       ],
39337       [
39338         "Réunion (La Réunion)",
39339         "re",
39340         "262",
39341         0
39342       ],
39343       [
39344         "Romania (România)",
39345         "ro",
39346         "40"
39347       ],
39348       [
39349         "Russia (Россия)",
39350         "ru",
39351         "7",
39352         0
39353       ],
39354       [
39355         "Rwanda",
39356         "rw",
39357         "250"
39358       ],
39359       [
39360         "Saint Barthélemy",
39361         "bl",
39362         "590",
39363         1
39364       ],
39365       [
39366         "Saint Helena",
39367         "sh",
39368         "290"
39369       ],
39370       [
39371         "Saint Kitts and Nevis",
39372         "kn",
39373         "1869"
39374       ],
39375       [
39376         "Saint Lucia",
39377         "lc",
39378         "1758"
39379       ],
39380       [
39381         "Saint Martin (Saint-Martin (partie française))",
39382         "mf",
39383         "590",
39384         2
39385       ],
39386       [
39387         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39388         "pm",
39389         "508"
39390       ],
39391       [
39392         "Saint Vincent and the Grenadines",
39393         "vc",
39394         "1784"
39395       ],
39396       [
39397         "Samoa",
39398         "ws",
39399         "685"
39400       ],
39401       [
39402         "San Marino",
39403         "sm",
39404         "378"
39405       ],
39406       [
39407         "São Tomé and Príncipe (São Tomé e Príncipe)",
39408         "st",
39409         "239"
39410       ],
39411       [
39412         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39413         "sa",
39414         "966"
39415       ],
39416       [
39417         "Senegal (Sénégal)",
39418         "sn",
39419         "221"
39420       ],
39421       [
39422         "Serbia (Србија)",
39423         "rs",
39424         "381"
39425       ],
39426       [
39427         "Seychelles",
39428         "sc",
39429         "248"
39430       ],
39431       [
39432         "Sierra Leone",
39433         "sl",
39434         "232"
39435       ],
39436       [
39437         "Singapore",
39438         "sg",
39439         "65"
39440       ],
39441       [
39442         "Sint Maarten",
39443         "sx",
39444         "1721"
39445       ],
39446       [
39447         "Slovakia (Slovensko)",
39448         "sk",
39449         "421"
39450       ],
39451       [
39452         "Slovenia (Slovenija)",
39453         "si",
39454         "386"
39455       ],
39456       [
39457         "Solomon Islands",
39458         "sb",
39459         "677"
39460       ],
39461       [
39462         "Somalia (Soomaaliya)",
39463         "so",
39464         "252"
39465       ],
39466       [
39467         "South Africa",
39468         "za",
39469         "27"
39470       ],
39471       [
39472         "South Korea (대한민국)",
39473         "kr",
39474         "82"
39475       ],
39476       [
39477         "South Sudan (‫جنوب السودان‬‎)",
39478         "ss",
39479         "211"
39480       ],
39481       [
39482         "Spain (España)",
39483         "es",
39484         "34"
39485       ],
39486       [
39487         "Sri Lanka (ශ්‍රී ලංකාව)",
39488         "lk",
39489         "94"
39490       ],
39491       [
39492         "Sudan (‫السودان‬‎)",
39493         "sd",
39494         "249"
39495       ],
39496       [
39497         "Suriname",
39498         "sr",
39499         "597"
39500       ],
39501       [
39502         "Svalbard and Jan Mayen",
39503         "sj",
39504         "47",
39505         1
39506       ],
39507       [
39508         "Swaziland",
39509         "sz",
39510         "268"
39511       ],
39512       [
39513         "Sweden (Sverige)",
39514         "se",
39515         "46"
39516       ],
39517       [
39518         "Switzerland (Schweiz)",
39519         "ch",
39520         "41"
39521       ],
39522       [
39523         "Syria (‫سوريا‬‎)",
39524         "sy",
39525         "963"
39526       ],
39527       [
39528         "Taiwan (台灣)",
39529         "tw",
39530         "886"
39531       ],
39532       [
39533         "Tajikistan",
39534         "tj",
39535         "992"
39536       ],
39537       [
39538         "Tanzania",
39539         "tz",
39540         "255"
39541       ],
39542       [
39543         "Thailand (ไทย)",
39544         "th",
39545         "66"
39546       ],
39547       [
39548         "Timor-Leste",
39549         "tl",
39550         "670"
39551       ],
39552       [
39553         "Togo",
39554         "tg",
39555         "228"
39556       ],
39557       [
39558         "Tokelau",
39559         "tk",
39560         "690"
39561       ],
39562       [
39563         "Tonga",
39564         "to",
39565         "676"
39566       ],
39567       [
39568         "Trinidad and Tobago",
39569         "tt",
39570         "1868"
39571       ],
39572       [
39573         "Tunisia (‫تونس‬‎)",
39574         "tn",
39575         "216"
39576       ],
39577       [
39578         "Turkey (Türkiye)",
39579         "tr",
39580         "90"
39581       ],
39582       [
39583         "Turkmenistan",
39584         "tm",
39585         "993"
39586       ],
39587       [
39588         "Turks and Caicos Islands",
39589         "tc",
39590         "1649"
39591       ],
39592       [
39593         "Tuvalu",
39594         "tv",
39595         "688"
39596       ],
39597       [
39598         "U.S. Virgin Islands",
39599         "vi",
39600         "1340"
39601       ],
39602       [
39603         "Uganda",
39604         "ug",
39605         "256"
39606       ],
39607       [
39608         "Ukraine (Україна)",
39609         "ua",
39610         "380"
39611       ],
39612       [
39613         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39614         "ae",
39615         "971"
39616       ],
39617       [
39618         "United Kingdom",
39619         "gb",
39620         "44",
39621         0
39622       ],
39623       [
39624         "United States",
39625         "us",
39626         "1",
39627         0
39628       ],
39629       [
39630         "Uruguay",
39631         "uy",
39632         "598"
39633       ],
39634       [
39635         "Uzbekistan (Oʻzbekiston)",
39636         "uz",
39637         "998"
39638       ],
39639       [
39640         "Vanuatu",
39641         "vu",
39642         "678"
39643       ],
39644       [
39645         "Vatican City (Città del Vaticano)",
39646         "va",
39647         "39",
39648         1
39649       ],
39650       [
39651         "Venezuela",
39652         "ve",
39653         "58"
39654       ],
39655       [
39656         "Vietnam (Việt Nam)",
39657         "vn",
39658         "84"
39659       ],
39660       [
39661         "Wallis and Futuna (Wallis-et-Futuna)",
39662         "wf",
39663         "681"
39664       ],
39665       [
39666         "Western Sahara (‫الصحراء الغربية‬‎)",
39667         "eh",
39668         "212",
39669         1
39670       ],
39671       [
39672         "Yemen (‫اليمن‬‎)",
39673         "ye",
39674         "967"
39675       ],
39676       [
39677         "Zambia",
39678         "zm",
39679         "260"
39680       ],
39681       [
39682         "Zimbabwe",
39683         "zw",
39684         "263"
39685       ],
39686       [
39687         "Åland Islands",
39688         "ax",
39689         "358",
39690         1
39691       ]
39692   ];
39693   
39694   return d;
39695 }/**
39696 *    This script refer to:
39697 *    Title: International Telephone Input
39698 *    Author: Jack O'Connor
39699 *    Code version:  v12.1.12
39700 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39701 **/
39702
39703 /**
39704  * @class Roo.bootstrap.PhoneInput
39705  * @extends Roo.bootstrap.TriggerField
39706  * An input with International dial-code selection
39707  
39708  * @cfg {String} defaultDialCode default '+852'
39709  * @cfg {Array} preferedCountries default []
39710   
39711  * @constructor
39712  * Create a new PhoneInput.
39713  * @param {Object} config Configuration options
39714  */
39715
39716 Roo.bootstrap.PhoneInput = function(config) {
39717     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39718 };
39719
39720 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39721         
39722         listWidth: undefined,
39723         
39724         selectedClass: 'active',
39725         
39726         invalidClass : "has-warning",
39727         
39728         validClass: 'has-success',
39729         
39730         allowed: '0123456789',
39731         
39732         /**
39733          * @cfg {String} defaultDialCode The default dial code when initializing the input
39734          */
39735         defaultDialCode: '+852',
39736         
39737         /**
39738          * @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
39739          */
39740         preferedCountries: false,
39741         
39742         getAutoCreate : function()
39743         {
39744             var data = Roo.bootstrap.PhoneInputData();
39745             var align = this.labelAlign || this.parentLabelAlign();
39746             var id = Roo.id();
39747             
39748             this.allCountries = [];
39749             this.dialCodeMapping = [];
39750             
39751             for (var i = 0; i < data.length; i++) {
39752               var c = data[i];
39753               this.allCountries[i] = {
39754                 name: c[0],
39755                 iso2: c[1],
39756                 dialCode: c[2],
39757                 priority: c[3] || 0,
39758                 areaCodes: c[4] || null
39759               };
39760               this.dialCodeMapping[c[2]] = {
39761                   name: c[0],
39762                   iso2: c[1],
39763                   priority: c[3] || 0,
39764                   areaCodes: c[4] || null
39765               };
39766             }
39767             
39768             var cfg = {
39769                 cls: 'form-group',
39770                 cn: []
39771             };
39772             
39773             var input =  {
39774                 tag: 'input',
39775                 id : id,
39776                 cls : 'form-control tel-input',
39777                 autocomplete: 'new-password'
39778             };
39779             
39780             var hiddenInput = {
39781                 tag: 'input',
39782                 type: 'hidden',
39783                 cls: 'hidden-tel-input'
39784             };
39785             
39786             if (this.name) {
39787                 hiddenInput.name = this.name;
39788             }
39789             
39790             if (this.disabled) {
39791                 input.disabled = true;
39792             }
39793             
39794             var flag_container = {
39795                 tag: 'div',
39796                 cls: 'flag-box',
39797                 cn: [
39798                     {
39799                         tag: 'div',
39800                         cls: 'flag'
39801                     },
39802                     {
39803                         tag: 'div',
39804                         cls: 'caret'
39805                     }
39806                 ]
39807             };
39808             
39809             var box = {
39810                 tag: 'div',
39811                 cls: this.hasFeedback ? 'has-feedback' : '',
39812                 cn: [
39813                     hiddenInput,
39814                     input,
39815                     {
39816                         tag: 'input',
39817                         cls: 'dial-code-holder',
39818                         disabled: true
39819                     }
39820                 ]
39821             };
39822             
39823             var container = {
39824                 cls: 'roo-select2-container input-group',
39825                 cn: [
39826                     flag_container,
39827                     box
39828                 ]
39829             };
39830             
39831             if (this.fieldLabel.length) {
39832                 var indicator = {
39833                     tag: 'i',
39834                     tooltip: 'This field is required'
39835                 };
39836                 
39837                 var label = {
39838                     tag: 'label',
39839                     'for':  id,
39840                     cls: 'control-label',
39841                     cn: []
39842                 };
39843                 
39844                 var label_text = {
39845                     tag: 'span',
39846                     html: this.fieldLabel
39847                 };
39848                 
39849                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39850                 label.cn = [
39851                     indicator,
39852                     label_text
39853                 ];
39854                 
39855                 if(this.indicatorpos == 'right') {
39856                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39857                     label.cn = [
39858                         label_text,
39859                         indicator
39860                     ];
39861                 }
39862                 
39863                 if(align == 'left') {
39864                     container = {
39865                         tag: 'div',
39866                         cn: [
39867                             container
39868                         ]
39869                     };
39870                     
39871                     if(this.labelWidth > 12){
39872                         label.style = "width: " + this.labelWidth + 'px';
39873                     }
39874                     if(this.labelWidth < 13 && this.labelmd == 0){
39875                         this.labelmd = this.labelWidth;
39876                     }
39877                     if(this.labellg > 0){
39878                         label.cls += ' col-lg-' + this.labellg;
39879                         input.cls += ' col-lg-' + (12 - this.labellg);
39880                     }
39881                     if(this.labelmd > 0){
39882                         label.cls += ' col-md-' + this.labelmd;
39883                         container.cls += ' col-md-' + (12 - this.labelmd);
39884                     }
39885                     if(this.labelsm > 0){
39886                         label.cls += ' col-sm-' + this.labelsm;
39887                         container.cls += ' col-sm-' + (12 - this.labelsm);
39888                     }
39889                     if(this.labelxs > 0){
39890                         label.cls += ' col-xs-' + this.labelxs;
39891                         container.cls += ' col-xs-' + (12 - this.labelxs);
39892                     }
39893                 }
39894             }
39895             
39896             cfg.cn = [
39897                 label,
39898                 container
39899             ];
39900             
39901             var settings = this;
39902             
39903             ['xs','sm','md','lg'].map(function(size){
39904                 if (settings[size]) {
39905                     cfg.cls += ' col-' + size + '-' + settings[size];
39906                 }
39907             });
39908             
39909             this.store = new Roo.data.Store({
39910                 proxy : new Roo.data.MemoryProxy({}),
39911                 reader : new Roo.data.JsonReader({
39912                     fields : [
39913                         {
39914                             'name' : 'name',
39915                             'type' : 'string'
39916                         },
39917                         {
39918                             'name' : 'iso2',
39919                             'type' : 'string'
39920                         },
39921                         {
39922                             'name' : 'dialCode',
39923                             'type' : 'string'
39924                         },
39925                         {
39926                             'name' : 'priority',
39927                             'type' : 'string'
39928                         },
39929                         {
39930                             'name' : 'areaCodes',
39931                             'type' : 'string'
39932                         }
39933                     ]
39934                 })
39935             });
39936             
39937             if(!this.preferedCountries) {
39938                 this.preferedCountries = [
39939                     'hk',
39940                     'gb',
39941                     'us'
39942                 ];
39943             }
39944             
39945             var p = this.preferedCountries.reverse();
39946             
39947             if(p) {
39948                 for (var i = 0; i < p.length; i++) {
39949                     for (var j = 0; j < this.allCountries.length; j++) {
39950                         if(this.allCountries[j].iso2 == p[i]) {
39951                             var t = this.allCountries[j];
39952                             this.allCountries.splice(j,1);
39953                             this.allCountries.unshift(t);
39954                         }
39955                     } 
39956                 }
39957             }
39958             
39959             this.store.proxy.data = {
39960                 success: true,
39961                 data: this.allCountries
39962             };
39963             
39964             return cfg;
39965         },
39966         
39967         initEvents : function()
39968         {
39969             this.createList();
39970             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39971             
39972             this.indicator = this.indicatorEl();
39973             this.flag = this.flagEl();
39974             this.dialCodeHolder = this.dialCodeHolderEl();
39975             
39976             this.trigger = this.el.select('div.flag-box',true).first();
39977             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39978             
39979             var _this = this;
39980             
39981             (function(){
39982                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39983                 _this.list.setWidth(lw);
39984             }).defer(100);
39985             
39986             this.list.on('mouseover', this.onViewOver, this);
39987             this.list.on('mousemove', this.onViewMove, this);
39988             this.inputEl().on("keyup", this.onKeyUp, this);
39989             
39990             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39991
39992             this.view = new Roo.View(this.list, this.tpl, {
39993                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39994             });
39995             
39996             this.view.on('click', this.onViewClick, this);
39997             this.setValue(this.defaultDialCode);
39998         },
39999         
40000         onTriggerClick : function(e)
40001         {
40002             Roo.log('trigger click');
40003             if(this.disabled){
40004                 return;
40005             }
40006             
40007             if(this.isExpanded()){
40008                 this.collapse();
40009                 this.hasFocus = false;
40010             }else {
40011                 this.store.load({});
40012                 this.hasFocus = true;
40013                 this.expand();
40014             }
40015         },
40016         
40017         isExpanded : function()
40018         {
40019             return this.list.isVisible();
40020         },
40021         
40022         collapse : function()
40023         {
40024             if(!this.isExpanded()){
40025                 return;
40026             }
40027             this.list.hide();
40028             Roo.get(document).un('mousedown', this.collapseIf, this);
40029             Roo.get(document).un('mousewheel', this.collapseIf, this);
40030             this.fireEvent('collapse', this);
40031             this.validate();
40032         },
40033         
40034         expand : function()
40035         {
40036             Roo.log('expand');
40037
40038             if(this.isExpanded() || !this.hasFocus){
40039                 return;
40040             }
40041             
40042             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40043             this.list.setWidth(lw);
40044             
40045             this.list.show();
40046             this.restrictHeight();
40047             
40048             Roo.get(document).on('mousedown', this.collapseIf, this);
40049             Roo.get(document).on('mousewheel', this.collapseIf, this);
40050             
40051             this.fireEvent('expand', this);
40052         },
40053         
40054         restrictHeight : function()
40055         {
40056             this.list.alignTo(this.inputEl(), this.listAlign);
40057             this.list.alignTo(this.inputEl(), this.listAlign);
40058         },
40059         
40060         onViewOver : function(e, t)
40061         {
40062             if(this.inKeyMode){
40063                 return;
40064             }
40065             var item = this.view.findItemFromChild(t);
40066             
40067             if(item){
40068                 var index = this.view.indexOf(item);
40069                 this.select(index, false);
40070             }
40071         },
40072
40073         // private
40074         onViewClick : function(view, doFocus, el, e)
40075         {
40076             var index = this.view.getSelectedIndexes()[0];
40077             
40078             var r = this.store.getAt(index);
40079             
40080             if(r){
40081                 this.onSelect(r, index);
40082             }
40083             if(doFocus !== false && !this.blockFocus){
40084                 this.inputEl().focus();
40085             }
40086         },
40087         
40088         onViewMove : function(e, t)
40089         {
40090             this.inKeyMode = false;
40091         },
40092         
40093         select : function(index, scrollIntoView)
40094         {
40095             this.selectedIndex = index;
40096             this.view.select(index);
40097             if(scrollIntoView !== false){
40098                 var el = this.view.getNode(index);
40099                 if(el){
40100                     this.list.scrollChildIntoView(el, false);
40101                 }
40102             }
40103         },
40104         
40105         createList : function()
40106         {
40107             this.list = Roo.get(document.body).createChild({
40108                 tag: 'ul',
40109                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40110                 style: 'display:none'
40111             });
40112             
40113             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40114         },
40115         
40116         collapseIf : function(e)
40117         {
40118             var in_combo  = e.within(this.el);
40119             var in_list =  e.within(this.list);
40120             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40121             
40122             if (in_combo || in_list || is_list) {
40123                 return;
40124             }
40125             this.collapse();
40126         },
40127         
40128         onSelect : function(record, index)
40129         {
40130             if(this.fireEvent('beforeselect', this, record, index) !== false){
40131                 
40132                 this.setFlagClass(record.data.iso2);
40133                 this.setDialCode(record.data.dialCode);
40134                 this.hasFocus = false;
40135                 this.collapse();
40136                 this.fireEvent('select', this, record, index);
40137             }
40138         },
40139         
40140         flagEl : function()
40141         {
40142             var flag = this.el.select('div.flag',true).first();
40143             if(!flag){
40144                 return false;
40145             }
40146             return flag;
40147         },
40148         
40149         dialCodeHolderEl : function()
40150         {
40151             var d = this.el.select('input.dial-code-holder',true).first();
40152             if(!d){
40153                 return false;
40154             }
40155             return d;
40156         },
40157         
40158         setDialCode : function(v)
40159         {
40160             this.dialCodeHolder.dom.value = '+'+v;
40161         },
40162         
40163         setFlagClass : function(n)
40164         {
40165             this.flag.dom.className = 'flag '+n;
40166         },
40167         
40168         getValue : function()
40169         {
40170             var v = this.inputEl().getValue();
40171             if(this.dialCodeHolder) {
40172                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40173             }
40174             return v;
40175         },
40176         
40177         setValue : function(v)
40178         {
40179             var d = this.getDialCode(v);
40180             
40181             //invalid dial code
40182             if(v.length == 0 || !d || d.length == 0) {
40183                 if(this.rendered){
40184                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40185                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40186                 }
40187                 return;
40188             }
40189             
40190             //valid dial code
40191             this.setFlagClass(this.dialCodeMapping[d].iso2);
40192             this.setDialCode(d);
40193             this.inputEl().dom.value = v.replace('+'+d,'');
40194             this.hiddenEl().dom.value = this.getValue();
40195             
40196             this.validate();
40197         },
40198         
40199         getDialCode : function(v)
40200         {
40201             v = v ||  '';
40202             
40203             if (v.length == 0) {
40204                 return this.dialCodeHolder.dom.value;
40205             }
40206             
40207             var dialCode = "";
40208             if (v.charAt(0) != "+") {
40209                 return false;
40210             }
40211             var numericChars = "";
40212             for (var i = 1; i < v.length; i++) {
40213               var c = v.charAt(i);
40214               if (!isNaN(c)) {
40215                 numericChars += c;
40216                 if (this.dialCodeMapping[numericChars]) {
40217                   dialCode = v.substr(1, i);
40218                 }
40219                 if (numericChars.length == 4) {
40220                   break;
40221                 }
40222               }
40223             }
40224             return dialCode;
40225         },
40226         
40227         reset : function()
40228         {
40229             this.setValue(this.defaultDialCode);
40230             this.validate();
40231         },
40232         
40233         hiddenEl : function()
40234         {
40235             return this.el.select('input.hidden-tel-input',true).first();
40236         },
40237         
40238         onKeyUp : function(e){
40239             
40240             var k = e.getKey();
40241             var c = e.getCharCode();
40242             
40243             if(
40244                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40245                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40246             ){
40247                 e.stopEvent();
40248             }
40249             
40250             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40251             //     return;
40252             // }
40253             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40254                 e.stopEvent();
40255             }
40256             
40257             this.setValue(this.getValue());
40258         }
40259         
40260 });
40261 /**
40262  * @class Roo.bootstrap.MoneyField
40263  * @extends Roo.bootstrap.ComboBox
40264  * Bootstrap MoneyField class
40265  * 
40266  * @constructor
40267  * Create a new MoneyField.
40268  * @param {Object} config Configuration options
40269  */
40270
40271 Roo.bootstrap.MoneyField = function(config) {
40272     
40273     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40274     
40275 };
40276
40277 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40278     
40279     /**
40280      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40281      */
40282     allowDecimals : true,
40283     /**
40284      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40285      */
40286     decimalSeparator : ".",
40287     /**
40288      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40289      */
40290     decimalPrecision : 0,
40291     /**
40292      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40293      */
40294     allowNegative : true,
40295     /**
40296      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40297      */
40298     allowZero: true,
40299     /**
40300      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40301      */
40302     minValue : Number.NEGATIVE_INFINITY,
40303     /**
40304      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40305      */
40306     maxValue : Number.MAX_VALUE,
40307     /**
40308      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40309      */
40310     minText : "The minimum value for this field is {0}",
40311     /**
40312      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40313      */
40314     maxText : "The maximum value for this field is {0}",
40315     /**
40316      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40317      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40318      */
40319     nanText : "{0} is not a valid number",
40320     /**
40321      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40322      */
40323     castInt : true,
40324     /**
40325      * @cfg {String} defaults currency of the MoneyField
40326      * value should be in lkey
40327      */
40328     defaultCurrency : false,
40329     /**
40330      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40331      */
40332     thousandsDelimiter : false,
40333     
40334     
40335     inputlg : 9,
40336     inputmd : 9,
40337     inputsm : 9,
40338     inputxs : 6,
40339     
40340     store : false,
40341     
40342     getAutoCreate : function()
40343     {
40344         var align = this.labelAlign || this.parentLabelAlign();
40345         
40346         var id = Roo.id();
40347
40348         var cfg = {
40349             cls: 'form-group',
40350             cn: []
40351         };
40352
40353         var input =  {
40354             tag: 'input',
40355             id : id,
40356             cls : 'form-control roo-money-amount-input',
40357             autocomplete: 'new-password'
40358         };
40359         
40360         var hiddenInput = {
40361             tag: 'input',
40362             type: 'hidden',
40363             id: Roo.id(),
40364             cls: 'hidden-number-input'
40365         };
40366         
40367         if (this.name) {
40368             hiddenInput.name = this.name;
40369         }
40370
40371         if (this.disabled) {
40372             input.disabled = true;
40373         }
40374
40375         var clg = 12 - this.inputlg;
40376         var cmd = 12 - this.inputmd;
40377         var csm = 12 - this.inputsm;
40378         var cxs = 12 - this.inputxs;
40379         
40380         var container = {
40381             tag : 'div',
40382             cls : 'row roo-money-field',
40383             cn : [
40384                 {
40385                     tag : 'div',
40386                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40387                     cn : [
40388                         {
40389                             tag : 'div',
40390                             cls: 'roo-select2-container input-group',
40391                             cn: [
40392                                 {
40393                                     tag : 'input',
40394                                     cls : 'form-control roo-money-currency-input',
40395                                     autocomplete: 'new-password',
40396                                     readOnly : 1,
40397                                     name : this.currencyName
40398                                 },
40399                                 {
40400                                     tag :'span',
40401                                     cls : 'input-group-addon',
40402                                     cn : [
40403                                         {
40404                                             tag: 'span',
40405                                             cls: 'caret'
40406                                         }
40407                                     ]
40408                                 }
40409                             ]
40410                         }
40411                     ]
40412                 },
40413                 {
40414                     tag : 'div',
40415                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40416                     cn : [
40417                         {
40418                             tag: 'div',
40419                             cls: this.hasFeedback ? 'has-feedback' : '',
40420                             cn: [
40421                                 input
40422                             ]
40423                         }
40424                     ]
40425                 }
40426             ]
40427             
40428         };
40429         
40430         if (this.fieldLabel.length) {
40431             var indicator = {
40432                 tag: 'i',
40433                 tooltip: 'This field is required'
40434             };
40435
40436             var label = {
40437                 tag: 'label',
40438                 'for':  id,
40439                 cls: 'control-label',
40440                 cn: []
40441             };
40442
40443             var label_text = {
40444                 tag: 'span',
40445                 html: this.fieldLabel
40446             };
40447
40448             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40449             label.cn = [
40450                 indicator,
40451                 label_text
40452             ];
40453
40454             if(this.indicatorpos == 'right') {
40455                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40456                 label.cn = [
40457                     label_text,
40458                     indicator
40459                 ];
40460             }
40461
40462             if(align == 'left') {
40463                 container = {
40464                     tag: 'div',
40465                     cn: [
40466                         container
40467                     ]
40468                 };
40469
40470                 if(this.labelWidth > 12){
40471                     label.style = "width: " + this.labelWidth + 'px';
40472                 }
40473                 if(this.labelWidth < 13 && this.labelmd == 0){
40474                     this.labelmd = this.labelWidth;
40475                 }
40476                 if(this.labellg > 0){
40477                     label.cls += ' col-lg-' + this.labellg;
40478                     input.cls += ' col-lg-' + (12 - this.labellg);
40479                 }
40480                 if(this.labelmd > 0){
40481                     label.cls += ' col-md-' + this.labelmd;
40482                     container.cls += ' col-md-' + (12 - this.labelmd);
40483                 }
40484                 if(this.labelsm > 0){
40485                     label.cls += ' col-sm-' + this.labelsm;
40486                     container.cls += ' col-sm-' + (12 - this.labelsm);
40487                 }
40488                 if(this.labelxs > 0){
40489                     label.cls += ' col-xs-' + this.labelxs;
40490                     container.cls += ' col-xs-' + (12 - this.labelxs);
40491                 }
40492             }
40493         }
40494
40495         cfg.cn = [
40496             label,
40497             container,
40498             hiddenInput
40499         ];
40500         
40501         var settings = this;
40502
40503         ['xs','sm','md','lg'].map(function(size){
40504             if (settings[size]) {
40505                 cfg.cls += ' col-' + size + '-' + settings[size];
40506             }
40507         });
40508         
40509         return cfg;
40510     },
40511     
40512     initEvents : function()
40513     {
40514         this.indicator = this.indicatorEl();
40515         
40516         this.initCurrencyEvent();
40517         
40518         this.initNumberEvent();
40519     },
40520     
40521     initCurrencyEvent : function()
40522     {
40523         if (!this.store) {
40524             throw "can not find store for combo";
40525         }
40526         
40527         this.store = Roo.factory(this.store, Roo.data);
40528         this.store.parent = this;
40529         
40530         this.createList();
40531         
40532         this.triggerEl = this.el.select('.input-group-addon', true).first();
40533         
40534         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40535         
40536         var _this = this;
40537         
40538         (function(){
40539             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40540             _this.list.setWidth(lw);
40541         }).defer(100);
40542         
40543         this.list.on('mouseover', this.onViewOver, this);
40544         this.list.on('mousemove', this.onViewMove, this);
40545         this.list.on('scroll', this.onViewScroll, this);
40546         
40547         if(!this.tpl){
40548             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40549         }
40550         
40551         this.view = new Roo.View(this.list, this.tpl, {
40552             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40553         });
40554         
40555         this.view.on('click', this.onViewClick, this);
40556         
40557         this.store.on('beforeload', this.onBeforeLoad, this);
40558         this.store.on('load', this.onLoad, this);
40559         this.store.on('loadexception', this.onLoadException, this);
40560         
40561         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40562             "up" : function(e){
40563                 this.inKeyMode = true;
40564                 this.selectPrev();
40565             },
40566
40567             "down" : function(e){
40568                 if(!this.isExpanded()){
40569                     this.onTriggerClick();
40570                 }else{
40571                     this.inKeyMode = true;
40572                     this.selectNext();
40573                 }
40574             },
40575
40576             "enter" : function(e){
40577                 this.collapse();
40578                 
40579                 if(this.fireEvent("specialkey", this, e)){
40580                     this.onViewClick(false);
40581                 }
40582                 
40583                 return true;
40584             },
40585
40586             "esc" : function(e){
40587                 this.collapse();
40588             },
40589
40590             "tab" : function(e){
40591                 this.collapse();
40592                 
40593                 if(this.fireEvent("specialkey", this, e)){
40594                     this.onViewClick(false);
40595                 }
40596                 
40597                 return true;
40598             },
40599
40600             scope : this,
40601
40602             doRelay : function(foo, bar, hname){
40603                 if(hname == 'down' || this.scope.isExpanded()){
40604                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40605                 }
40606                 return true;
40607             },
40608
40609             forceKeyDown: true
40610         });
40611         
40612         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40613         
40614     },
40615     
40616     initNumberEvent : function(e)
40617     {
40618         this.inputEl().on("keydown" , this.fireKey,  this);
40619         this.inputEl().on("focus", this.onFocus,  this);
40620         this.inputEl().on("blur", this.onBlur,  this);
40621         
40622         this.inputEl().relayEvent('keyup', this);
40623         
40624         if(this.indicator){
40625             this.indicator.addClass('invisible');
40626         }
40627  
40628         this.originalValue = this.getValue();
40629         
40630         if(this.validationEvent == 'keyup'){
40631             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40632             this.inputEl().on('keyup', this.filterValidation, this);
40633         }
40634         else if(this.validationEvent !== false){
40635             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40636         }
40637         
40638         if(this.selectOnFocus){
40639             this.on("focus", this.preFocus, this);
40640             
40641         }
40642         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40643             this.inputEl().on("keypress", this.filterKeys, this);
40644         } else {
40645             this.inputEl().relayEvent('keypress', this);
40646         }
40647         
40648         var allowed = "0123456789";
40649         
40650         if(this.allowDecimals){
40651             allowed += this.decimalSeparator;
40652         }
40653         
40654         if(this.allowNegative){
40655             allowed += "-";
40656         }
40657         
40658         if(this.thousandsDelimiter) {
40659             allowed += ",";
40660         }
40661         
40662         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40663         
40664         var keyPress = function(e){
40665             
40666             var k = e.getKey();
40667             
40668             var c = e.getCharCode();
40669             
40670             if(
40671                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40672                     allowed.indexOf(String.fromCharCode(c)) === -1
40673             ){
40674                 e.stopEvent();
40675                 return;
40676             }
40677             
40678             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40679                 return;
40680             }
40681             
40682             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40683                 e.stopEvent();
40684             }
40685         };
40686         
40687         this.inputEl().on("keypress", keyPress, this);
40688         
40689     },
40690     
40691     onTriggerClick : function(e)
40692     {   
40693         if(this.disabled){
40694             return;
40695         }
40696         
40697         this.page = 0;
40698         this.loadNext = false;
40699         
40700         if(this.isExpanded()){
40701             this.collapse();
40702             return;
40703         }
40704         
40705         this.hasFocus = true;
40706         
40707         if(this.triggerAction == 'all') {
40708             this.doQuery(this.allQuery, true);
40709             return;
40710         }
40711         
40712         this.doQuery(this.getRawValue());
40713     },
40714     
40715     getCurrency : function()
40716     {   
40717         var v = this.currencyEl().getValue();
40718         
40719         return v;
40720     },
40721     
40722     restrictHeight : function()
40723     {
40724         this.list.alignTo(this.currencyEl(), this.listAlign);
40725         this.list.alignTo(this.currencyEl(), this.listAlign);
40726     },
40727     
40728     onViewClick : function(view, doFocus, el, e)
40729     {
40730         var index = this.view.getSelectedIndexes()[0];
40731         
40732         var r = this.store.getAt(index);
40733         
40734         if(r){
40735             this.onSelect(r, index);
40736         }
40737     },
40738     
40739     onSelect : function(record, index){
40740         
40741         if(this.fireEvent('beforeselect', this, record, index) !== false){
40742         
40743             this.setFromCurrencyData(index > -1 ? record.data : false);
40744             
40745             this.collapse();
40746             
40747             this.fireEvent('select', this, record, index);
40748         }
40749     },
40750     
40751     setFromCurrencyData : function(o)
40752     {
40753         var currency = '';
40754         
40755         this.lastCurrency = o;
40756         
40757         if (this.currencyField) {
40758             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40759         } else {
40760             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40761         }
40762         
40763         this.lastSelectionText = currency;
40764         
40765         //setting default currency
40766         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40767             this.setCurrency(this.defaultCurrency);
40768             return;
40769         }
40770         
40771         this.setCurrency(currency);
40772     },
40773     
40774     setFromData : function(o)
40775     {
40776         var c = {};
40777         
40778         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40779         
40780         this.setFromCurrencyData(c);
40781         
40782         var value = '';
40783         
40784         if (this.name) {
40785             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40786         } else {
40787             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40788         }
40789         
40790         this.setValue(value);
40791         
40792     },
40793     
40794     setCurrency : function(v)
40795     {   
40796         this.currencyValue = v;
40797         
40798         if(this.rendered){
40799             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40800             this.validate();
40801         }
40802     },
40803     
40804     setValue : function(v)
40805     {
40806         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40807         
40808         this.value = v;
40809         
40810         if(this.rendered){
40811             
40812             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40813             
40814             this.inputEl().dom.value = (v == '') ? '' :
40815                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40816             
40817             if(!this.allowZero && v === '0') {
40818                 this.hiddenEl().dom.value = '';
40819                 this.inputEl().dom.value = '';
40820             }
40821             
40822             this.validate();
40823         }
40824     },
40825     
40826     getRawValue : function()
40827     {
40828         var v = this.inputEl().getValue();
40829         
40830         return v;
40831     },
40832     
40833     getValue : function()
40834     {
40835         return this.fixPrecision(this.parseValue(this.getRawValue()));
40836     },
40837     
40838     parseValue : function(value)
40839     {
40840         if(this.thousandsDelimiter) {
40841             value += "";
40842             r = new RegExp(",", "g");
40843             value = value.replace(r, "");
40844         }
40845         
40846         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40847         return isNaN(value) ? '' : value;
40848         
40849     },
40850     
40851     fixPrecision : function(value)
40852     {
40853         if(this.thousandsDelimiter) {
40854             value += "";
40855             r = new RegExp(",", "g");
40856             value = value.replace(r, "");
40857         }
40858         
40859         var nan = isNaN(value);
40860         
40861         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40862             return nan ? '' : value;
40863         }
40864         return parseFloat(value).toFixed(this.decimalPrecision);
40865     },
40866     
40867     decimalPrecisionFcn : function(v)
40868     {
40869         return Math.floor(v);
40870     },
40871     
40872     validateValue : function(value)
40873     {
40874         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40875             return false;
40876         }
40877         
40878         var num = this.parseValue(value);
40879         
40880         if(isNaN(num)){
40881             this.markInvalid(String.format(this.nanText, value));
40882             return false;
40883         }
40884         
40885         if(num < this.minValue){
40886             this.markInvalid(String.format(this.minText, this.minValue));
40887             return false;
40888         }
40889         
40890         if(num > this.maxValue){
40891             this.markInvalid(String.format(this.maxText, this.maxValue));
40892             return false;
40893         }
40894         
40895         return true;
40896     },
40897     
40898     validate : function()
40899     {
40900         if(this.disabled || this.allowBlank){
40901             this.markValid();
40902             return true;
40903         }
40904         
40905         var currency = this.getCurrency();
40906         
40907         if(this.validateValue(this.getRawValue()) && currency.length){
40908             this.markValid();
40909             return true;
40910         }
40911         
40912         this.markInvalid();
40913         return false;
40914     },
40915     
40916     getName: function()
40917     {
40918         return this.name;
40919     },
40920     
40921     beforeBlur : function()
40922     {
40923         if(!this.castInt){
40924             return;
40925         }
40926         
40927         var v = this.parseValue(this.getRawValue());
40928         
40929         if(v || v == 0){
40930             this.setValue(v);
40931         }
40932     },
40933     
40934     onBlur : function()
40935     {
40936         this.beforeBlur();
40937         
40938         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40939             //this.el.removeClass(this.focusClass);
40940         }
40941         
40942         this.hasFocus = false;
40943         
40944         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40945             this.validate();
40946         }
40947         
40948         var v = this.getValue();
40949         
40950         if(String(v) !== String(this.startValue)){
40951             this.fireEvent('change', this, v, this.startValue);
40952         }
40953         
40954         this.fireEvent("blur", this);
40955     },
40956     
40957     inputEl : function()
40958     {
40959         return this.el.select('.roo-money-amount-input', true).first();
40960     },
40961     
40962     currencyEl : function()
40963     {
40964         return this.el.select('.roo-money-currency-input', true).first();
40965     },
40966     
40967     hiddenEl : function()
40968     {
40969         return this.el.select('input.hidden-number-input',true).first();
40970     }
40971     
40972 });